@aifabrix/builder 2.42.1 → 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 (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  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 +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @fileoverview Debug / audit TTY appendix for DatasourceTestRun (plan §3.7).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { SEP, appendReferenceLayoutLines } = require('./datasource-test-run-display');
8
+ const { buildDebugEnvelopeSlice } = require('./datasource-test-run-debug-slice');
9
+
10
+ const FULL_MAX_BYTES_PER_STRING = 8192;
11
+ const RAW_MAX_STRING_TTY = 65536;
12
+ const RAW_MAX_STRING_PIPE = 1024 * 1024;
13
+ const RAW_MAX_LINES = 200;
14
+
15
+ /**
16
+ * @param {*} debugOpt - Commander value: undefined, false, true, or level string
17
+ * @returns {null|'summary'|'full'|'raw'}
18
+ */
19
+ function resolveDebugDisplayMode(debugOpt) {
20
+ if (debugOpt === undefined || debugOpt === false) return null;
21
+ if (debugOpt === true) return 'summary';
22
+ const s = String(debugOpt).trim().toLowerCase();
23
+ if (s === 'summary' || s === 'full' || s === 'raw') return s;
24
+ return 'summary';
25
+ }
26
+
27
+ /**
28
+ * @param {string} str
29
+ * @param {number} maxBytes
30
+ * @returns {string}
31
+ */
32
+ function truncateUtf8String(str, maxBytes) {
33
+ if (str === null || str === undefined) return '';
34
+ const s = String(str);
35
+ if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;
36
+ let end = s.length;
37
+ while (end > 0 && Buffer.byteLength(s.slice(0, end), 'utf8') > maxBytes) {
38
+ end -= 1;
39
+ }
40
+ const prefix = s.slice(0, end);
41
+ const omitted = Buffer.byteLength(s, 'utf8') - Buffer.byteLength(prefix, 'utf8');
42
+ return `${prefix} … [truncated, ${omitted} bytes]`;
43
+ }
44
+
45
+ /**
46
+ * @param {*} value
47
+ * @param {number} maxBytesPerString
48
+ * @param {number} depth
49
+ * @returns {*}
50
+ */
51
+ function deepTruncateStrings(value, maxBytesPerString, depth) {
52
+ if (depth <= 0) return '[…]';
53
+ if (value === null || typeof value === 'boolean' || typeof value === 'number') return value;
54
+ if (typeof value === 'string') return truncateUtf8String(value, maxBytesPerString);
55
+ if (Array.isArray(value)) return value.map(v => deepTruncateStrings(v, maxBytesPerString, depth - 1));
56
+ if (typeof value === 'object') {
57
+ const out = {};
58
+ for (const [k, v] of Object.entries(value)) {
59
+ out[k] = deepTruncateStrings(v, maxBytesPerString, depth - 1);
60
+ }
61
+ return out;
62
+ }
63
+ return value;
64
+ }
65
+
66
+ /**
67
+ * @param {string} text
68
+ * @returns {string}
69
+ */
70
+ function redactDebugText(text) {
71
+ return String(text)
72
+ .replace(/Bearer\s+\S+/gi, 'Bearer [REDACTED]')
73
+ .replace(/("Authorization"\s*:\s*")[^"]*(")/gi, '$1[REDACTED]$2')
74
+ .replace(/("authorization"\s*:\s*")[^"]*(")/g, '$1[REDACTED]$2');
75
+ }
76
+
77
+ function pushSummaryRefLines(lines, envelope) {
78
+ const before = lines.length;
79
+ appendReferenceLayoutLines(lines, envelope, { maxRefChars: 600 });
80
+ if (lines.length === before) {
81
+ lines.push('(No audit or debug references on this report.)');
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Rich JSON appendix for full/raw debug (validation, integration, certificate, capabilities).
87
+ * @param {Object} envelope
88
+ * @param {number} maxStr
89
+ * @returns {string}
90
+ */
91
+ function stringifyDebugSlice(envelope, maxStr) {
92
+ const slice = buildDebugEnvelopeSlice(envelope);
93
+ return JSON.stringify(deepTruncateStrings(slice, maxStr, 16), null, 2);
94
+ }
95
+
96
+ function applyRawLineCap(text) {
97
+ let t = redactDebugText(text);
98
+ const ls = t.split('\n');
99
+ if (ls.length > RAW_MAX_LINES) {
100
+ const omitted = ls.length - RAW_MAX_LINES;
101
+ t = `${ls.slice(0, RAW_MAX_LINES).join('\n')}\n… [${omitted} lines omitted; use audit ref]`;
102
+ }
103
+ return t;
104
+ }
105
+
106
+ /**
107
+ * Human appendix after main TTY/summary (not used for --json).
108
+ * @param {Object|null} envelope
109
+ * @param {'summary'|'full'|'raw'} mode
110
+ * @param {boolean} isTTY
111
+ * @returns {string}
112
+ */
113
+ function formatDatasourceTestRunDebugBlock(envelope, mode, isTTY) {
114
+ if (!envelope || typeof envelope !== 'object') return '';
115
+
116
+ const lines = ['', SEP, `Debug (${mode})`];
117
+
118
+ if (mode === 'summary') {
119
+ pushSummaryRefLines(lines, envelope);
120
+ return lines.join('\n');
121
+ }
122
+
123
+ const maxStr = mode === 'full' ? FULL_MAX_BYTES_PER_STRING : isTTY ? RAW_MAX_STRING_TTY : RAW_MAX_STRING_PIPE;
124
+ const text = mode === 'raw' ? applyRawLineCap(stringifyDebugSlice(envelope, maxStr)) : stringifyDebugSlice(envelope, maxStr);
125
+ lines.push(text);
126
+ return lines.join('\n');
127
+ }
128
+
129
+ module.exports = {
130
+ resolveDebugDisplayMode,
131
+ truncateUtf8String,
132
+ formatDatasourceTestRunDebugBlock,
133
+ FULL_MAX_BYTES_PER_STRING,
134
+ RAW_MAX_STRING_TTY,
135
+ RAW_MAX_STRING_PIPE,
136
+ RAW_MAX_LINES
137
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @fileoverview Compact slice of DatasourceTestRun for debug JSON output (full/raw modes).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ /**
8
+ * @param {Object|undefined|null} v
9
+ * @returns {Object|undefined}
10
+ */
11
+ function sliceValidationLayer(v) {
12
+ if (!v || typeof v !== 'object') return undefined;
13
+ return {
14
+ status: v.status,
15
+ summary: v.summary,
16
+ dataReadiness: v.dataReadiness,
17
+ metricsOutput: v.metricsOutput,
18
+ issues: Array.isArray(v.issues) ? v.issues.slice(0, 50) : undefined
19
+ };
20
+ }
21
+
22
+ /**
23
+ * @param {Object|undefined|null} integ
24
+ * @returns {Object|undefined}
25
+ */
26
+ function sliceIntegration(integ) {
27
+ if (!integ || typeof integ !== 'object') return undefined;
28
+ return {
29
+ status: integ.status,
30
+ summary: integ.summary,
31
+ stepResults: integ.stepResults
32
+ };
33
+ }
34
+
35
+ /**
36
+ * @param {Object|undefined|null} cert
37
+ * @returns {Object|undefined}
38
+ */
39
+ function sliceCertificate(cert) {
40
+ if (!cert || typeof cert !== 'object') return undefined;
41
+ return {
42
+ status: cert.status,
43
+ level: cert.level,
44
+ summary: cert.summary,
45
+ blockers: Array.isArray(cert.blockers) ? cert.blockers.slice(0, 30) : undefined
46
+ };
47
+ }
48
+
49
+ /**
50
+ * @param {Object|undefined|null} e2e
51
+ * @returns {Object|undefined}
52
+ */
53
+ function sliceCapabilityE2e(e2e) {
54
+ if (!e2e || typeof e2e !== 'object') return undefined;
55
+ return {
56
+ status: e2e.status,
57
+ strategy: e2e.strategy,
58
+ steps: e2e.steps
59
+ };
60
+ }
61
+
62
+ /**
63
+ * @param {Array|undefined|null} capabilities
64
+ * @returns {Array|undefined}
65
+ */
66
+ function sliceCapabilities(capabilities) {
67
+ if (!Array.isArray(capabilities) || capabilities.length === 0) return undefined;
68
+ return capabilities.map(c => ({
69
+ key: c.key,
70
+ status: c.status,
71
+ permission: c.permission,
72
+ e2e: sliceCapabilityE2e(c.e2e)
73
+ }));
74
+ }
75
+
76
+ /**
77
+ * @param {Object} envelope
78
+ * @returns {Object}
79
+ */
80
+ function buildDebugEnvelopeSlice(envelope) {
81
+ return {
82
+ audit: envelope.audit,
83
+ debug: envelope.debug,
84
+ developer: envelope.developer,
85
+ validation: sliceValidationLayer(envelope.validation),
86
+ integration: sliceIntegration(envelope.integration),
87
+ certificate: sliceCertificate(envelope.certificate),
88
+ capabilitySummary: envelope.capabilitySummary,
89
+ capabilities: sliceCapabilities(envelope.capabilities)
90
+ };
91
+ }
92
+
93
+ module.exports = { buildDebugEnvelopeSlice };
@@ -0,0 +1,442 @@
1
+ /**
2
+ * @fileoverview Minimal human TTY + --summary for DatasourceTestRun (plan §3.2 / §16.9 subset).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const { sectionTitle, headerKeyValue, colorAggregateGlyph, successGlyph, failureGlyph } = require('./cli-test-layout-chalk');
9
+
10
+ const SEP = '────────────────────────────────';
11
+
12
+ /** @type {number} */
13
+ const DEFAULT_MAX_REF_CHARS = 200;
14
+
15
+ /**
16
+ * @param {'ok'|'warn'|'fail'|'skipped'} status
17
+ * @returns {string}
18
+ */
19
+ function statusGlyph(status) {
20
+ if (status === 'ok') return '✔';
21
+ if (status === 'warn') return '⚠';
22
+ if (status === 'fail') return '✖';
23
+ if (status === 'skipped') return '⏭';
24
+ return '?';
25
+ }
26
+
27
+ /**
28
+ * Map API status string to aggregate bucket for colors.
29
+ * @param {*} status
30
+ * @returns {'ok'|'warn'|'fail'|'skipped'|null} null = unknown / other
31
+ */
32
+ function envelopeStatusAggregate(status) {
33
+ const s = String(status || '').toLowerCase();
34
+ if (s === 'warn') return 'warn';
35
+ if (s === 'fail') return 'fail';
36
+ if (s === 'skipped' || s === 'skip') return 'skipped';
37
+ if (s === 'ok') return 'ok';
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * @param {*} status
43
+ * @param {string} rawWord - as returned by API (e.g. ok, warn)
44
+ * @returns {string}
45
+ */
46
+ function colorStatusWord(status, rawWord) {
47
+ const agg = envelopeStatusAggregate(status);
48
+ const w = String(rawWord);
49
+ if (!agg) return chalk.gray(w);
50
+ if (agg === 'ok') return chalk.green(w);
51
+ if (agg === 'warn') return chalk.yellow(w);
52
+ if (agg === 'fail') return chalk.red(w);
53
+ return chalk.gray(w);
54
+ }
55
+
56
+ /**
57
+ * Colored glyph for status (neutral when unknown).
58
+ * @param {*} status
59
+ * @returns {string}
60
+ */
61
+ function colorStatusGlyph(status) {
62
+ const agg = envelopeStatusAggregate(status);
63
+ const glyph = statusGlyph(status);
64
+ return agg ? colorAggregateGlyph(agg, glyph) : chalk.gray(glyph);
65
+ }
66
+
67
+ /**
68
+ * `Status: ✔ ok` with layout-aligned colors (preserves raw status text).
69
+ * @param {Object} envelope
70
+ * @returns {string}
71
+ */
72
+ function formatEnvelopeStatusLine(envelope) {
73
+ const raw = envelope.status !== undefined && envelope.status !== null ? String(envelope.status) : 'unknown';
74
+ const g = colorStatusGlyph(envelope.status);
75
+ return `${chalk.gray('Status:')} ${g} ${colorStatusWord(envelope.status, raw)}`;
76
+ }
77
+
78
+ /**
79
+ * @param {boolean} ok
80
+ * @param {string} body - step name + optional tail
81
+ * @returns {string}
82
+ */
83
+ function colorStepLine(ok, body) {
84
+ const sym = ok ? successGlyph() : failureGlyph();
85
+ return ` ${sym} ${chalk.white(body)}`;
86
+ }
87
+
88
+ /**
89
+ * Normalize CLI --capability value for matching envelope.capabilities[].key
90
+ * @param {*} focusCapabilityKey
91
+ * @returns {string} trimmed or ''
92
+ */
93
+ function normalizedFocusCapabilityKey(focusCapabilityKey) {
94
+ if (focusCapabilityKey === undefined || focusCapabilityKey === null) return '';
95
+ const s = String(focusCapabilityKey).trim();
96
+ return s;
97
+ }
98
+
99
+ /**
100
+ * @param {string[]} lines
101
+ * @param {Object|null|undefined} e2e
102
+ */
103
+ function pushCapabilityE2eLines(lines, e2e) {
104
+ if (!e2e || typeof e2e !== 'object') return;
105
+ if (e2e.status) {
106
+ const g = colorStatusGlyph(e2e.status);
107
+ lines.push(`${chalk.gray('E2E:')} ${g} ${colorStatusWord(e2e.status, e2e.status)}`);
108
+ }
109
+ const esteps = Array.isArray(e2e.steps) ? e2e.steps : [];
110
+ for (const st of esteps) {
111
+ const nm = st.name || st.step || 'step';
112
+ const ok = st.success !== false && !st.error;
113
+ const tail = st.error || st.message ? `: ${st.error || st.message}` : '';
114
+ lines.push(colorStepLine(ok, `${nm}${tail}`));
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Multi-line TTY block for a single capability row (plan §2.3 drill-down).
120
+ * @param {Object|null|undefined} envelope
121
+ * @param {string} focusKey - non-empty
122
+ * @returns {string}
123
+ */
124
+ function formatCapabilityFocusSection(envelope, focusKey) {
125
+ if (!focusKey) return '';
126
+ const lines = ['', chalk.gray(SEP), sectionTitle(`Capability scope: ${focusKey}`)];
127
+ const caps =
128
+ envelope && typeof envelope === 'object' && Array.isArray(envelope.capabilities)
129
+ ? envelope.capabilities
130
+ : [];
131
+ const cap = caps.find(c => c && String(c.key) === focusKey);
132
+ if (!cap) {
133
+ lines.push(chalk.yellow(`(No row with key "${focusKey}" in capabilities[])`));
134
+ return lines.join('\n');
135
+ }
136
+ lines.push(formatEnvelopeStatusLine(cap));
137
+ if (cap.permission) {
138
+ lines.push(headerKeyValue('Permission:', String(cap.permission)));
139
+ }
140
+ pushCapabilityE2eLines(lines, cap.e2e);
141
+ return lines.join('\n');
142
+ }
143
+
144
+ /**
145
+ * @param {Object} envelope
146
+ * @returns {string[]}
147
+ */
148
+ function buildTtyMetaLines(envelope) {
149
+ const lines = [];
150
+ lines.push(
151
+ headerKeyValue('Datasource:', `${envelope.datasourceKey} (${envelope.systemKey})`)
152
+ );
153
+ lines.push(headerKeyValue('Run:', String(envelope.runType)));
154
+ lines.push(formatEnvelopeStatusLine(envelope));
155
+ const rid = envelope.runId || envelope.testRunId;
156
+ if (rid) lines.push(`${chalk.gray('Run ID:')} ${chalk.cyan(String(rid))}`);
157
+ if (envelope.reportCompleteness && envelope.reportCompleteness !== 'full') {
158
+ lines.push(
159
+ `${chalk.gray('Report:')} ${chalk.yellow(String(envelope.reportCompleteness))}`
160
+ );
161
+ }
162
+ return lines;
163
+ }
164
+
165
+ /**
166
+ * @param {*} val
167
+ * @returns {string[]}
168
+ */
169
+ function normalizeRefArray(val) {
170
+ if (val === null || val === undefined) return [];
171
+ return Array.isArray(val) ? val : [val];
172
+ }
173
+
174
+ /**
175
+ * @param {string} str
176
+ * @param {number} maxChars
177
+ * @returns {string}
178
+ */
179
+ function truncateRefLine(str, maxChars) {
180
+ const s = String(str);
181
+ if (s.length <= maxChars) return s;
182
+ return `${s.slice(0, Math.max(0, maxChars - 24))}… [+${s.length - maxChars + 24} chars]`;
183
+ }
184
+
185
+ /**
186
+ * @param {string[]} lines
187
+ * @param {string} title
188
+ * @param {*} arr
189
+ * @param {number} maxRefChars
190
+ * @returns {boolean}
191
+ */
192
+ function appendStringRefBlock(lines, title, arr, maxRefChars) {
193
+ const entries = normalizeRefArray(arr).map(String);
194
+ if (!entries.length) return false;
195
+ lines.push(chalk.blue.bold(String(title)));
196
+ entries.forEach((text, i) => {
197
+ lines.push(` ${chalk.gray(`[${i + 1}]`)} ${chalk.white(truncateRefLine(text, maxRefChars))}`);
198
+ });
199
+ return true;
200
+ }
201
+
202
+ /**
203
+ * @param {string[]} lines
204
+ * @param {Object|null|undefined} dbg
205
+ * @param {number} maxRefChars
206
+ * @returns {boolean}
207
+ */
208
+ function appendDebugPayloadRefLines(lines, dbg, maxRefChars) {
209
+ const payloadRefs = dbg && typeof dbg === 'object' ? normalizeRefArray(dbg.payloadRefs) : [];
210
+ if (!payloadRefs.length) return false;
211
+ lines.push(chalk.blue.bold('debug.payloadRefs:'));
212
+ payloadRefs.forEach((pr, i) => {
213
+ const idx = chalk.gray(`[${i + 1}]`);
214
+ if (typeof pr === 'string') {
215
+ lines.push(` ${idx} ${chalk.white(truncateRefLine(pr, maxRefChars))}`);
216
+ return;
217
+ }
218
+ if (pr && typeof pr === 'object') {
219
+ const k = pr.key !== undefined && pr.key !== null ? String(pr.key) : 'payload';
220
+ const r = pr.ref !== undefined && pr.ref !== null ? String(pr.ref) : '';
221
+ const row = r ? `${k}: ${r}` : k;
222
+ lines.push(` ${idx} ${chalk.white(truncateRefLine(row, maxRefChars))}`);
223
+ return;
224
+ }
225
+ lines.push(` ${idx} ${chalk.gray('(ref)')}`);
226
+ });
227
+ return true;
228
+ }
229
+
230
+ /**
231
+ * @param {string[]} lines
232
+ * @param {Object} dbg
233
+ * @param {number} maxRefChars
234
+ * @returns {boolean}
235
+ */
236
+ function appendDebugMetaLines(lines, dbg, maxRefChars) {
237
+ let added = false;
238
+ if (dbg.mode) {
239
+ lines.push(
240
+ `${chalk.blue.bold('debug.mode:')} ${chalk.white(String(dbg.mode))}`
241
+ );
242
+ added = true;
243
+ }
244
+ if (dbg.executionSummary) {
245
+ lines.push(
246
+ `${chalk.blue.bold('debug.executionSummary:')} ${chalk.white(
247
+ truncateRefLine(String(dbg.executionSummary), maxRefChars)
248
+ )}`
249
+ );
250
+ added = true;
251
+ }
252
+ return added;
253
+ }
254
+
255
+ /**
256
+ * Append audit + debug reference layout (OpenAPI AuditRefs, DebugTrace.payloadRefs).
257
+ * @param {string[]} lines
258
+ * @param {Object} envelope
259
+ * @param {{ maxRefChars?: number }} [opts]
260
+ * @returns {boolean} True if any reference line was added
261
+ */
262
+ function appendReferenceLayoutLines(lines, envelope, opts = {}) {
263
+ const maxRefChars = opts.maxRefChars ?? DEFAULT_MAX_REF_CHARS;
264
+ const audit = envelope && envelope.audit;
265
+ const dbg = envelope && envelope.debug;
266
+ const parts = [];
267
+
268
+ if (audit && typeof audit === 'object') {
269
+ parts.push(appendStringRefBlock(lines, 'audit.executionIds:', audit.executionIds, maxRefChars));
270
+ parts.push(appendStringRefBlock(lines, 'audit.traceRefs:', audit.traceRefs, maxRefChars));
271
+ parts.push(appendStringRefBlock(lines, 'audit.rbacTraceRefs:', audit.rbacTraceRefs, maxRefChars));
272
+ parts.push(appendStringRefBlock(lines, 'audit.abacTraceRefs:', audit.abacTraceRefs, maxRefChars));
273
+ }
274
+
275
+ parts.push(appendDebugPayloadRefLines(lines, dbg, maxRefChars));
276
+
277
+ if (dbg && typeof dbg === 'object') {
278
+ parts.push(appendStringRefBlock(lines, 'debug.executionIds:', dbg.executionIds, maxRefChars));
279
+ parts.push(appendDebugMetaLines(lines, dbg, maxRefChars));
280
+ }
281
+
282
+ return parts.some(Boolean);
283
+ }
284
+
285
+ /**
286
+ * @param {string[]} lines
287
+ * @param {Object} envelope
288
+ * @param {number} [maxIssues]
289
+ */
290
+ function appendValidationIssueLines(lines, envelope, maxIssues = 5) {
291
+ const issues = envelope && envelope.validation && Array.isArray(envelope.validation.issues) ? envelope.validation.issues : [];
292
+ if (!issues.length) return;
293
+ lines.push(sectionTitle('Validation issues:'));
294
+ const cap = Math.min(maxIssues, issues.length);
295
+ for (let i = 0; i < cap; i += 1) {
296
+ const iss = issues[i];
297
+ const code = iss && iss.code ? chalk.red(`[${iss.code}] `) : '';
298
+ const msg = iss && iss.message ? String(iss.message) : JSON.stringify(iss);
299
+ lines.push(` ${code}${chalk.yellow(msg)}`);
300
+ }
301
+ if (issues.length > cap) {
302
+ lines.push(chalk.gray(` … and ${issues.length - cap} more (see --json or debug full/raw)`));
303
+ }
304
+ }
305
+
306
+ /**
307
+ * @param {string[]} lines
308
+ * @param {Object} envelope
309
+ */
310
+ function formatIntegrationStepLine(st) {
311
+ const nm = (st && (st.name || st.step)) || 'step';
312
+ const ok = st && st.success !== false && !st.error;
313
+ const detail = st && (st.message || st.error) ? `: ${st.message || st.error}` : '';
314
+ return colorStepLine(ok, `${nm}${detail}`);
315
+ }
316
+
317
+ function appendIntegrationStepLines(lines, envelope) {
318
+ const integ = envelope && envelope.integration;
319
+ const steps = integ && Array.isArray(integ.stepResults) ? integ.stepResults : [];
320
+ if (!steps.length) return;
321
+ lines.push(sectionTitle('Integration steps:'));
322
+ for (const st of steps) {
323
+ lines.push(formatIntegrationStepLine(st));
324
+ }
325
+ }
326
+
327
+ function pickExecutiveVerdictLine(envelope) {
328
+ const dev = envelope.developer;
329
+ return (
330
+ (dev && dev.executiveSummary) ||
331
+ (envelope.certificate && envelope.certificate.summary) ||
332
+ (envelope.validation && envelope.validation.summary) ||
333
+ (envelope.integration && envelope.integration.summary) ||
334
+ `Run finished with status ${envelope.status}.`
335
+ );
336
+ }
337
+
338
+ /**
339
+ * Plan §3.9: explicit line when E2E has no capability rows (skipped when --capability drill-down is active).
340
+ * @param {string[]} lines
341
+ * @param {Object} envelope
342
+ * @param {{ focusCapabilityKey?: string }} [options]
343
+ */
344
+ function appendNoCapabilitiesReportedLine(lines, envelope, options = {}) {
345
+ if (envelope.runType !== 'e2e') return;
346
+ if (normalizedFocusCapabilityKey(options.focusCapabilityKey)) return;
347
+ const caps = envelope.capabilities;
348
+ if (Array.isArray(caps) && caps.length > 0) return;
349
+ lines.push(chalk.gray('No capabilities reported.'));
350
+ }
351
+
352
+ /**
353
+ * @param {string[]} parts
354
+ * @param {Object} envelope
355
+ * @param {string} focus - normalized or ''
356
+ */
357
+ function appendCapabilitySummaryPart(parts, envelope, focus) {
358
+ if (focus) {
359
+ const caps = Array.isArray(envelope.capabilities) ? envelope.capabilities : [];
360
+ const row = caps.find(c => c && String(c.key) === focus);
361
+ parts.push(
362
+ row ? `Cap ${focus}: ${statusGlyph(row.status)} ${row.status}` : `Cap ${focus}: (missing)`
363
+ );
364
+ return;
365
+ }
366
+ const capSummary = envelope.capabilitySummary;
367
+ if (capSummary && typeof capSummary === 'object') {
368
+ const passed = capSummary.passedCount;
369
+ const total = capSummary.totalCount;
370
+ if (typeof passed === 'number' && typeof total === 'number') {
371
+ parts.push(`Capabilities: ${passed}/${total}`);
372
+ }
373
+ }
374
+ }
375
+
376
+ /**
377
+ * @param {string[]} parts
378
+ * @param {Object} envelope
379
+ */
380
+ function appendCertificateSummaryPart(parts, envelope) {
381
+ const cert = envelope.certificate;
382
+ if (!cert || typeof cert !== 'object') return;
383
+ const level =
384
+ cert.level !== undefined && cert.level !== null ? String(cert.level) : '';
385
+ const cg = cert.status === 'passed' ? '✔' : cert.status === 'not_passed' ? '✖' : '⚠';
386
+ if (level) parts.push(`Certificate: ${level} ${cg}`);
387
+ }
388
+
389
+ /**
390
+ * One-line summary (plan §16.9 normative field order subset).
391
+ * @param {Object} envelope
392
+ * @param {{ focusCapabilityKey?: string }} [options]
393
+ * @returns {string}
394
+ */
395
+ function formatDatasourceTestRunSummary(envelope, options = {}) {
396
+ if (!envelope || typeof envelope !== 'object') return '';
397
+ const parts = [];
398
+ parts.push(String(envelope.datasourceKey || 'unknown'));
399
+ parts.push(`${statusGlyph(envelope.status)} ${(envelope.status || '').toUpperCase()}`);
400
+ const focus = normalizedFocusCapabilityKey(options.focusCapabilityKey);
401
+ appendCapabilitySummaryPart(parts, envelope, focus);
402
+ appendCertificateSummaryPart(parts, envelope);
403
+ return parts.join(' | ');
404
+ }
405
+
406
+ /**
407
+ * Default TTY block (header + verdict line + short summary + optional completeness).
408
+ * @param {Object} envelope
409
+ * @param {{ focusCapabilityKey?: string }} [options] - When set (e.g. --capability), append single-cap block (plan §2.3).
410
+ * @returns {string}
411
+ */
412
+ function formatDatasourceTestRunTTY(envelope, options = {}) {
413
+ if (!envelope || typeof envelope !== 'object') return '';
414
+ const lines = [...buildTtyMetaLines(envelope)];
415
+ appendNoCapabilitiesReportedLine(lines, envelope, options);
416
+ lines.push('');
417
+ lines.push(sectionTitle('Verdict:'));
418
+ lines.push(chalk.white(pickExecutiveVerdictLine(envelope)));
419
+ lines.push('');
420
+ lines.push(chalk.gray(SEP));
421
+ if (appendReferenceLayoutLines(lines, envelope, { maxRefChars: 160 })) {
422
+ lines.push('');
423
+ }
424
+ appendValidationIssueLines(lines, envelope);
425
+ appendIntegrationStepLines(lines, envelope);
426
+ const focus = normalizedFocusCapabilityKey(options.focusCapabilityKey);
427
+ if (focus) {
428
+ lines.push(formatCapabilityFocusSection(envelope, focus));
429
+ }
430
+ return lines.join('\n');
431
+ }
432
+
433
+ module.exports = {
434
+ formatDatasourceTestRunSummary,
435
+ formatDatasourceTestRunTTY,
436
+ formatCapabilityFocusSection,
437
+ normalizedFocusCapabilityKey,
438
+ statusGlyph,
439
+ SEP,
440
+ appendReferenceLayoutLines,
441
+ normalizeRefArray
442
+ };