@aifabrix/builder 2.45.0 → 2.46.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 (352) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/docs-rules.mdc +3 -1
  4. package/.cursor/rules/project-rules.mdc +1 -0
  5. package/README.md +43 -17
  6. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  7. package/jest.config.default.js +2 -1
  8. package/jest.config.isolated.js +2 -1
  9. package/jest.global-live-fabrix-hooks.js +36 -0
  10. package/jest.isolated-projects.js +130 -0
  11. package/jest.projects.js +69 -121
  12. package/lib/api/agent-metadata-validation.api.js +93 -0
  13. package/lib/api/api-response-errors.js +124 -0
  14. package/lib/api/audit-api-helpers.js +68 -0
  15. package/lib/api/audit.api.js +119 -0
  16. package/lib/api/auth-cache.api.js +44 -0
  17. package/lib/api/certificates.api.js +4 -4
  18. package/lib/api/credential.api.js +2 -2
  19. package/lib/api/dataplane-health.api.js +137 -0
  20. package/lib/api/dataplane-sync.api.js +27 -0
  21. package/lib/api/datasources-core.api.js +21 -18
  22. package/lib/api/datasources-extended.api.js +22 -22
  23. package/lib/api/dimensions.api.js +3 -0
  24. package/lib/api/external-systems.api.js +13 -13
  25. package/lib/api/governance-scenario-pack.api.js +159 -0
  26. package/lib/api/governance-scenarios.api.js +39 -0
  27. package/lib/api/groups.api.js +79 -0
  28. package/lib/api/index.js +88 -5
  29. package/lib/api/lifecycle.api.js +77 -0
  30. package/lib/api/pipeline.api.js +7 -5
  31. package/lib/api/protection.api.js +157 -0
  32. package/lib/api/records-bulk.api.js +43 -0
  33. package/lib/api/records-search-parse.js +49 -0
  34. package/lib/api/records-search.api.js +29 -0
  35. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  36. package/lib/api/types/audit.types.js +72 -0
  37. package/lib/api/types/dataplane-health.types.js +27 -0
  38. package/lib/api/types/dimensions.types.js +2 -0
  39. package/lib/api/types/governance-scenarios.types.js +39 -0
  40. package/lib/api/types/protection.types.js +69 -0
  41. package/lib/api/types/records-bulk.types.js +35 -0
  42. package/lib/api/types/records-search.types.js +28 -0
  43. package/lib/api/user-groups.api.js +50 -0
  44. package/lib/api/users.api.js +95 -0
  45. package/lib/api/validation-run.api.js +3 -3
  46. package/lib/api/validation-runner.js +5 -3
  47. package/lib/api/wizard-platform.api.js +3 -3
  48. package/lib/api/wizard.api.js +21 -21
  49. package/lib/app/deploy-config.js +4 -1
  50. package/lib/app/deploy.js +34 -19
  51. package/lib/app/down.js +20 -1
  52. package/lib/app/push.js +4 -5
  53. package/lib/app/register.js +7 -4
  54. package/lib/app/run-env-compose.js +84 -30
  55. package/lib/build/index.js +5 -0
  56. package/lib/certification/sync-system-certification.js +13 -6
  57. package/lib/cli/index.js +8 -2
  58. package/lib/cli/infra-guided-auth.js +109 -0
  59. package/lib/cli/infra-guided.js +91 -69
  60. package/lib/cli/setup-app.help.js +19 -2
  61. package/lib/cli/setup-app.js +58 -23
  62. package/lib/cli/setup-app.test-commands.js +28 -21
  63. package/lib/cli/setup-auth.js +55 -3
  64. package/lib/cli/setup-certification-commands.js +147 -0
  65. package/lib/cli/setup-certification.help.js +141 -0
  66. package/lib/cli/setup-environment.js +19 -2
  67. package/lib/cli/setup-external-system-help.js +62 -0
  68. package/lib/cli/setup-external-system.js +62 -6
  69. package/lib/cli/setup-infra.js +9 -1
  70. package/lib/cli/setup-platform.js +6 -1
  71. package/lib/cli/setup-secrets.js +33 -1
  72. package/lib/cli/setup-utility-help-after.js +65 -0
  73. package/lib/cli/setup-utility-repair.js +198 -0
  74. package/lib/cli/setup-utility-resolve.js +42 -6
  75. package/lib/cli/setup-utility-validate.js +197 -0
  76. package/lib/cli/setup-utility.js +41 -258
  77. package/lib/commands/auth-status-client-tokens.js +214 -0
  78. package/lib/commands/auth-status-dataplane-version.js +269 -0
  79. package/lib/commands/auth-status-display.js +94 -0
  80. package/lib/commands/auth-status.js +111 -53
  81. package/lib/commands/credential-push.js +10 -5
  82. package/lib/commands/datasource-load-export-cli.js +178 -0
  83. package/lib/commands/datasource-test-trust-cli.js +118 -0
  84. package/lib/commands/datasource-unified-test-cli.js +31 -20
  85. package/lib/commands/datasource-unified-test-cli.options.js +22 -3
  86. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  87. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  88. package/lib/commands/datasource.js +68 -1
  89. package/lib/commands/dev-show-display.js +8 -1
  90. package/lib/commands/dimension-value.js +43 -13
  91. package/lib/commands/dimension.js +38 -7
  92. package/lib/commands/governance-cli-shared.js +94 -0
  93. package/lib/commands/governance-command-action.js +132 -0
  94. package/lib/commands/governance-download-external.js +34 -0
  95. package/lib/commands/governance-generate-external.js +71 -0
  96. package/lib/commands/governance-upload-external.js +36 -0
  97. package/lib/commands/governance-verify-external.js +64 -0
  98. package/lib/commands/governance.js +144 -0
  99. package/lib/commands/identity-apply-sync.js +131 -0
  100. package/lib/commands/identity-group-cli.js +146 -0
  101. package/lib/commands/identity-membership-role-cache.js +175 -0
  102. package/lib/commands/identity-shared.js +94 -0
  103. package/lib/commands/identity-user-cli.js +158 -0
  104. package/lib/commands/identity.js +43 -0
  105. package/lib/commands/lifecycle-command-action.js +123 -0
  106. package/lib/commands/lifecycle-external.js +268 -0
  107. package/lib/commands/login.js +56 -17
  108. package/lib/commands/protection-cli-leaves.js +286 -0
  109. package/lib/commands/protection.js +50 -0
  110. package/lib/commands/repair-auth-apply.js +127 -0
  111. package/lib/commands/repair-changed-files.js +41 -0
  112. package/lib/commands/repair-datasource-keys.js +4 -1
  113. package/lib/commands/repair-datasource-run.js +59 -0
  114. package/lib/commands/repair-datasource-test-payload.js +259 -0
  115. package/lib/commands/repair-datasource.js +1 -51
  116. package/lib/commands/repair-env-template.js +64 -13
  117. package/lib/commands/repair-external-run.js +216 -0
  118. package/lib/commands/repair-openapi-sync.js +23 -0
  119. package/lib/commands/repair-persist.js +4 -0
  120. package/lib/commands/repair-rbac.js +52 -1
  121. package/lib/commands/repair-steps.js +145 -0
  122. package/lib/commands/repair.js +29 -286
  123. package/lib/commands/secrets-get.js +184 -0
  124. package/lib/commands/secrets-remove-all.js +3 -1
  125. package/lib/commands/setup-image-refresh.js +146 -0
  126. package/lib/commands/setup-modes.js +158 -162
  127. package/lib/commands/setup-platform-auth.js +76 -0
  128. package/lib/commands/setup-prompts-admin.js +244 -0
  129. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  130. package/lib/commands/setup-prompts-validators.js +36 -0
  131. package/lib/commands/setup-prompts.js +24 -76
  132. package/lib/commands/setup-run-context.js +45 -0
  133. package/lib/commands/setup-spinners.js +39 -0
  134. package/lib/commands/setup.js +27 -5
  135. package/lib/commands/teardown.js +39 -0
  136. package/lib/commands/test-e2e-external.js +61 -14
  137. package/lib/commands/test-governance-cli-options.js +47 -0
  138. package/lib/commands/test-governance-external.js +67 -0
  139. package/lib/commands/test-trust-cli-options.js +32 -0
  140. package/lib/commands/test-trust-external.js +126 -0
  141. package/lib/commands/up-common.js +25 -13
  142. package/lib/commands/up-dataplane.js +50 -13
  143. package/lib/commands/upload-publish-context.js +75 -0
  144. package/lib/commands/upload-sidecar-followups.js +89 -0
  145. package/lib/commands/upload.js +108 -105
  146. package/lib/commands/verify-governance-combined-external.js +61 -0
  147. package/lib/commands/verify-governance-command-action.js +64 -0
  148. package/lib/commands/verify-governance-db.js +102 -0
  149. package/lib/commands/verify-governance-external.js +77 -0
  150. package/lib/commands/verify-governance-result-builders.js +71 -0
  151. package/lib/commands/verify-operations-command-action.js +68 -0
  152. package/lib/commands/verify-operations-external.js +268 -0
  153. package/lib/commands/verify-operations-persist.js +75 -0
  154. package/lib/commands/verify-operations-step-plan.js +68 -0
  155. package/lib/commands/verify-operations-steps.js +111 -0
  156. package/lib/commands/verify-trust-command-action.js +70 -0
  157. package/lib/commands/verify-trust-external.js +67 -0
  158. package/lib/commands/wizard-config-normalizer.js +43 -23
  159. package/lib/commands/wizard-core.js +5 -0
  160. package/lib/core/admin-password-generator.js +38 -0
  161. package/lib/core/admin-secrets-bundle.js +111 -0
  162. package/lib/core/admin-secrets-env-overlay.js +62 -0
  163. package/lib/core/admin-secrets.js +32 -4
  164. package/lib/core/config-attach-extensions.js +3 -0
  165. package/lib/core/config-device-dataplane.js +185 -0
  166. package/lib/core/config-platform-controller.js +44 -0
  167. package/lib/core/config-setup-installation-profile.js +42 -0
  168. package/lib/core/config-setup-platform-mode.js +46 -0
  169. package/lib/core/config.js +46 -36
  170. package/lib/core/secrets-admin-env.js +6 -2
  171. package/lib/core/secrets-ensure.js +4 -0
  172. package/lib/core/secrets-env-content.js +54 -48
  173. package/lib/core/secrets-env-write.js +10 -1
  174. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  175. package/lib/core/setup-installation-profile.js +43 -0
  176. package/lib/core/setup-platform-mode.js +55 -0
  177. package/lib/datasource/agent-trust-debug-log.js +45 -0
  178. package/lib/datasource/agent-trust-fetch.js +115 -0
  179. package/lib/datasource/agent-trust-map.js +101 -0
  180. package/lib/datasource/agent-trust-mock.js +44 -0
  181. package/lib/datasource/agent-trust-run.js +95 -0
  182. package/lib/datasource/agent-trust-timeout.js +25 -0
  183. package/lib/datasource/audit-evidence-display.js +80 -0
  184. package/lib/datasource/audit-evidence-extract-debug.js +115 -0
  185. package/lib/datasource/audit-evidence-extract.js +180 -0
  186. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  187. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  188. package/lib/datasource/audit-evidence-poll.js +109 -0
  189. package/lib/datasource/audit-evidence-run.js +177 -0
  190. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  191. package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
  192. package/lib/datasource/audit-evidence-verify.js +108 -0
  193. package/lib/datasource/bulk-loader-run-batches.js +100 -0
  194. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  195. package/lib/datasource/bulk-loader-service.js +127 -0
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  197. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  198. package/lib/datasource/datasource-exporter-service.js +144 -0
  199. package/lib/datasource/datasource-identifier.js +148 -0
  200. package/lib/datasource/datasource-load-export-context.js +95 -0
  201. package/lib/datasource/integration-context.js +6 -4
  202. package/lib/datasource/local-data-paths.js +145 -0
  203. package/lib/datasource/log-cleaner.js +241 -0
  204. package/lib/datasource/log-viewer.js +111 -6
  205. package/lib/datasource/record-file-parser.js +85 -0
  206. package/lib/datasource/record-mapper.js +129 -0
  207. package/lib/datasource/resolve-app.js +74 -55
  208. package/lib/datasource/test-e2e.js +29 -2
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  211. package/lib/datasource/unified-validation-run.js +47 -18
  212. package/lib/datasource/validate.js +37 -20
  213. package/lib/deployment/environment-poll-status.js +138 -0
  214. package/lib/deployment/environment.js +42 -34
  215. package/lib/external-system/download.js +21 -20
  216. package/lib/external-system/generator.js +5 -1
  217. package/lib/external-system/integration-auth-context.js +45 -0
  218. package/lib/external-system/test-auth.js +12 -5
  219. package/lib/external-system/test-system-level.js +25 -4
  220. package/lib/external-system/test.js +9 -7
  221. package/lib/generator/external-controller-manifest.js +15 -0
  222. package/lib/governance/governance-cli-errors.js +96 -0
  223. package/lib/governance/governance-download-display.js +38 -0
  224. package/lib/governance/governance-pack-loader.js +70 -0
  225. package/lib/governance/governance-run-display.js +309 -0
  226. package/lib/identity/identity-apply-core.js +108 -0
  227. package/lib/identity/identity-apply-phases.js +244 -0
  228. package/lib/identity/identity-apply-service.js +77 -0
  229. package/lib/identity/identity-csv-parser.js +114 -0
  230. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  231. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  232. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  233. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  234. package/lib/infrastructure/helpers.js +23 -129
  235. package/lib/infrastructure/index.js +5 -6
  236. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  237. package/lib/internal/fs-real-sync.js +29 -1
  238. package/lib/lifecycle/certification-progress.js +231 -0
  239. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  240. package/lib/lifecycle/exit-codes.js +24 -0
  241. package/lib/lifecycle/json-envelope.js +25 -0
  242. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  243. package/lib/lifecycle/logger-quiet.js +29 -0
  244. package/lib/lifecycle/open-api-published.js +59 -0
  245. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  246. package/lib/lifecycle/product-model.js +196 -0
  247. package/lib/lifecycle/report-display.js +471 -0
  248. package/lib/lifecycle/scenario-upload.js +191 -0
  249. package/lib/lifecycle/verify-step-progress.js +87 -0
  250. package/lib/parameters/infra-kv-discovery.js +27 -2
  251. package/lib/protection/auth-context.js +51 -0
  252. package/lib/protection/convert-batch.js +90 -0
  253. package/lib/protection/load.js +41 -0
  254. package/lib/protection/paths.js +158 -0
  255. package/lib/protection/preflight-datasource-ready.js +46 -0
  256. package/lib/protection/protection-create-probe-lines.js +142 -0
  257. package/lib/protection/protection-create-probes.js +95 -0
  258. package/lib/protection/protection-create-scaffold.js +281 -0
  259. package/lib/protection/protection-display-helpers.js +445 -0
  260. package/lib/protection/protection-display.js +215 -0
  261. package/lib/protection/protection-preset-registry.js +97 -0
  262. package/lib/protection/report-exit.js +62 -0
  263. package/lib/protection/resolve.js +202 -0
  264. package/lib/protection/run-commands-validate.js +130 -0
  265. package/lib/protection/run-commands.js +216 -0
  266. package/lib/protection/run-protection-create-helpers.js +134 -0
  267. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  268. package/lib/protection/run-protection-create.js +191 -0
  269. package/lib/protection/scope.js +22 -0
  270. package/lib/protection/sync-after-upload.js +64 -0
  271. package/lib/protection/upload-batch.js +127 -0
  272. package/lib/protection/validate-batch.js +113 -0
  273. package/lib/protection/validate-local.js +65 -0
  274. package/lib/resolvers/dimension-file.js +29 -4
  275. package/lib/schema/application-schema.json +1 -1
  276. package/lib/schema/datasource-test-run.schema.json +89 -6
  277. package/lib/schema/external-datasource.schema.json +2636 -2588
  278. package/lib/schema/external-system.schema.json +1089 -761
  279. package/lib/schema/flag-map-validation-run.json +2 -2
  280. package/lib/schema/infra.parameter.yaml +45 -0
  281. package/lib/schema/protection.schema.json +374 -0
  282. package/lib/schema/type/credential-field-registry.json +642 -0
  283. package/lib/schema/wizard-config.schema.json +1 -1
  284. package/lib/utils/agent-trust-run-display.js +231 -0
  285. package/lib/utils/agent-trust-run-exit.js +59 -0
  286. package/lib/utils/agent-trust-wait-ui.js +94 -0
  287. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  288. package/lib/utils/api.js +55 -31
  289. package/lib/utils/app-relative-path-env.js +112 -0
  290. package/lib/utils/cli-exit-codes.js +30 -0
  291. package/lib/utils/cli-secrets-error-format.js +83 -21
  292. package/lib/utils/cli-sync-options.js +33 -0
  293. package/lib/utils/cli-utils.js +16 -4
  294. package/lib/utils/compose-generate-docker-compose.js +4 -0
  295. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  296. package/lib/utils/config-secrets-path.js +46 -0
  297. package/lib/utils/controller-url.js +88 -1
  298. package/lib/utils/credential-push-ui.js +90 -0
  299. package/lib/utils/credential-secrets-env.js +137 -9
  300. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  301. package/lib/utils/dataplane-cli-version-help.js +74 -0
  302. package/lib/utils/dataplane-resolver.js +129 -2
  303. package/lib/utils/datasource-export-display.js +137 -0
  304. package/lib/utils/datasource-load-display.js +174 -0
  305. package/lib/utils/datasource-load-export-ui.js +120 -0
  306. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  307. package/lib/utils/datasource-test-run-display.js +3 -3
  308. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  309. package/lib/utils/declarative-url-ports.js +3 -3
  310. package/lib/utils/deployment-auth-mode.js +41 -0
  311. package/lib/utils/developer-volume-id.js +33 -0
  312. package/lib/utils/env-copy.js +14 -9
  313. package/lib/utils/env-kv-orphan-lines.js +43 -0
  314. package/lib/utils/error-formatters/network-errors.js +13 -4
  315. package/lib/utils/external-env-template.js +1 -0
  316. package/lib/utils/external-system-readiness-core.js +10 -0
  317. package/lib/utils/external-system-readiness-display.js +80 -23
  318. package/lib/utils/health-check.js +4 -1
  319. package/lib/utils/help-builder.js +35 -26
  320. package/lib/utils/keycloak-realm-ready.js +101 -0
  321. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  322. package/lib/utils/manifest-source-emit.js +24 -0
  323. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  324. package/lib/utils/platform-controller-url.js +156 -0
  325. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  326. package/lib/utils/postgres-wipe.js +9 -2
  327. package/lib/utils/resolve-env-mode.js +47 -0
  328. package/lib/utils/secrets-generator.js +31 -25
  329. package/lib/utils/secrets-missing-error.js +164 -0
  330. package/lib/utils/semver-compare.js +127 -0
  331. package/lib/utils/service-url-label.js +81 -0
  332. package/lib/utils/setup-quiet-output.js +25 -0
  333. package/lib/utils/template-helpers.js +15 -0
  334. package/lib/utils/token-manager-client-credentials.js +201 -0
  335. package/lib/utils/token-manager-messages.js +8 -0
  336. package/lib/utils/token-manager-refresh.js +12 -2
  337. package/lib/utils/token-manager.js +86 -105
  338. package/lib/utils/upload-pipeline-ui.js +98 -0
  339. package/lib/utils/upload-sync-options.js +42 -0
  340. package/lib/utils/url-declarative-public-base.js +21 -3
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  342. package/lib/utils/validation-poll-ui.js +22 -7
  343. package/lib/utils/validation-run-stage.js +45 -0
  344. package/lib/validation/wizard-config-validator.js +58 -19
  345. package/package.json +1 -1
  346. package/templates/applications/dataplane/application.yaml +2 -1
  347. package/templates/applications/dataplane/env.template +36 -1
  348. package/templates/applications/dataplane/rbac.yaml +4 -0
  349. package/templates/applications/miso-controller/env.template +16 -0
  350. package/templates/external-system/README.md.hbs +116 -11
  351. package/templates/python/docker-compose.hbs +19 -18
  352. package/templates/typescript/docker-compose.hbs +18 -17
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Poll helpers for environment deploy status (deployment-by-ID).
3
+ *
4
+ * @fileoverview Parse pipeline/environments deployment poll responses and fail fast on terminal HTTP errors
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ const TERMINAL_POLL_HTTP_STATUSES = new Set([400, 401, 403, 404, 410, 422]);
10
+
11
+ /**
12
+ * @param {Object|null|undefined} response - API client response
13
+ * @returns {boolean}
14
+ */
15
+ function isTerminalPollApiResponse(response) {
16
+ return Boolean(
17
+ response &&
18
+ response.success === false &&
19
+ typeof response.status === 'number' &&
20
+ TERMINAL_POLL_HTTP_STATUSES.has(response.status)
21
+ );
22
+ }
23
+
24
+ /**
25
+ * @param {Object|null|undefined} response - Successful API client response
26
+ * @returns {Object|null} Deployment record or null
27
+ */
28
+ function extractDeploymentRecord(response) {
29
+ if (!response || response.success === false) {
30
+ return null;
31
+ }
32
+ if (response?.data?.data) {
33
+ return response.data.data;
34
+ }
35
+ if (response?.data && typeof response.data === 'object' && response.data.status) {
36
+ return response.data;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * @param {Object|null|undefined} pipelineRes
43
+ * @param {Object|null|undefined} envRes
44
+ * @returns {Error}
45
+ */
46
+ /**
47
+ * @param {string} requestedEnvKey
48
+ * @param {Error} error
49
+ * @returns {Error}
50
+ */
51
+ function enrichPollTerminalError(requestedEnvKey, error) {
52
+ if (!error?.pollTerminal || error.status !== 404) {
53
+ return error;
54
+ }
55
+ const base = error.message || '';
56
+ if (!base.includes('Environment with key')) {
57
+ return error;
58
+ }
59
+ error.message =
60
+ `${base}\n\n` +
61
+ 'The deployment may have been created under a different environment key ' +
62
+ '(for example evaluation-mode fallback to miso). Check controller logs for ' +
63
+ `effectiveEnvironmentKey, register '${requestedEnvKey}' in the controller, or run ` +
64
+ 'aifabrix env deploy miso.';
65
+ return error;
66
+ }
67
+
68
+ function buildPollStatusError(pipelineRes, envRes) {
69
+ const terminal = [pipelineRes, envRes].filter(isTerminalPollApiResponse);
70
+ const source = terminal[0] || pipelineRes || envRes;
71
+ const message = source?.formattedError || source?.error || 'Unable to retrieve deployment status';
72
+ const err = new Error(message);
73
+ err.pollTerminal = true;
74
+ if (typeof source?.status === 'number') {
75
+ err.status = source.status;
76
+ }
77
+ return err;
78
+ }
79
+
80
+ /**
81
+ * Fetches deployment status by ID (pipeline endpoint first, then environments).
82
+ *
83
+ * @async
84
+ * @param {Function} getPipelineDeployment - pipeline.api getPipelineDeployment
85
+ * @param {Function} getDeployment - deployments.api getDeployment
86
+ * @param {string} controllerUrl
87
+ * @param {string} envKey
88
+ * @param {string} deploymentId
89
+ * @param {Object} apiAuthConfig
90
+ * @returns {Promise<Object|null>} Deployment record or null when status is not yet available (retry)
91
+ * @throws {Error} When both endpoints return a terminal HTTP error (e.g. environment not found)
92
+ */
93
+ async function fetchDeploymentStatusById(
94
+ getPipelineDeployment,
95
+ getDeployment,
96
+ controllerUrl,
97
+ envKey,
98
+ deploymentId,
99
+ apiAuthConfig
100
+ ) {
101
+ let pipelineRes;
102
+ let envRes;
103
+
104
+ try {
105
+ pipelineRes = await getPipelineDeployment(controllerUrl, envKey, deploymentId, apiAuthConfig);
106
+ const fromPipeline = extractDeploymentRecord(pipelineRes);
107
+ if (fromPipeline) {
108
+ return fromPipeline;
109
+ }
110
+ } catch (err) {
111
+ pipelineRes = { success: false, error: err?.message || String(err) };
112
+ }
113
+
114
+ try {
115
+ envRes = await getDeployment(controllerUrl, envKey, deploymentId, apiAuthConfig);
116
+ const fromEnv = extractDeploymentRecord(envRes);
117
+ if (fromEnv) {
118
+ return fromEnv;
119
+ }
120
+ } catch (err) {
121
+ envRes = { success: false, error: err?.message || String(err) };
122
+ }
123
+
124
+ if (isTerminalPollApiResponse(pipelineRes) || isTerminalPollApiResponse(envRes)) {
125
+ throw buildPollStatusError(pipelineRes, envRes);
126
+ }
127
+
128
+ return null;
129
+ }
130
+
131
+ module.exports = {
132
+ TERMINAL_POLL_HTTP_STATUSES,
133
+ isTerminalPollApiResponse,
134
+ extractDeploymentRecord,
135
+ enrichPollTerminalError,
136
+ buildPollStatusError,
137
+ fetchDeploymentStatusById
138
+ };
@@ -25,6 +25,7 @@ const {
25
25
  buildEnvironmentConfigFromPreset,
26
26
  loadAndValidateEnvironmentDeployConfig
27
27
  } = require('./environment-config');
28
+ const { fetchDeploymentStatusById, enrichPollTerminalError } = require('./environment-poll-status');
28
29
 
29
30
  /**
30
31
  * Validates environment deployment prerequisites
@@ -161,31 +162,24 @@ async function sendEnvironmentDeployment(controllerUrl, envKey, authConfig, opti
161
162
  }
162
163
 
163
164
  /**
164
- * Fetches deployment status by ID (pipeline endpoint first, then environments)
165
- * Mirrors miso-controller manual test: GET pipeline/deployments/:id then GET environments/deployments/:id
165
+ * Fetches deployment status by ID (pipeline endpoint first, then environments).
166
166
  * @async
167
167
  * @param {string} controllerUrl - Controller URL
168
168
  * @param {string} envKey - Environment key
169
169
  * @param {string} deploymentId - Deployment ID
170
170
  * @param {Object} apiAuthConfig - Auth config { type: 'bearer', token }
171
- * @returns {Promise<Object|null>} Deployment record (status, progress, etc.) or null
171
+ * @returns {Promise<Object|null>} Deployment record or null when still in progress
172
+ * @throws {Error} On terminal poll errors (e.g. environment or deployment not found)
172
173
  */
173
174
  async function getDeploymentStatusById(controllerUrl, envKey, deploymentId, apiAuthConfig) {
174
- try {
175
- const pipelineRes = await getPipelineDeployment(controllerUrl, envKey, deploymentId, apiAuthConfig);
176
- if (pipelineRes?.data?.data) return pipelineRes.data.data;
177
- if (pipelineRes?.data && typeof pipelineRes.data === 'object' && pipelineRes.data.status) return pipelineRes.data;
178
- } catch {
179
- // Fallback to environments endpoint
180
- }
181
- try {
182
- const envRes = await getDeployment(controllerUrl, envKey, deploymentId, apiAuthConfig);
183
- if (envRes?.data?.data) return envRes.data.data;
184
- if (envRes?.data && typeof envRes.data === 'object' && envRes.data.status) return envRes.data;
185
- } catch {
186
- // Ignore
187
- }
188
- return null;
175
+ return fetchDeploymentStatusById(
176
+ getPipelineDeployment,
177
+ getDeployment,
178
+ controllerUrl,
179
+ envKey,
180
+ deploymentId,
181
+ apiAuthConfig
182
+ );
189
183
  }
190
184
 
191
185
  /**
@@ -218,6 +212,33 @@ function processDeploymentStatusResponse(deployment, validatedEnvKey) {
218
212
  return null;
219
213
  }
220
214
 
215
+ /**
216
+ * @param {number} attempt
217
+ * @param {number} maxAttempts
218
+ * @param {Object|null} deployment
219
+ */
220
+ function logPollAttemptProgress(attempt, maxAttempts, deployment) {
221
+ const progress = deployment?.progress ?? 0;
222
+ const statusLabel = deployment?.status ?? 'pending';
223
+ logger.log(chalk.gray(` Attempt ${attempt}/${maxAttempts}... Status: ${statusLabel} (${progress}%)`));
224
+ }
225
+
226
+ /**
227
+ * @param {Error} error
228
+ * @param {string} validatedEnvKey
229
+ * @throws {Error}
230
+ */
231
+ function handlePollIterationError(error, validatedEnvKey) {
232
+ const msg = error?.message || '';
233
+ const isDeploymentFailed =
234
+ msg.includes('Environment deployment failed') ||
235
+ (msg.includes('failed') && !error.pollTerminal);
236
+ if (error.pollTerminal || isDeploymentFailed) {
237
+ throw enrichPollTerminalError(validatedEnvKey, error);
238
+ }
239
+ logger.log(chalk.yellow(` ⚠ Status check failed (will retry): ${msg}`));
240
+ }
241
+
221
242
  /**
222
243
  * Polls environment deployment status
223
244
  * @async
@@ -231,44 +252,31 @@ function processDeploymentStatusResponse(deployment, validatedEnvKey) {
231
252
  async function pollEnvironmentStatus(deploymentId, controllerUrl, envKey, authConfig, options = {}) {
232
253
  const validatedUrl = validateControllerUrl(controllerUrl);
233
254
  const validatedEnvKey = validateEnvironmentKey(envKey);
234
-
235
255
  const pollInterval = options.pollInterval || 5000;
236
256
  const maxAttempts = options.maxAttempts || 60;
257
+ const apiAuthConfig = { type: 'bearer', token: authConfig.token };
237
258
 
238
259
  logger.log(chalk.blue(`⏳ Polling environment status (${pollInterval}ms intervals)...`));
239
260
 
240
- // Use centralized API client
241
- const apiAuthConfig = { type: 'bearer', token: authConfig.token };
242
-
243
261
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
244
262
  try {
245
263
  await new Promise(resolve => setTimeout(resolve, pollInterval));
246
-
247
264
  const deployment = await getDeploymentStatusById(
248
265
  validatedUrl,
249
266
  validatedEnvKey,
250
267
  deploymentId,
251
268
  apiAuthConfig
252
269
  );
253
- const progress = deployment?.progress ?? 0;
254
- const statusLabel = deployment?.status ?? 'pending';
255
- if (attempt <= maxAttempts) {
256
- logger.log(chalk.gray(` Attempt ${attempt}/${maxAttempts}... Status: ${statusLabel} (${progress}%)`));
257
- }
270
+ logPollAttemptProgress(attempt, maxAttempts, deployment);
258
271
  const statusResult = processDeploymentStatusResponse(deployment, validatedEnvKey);
259
272
  if (statusResult) {
260
273
  return statusResult;
261
274
  }
262
275
  } catch (error) {
263
- // If it's a terminal error (not a timeout), throw it
264
- if (error.message && error.message.includes('failed')) {
265
- throw error;
266
- }
267
- // Otherwise, continue polling
276
+ handlePollIterationError(error, validatedEnvKey);
268
277
  }
269
278
  }
270
279
 
271
- // Timeout
272
280
  throw new Error(`Environment deployment timeout after ${maxAttempts} attempts. Check controller logs for status.`);
273
281
  }
274
282
 
@@ -437,30 +437,24 @@ function logDownloadCommandHeader(systemKey) {
437
437
  }
438
438
 
439
439
  /**
440
- * Downloads external system from dataplane to local development structure
440
+ * Downloads external system from dataplane to local development structure.
441
441
  * @async
442
- * @function downloadExternalSystem
443
- * @param {string} systemKey - System key or ID
444
- * @param {Object} options - Download options
445
- * @param {string} [options.format] - Output format: 'yaml' (default) or 'json' (runs convert after split)
446
- * @param {string} [options.environment] - Environment (dev, tst, pro)
447
- * @param {string} [options.controller] - Controller URL
448
- * @param {boolean} [options.dryRun] - Show what would be downloaded without actually downloading
449
- * @param {boolean} [options.force] - Overwrite existing README.md without prompting
450
- * @returns {Promise<void>} Resolves when download completes
451
- * @throws {Error} If download fails
442
+ * @param {string} systemKey
443
+ * @param {Object} [options]
444
+ * @returns {Promise<void>}
452
445
  */
453
446
  async function downloadExternalSystem(systemKey, options = {}) {
454
447
  validateSystemKeyFormat(systemKey);
455
-
456
448
  const format = (options.format || 'yaml').toLowerCase();
457
449
 
458
450
  try {
459
451
  logDownloadCommandHeader(systemKey);
460
-
461
452
  const config = await getConfig();
462
- const { authConfig, dataplaneUrl } = await setupAuthenticationAndDataplane(systemKey, options, config);
463
-
453
+ const { authConfig, dataplaneUrl } = await setupAuthenticationAndDataplane(
454
+ systemKey,
455
+ options,
456
+ config
457
+ );
464
458
  if (options.dryRun) {
465
459
  handleDryRun(systemKey, dataplaneUrl);
466
460
  return;
@@ -468,15 +462,22 @@ async function downloadExternalSystem(systemKey, options = {}) {
468
462
 
469
463
  const manifest = await downloadFullManifest(dataplaneUrl, systemKey, authConfig);
470
464
  validateAndLogDownloadedData(manifest.application, manifest.dataSources);
471
-
472
465
  const finalPath = getIntegrationPath(systemKey);
473
- const splitOptions = await resolveDownloadSplitOptions(finalPath, options);
474
- await processDownloadedSystem(systemKey, manifest, splitOptions);
475
-
466
+ await processDownloadedSystem(
467
+ systemKey,
468
+ manifest,
469
+ await resolveDownloadSplitOptions(finalPath, options)
470
+ );
471
+ const { downloadGovernanceScenarioPacksFromManifest } = require('../lifecycle/scenario-upload');
472
+ await downloadGovernanceScenarioPacksFromManifest(
473
+ systemKey,
474
+ manifest,
475
+ dataplaneUrl,
476
+ authConfig
477
+ );
476
478
  if (format === 'json') {
477
479
  await runConvertToJsonIfRequested(systemKey);
478
480
  }
479
-
480
481
  displayDownloadSuccess(systemKey, finalPath, manifest.dataSources.length);
481
482
  } catch (error) {
482
483
  throw new Error(`Failed to download external system: ${error.message}`);
@@ -26,7 +26,7 @@ handlebars.registerHelper('json', (obj) => JSON.stringify(obj));
26
26
  * Build authentication object per schema authenticationVariablesByMethod.
27
27
  * Security values use canonical kv://<systemKey>/<segment> paths (segment from getKvPathSegmentForSecurityKey).
28
28
  * @param {string} systemKey - External system key
29
- * @param {string} authType - Auth method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none)
29
+ * @param {string} authType - Auth method (oauth2, aad, apikey, bearerToken, basic, queryParam, oidc, hmac, none)
30
30
  * @returns {{ method: string, variables: Object, security: Object }} Authentication object
31
31
  */
32
32
  function buildAuthenticationFromMethod(systemKey, authType) {
@@ -50,6 +50,10 @@ function buildAuthenticationFromMethod(systemKey, authType) {
50
50
  variables: { baseUrl: base, headerName: 'X-API-Key' },
51
51
  security: { apiKey: kvPath('apiKey') }
52
52
  },
53
+ bearerToken: {
54
+ variables: { baseUrl: base, headerName: 'Authorization', prefix: 'Bearer' },
55
+ security: { token: kvPath('token') }
56
+ },
53
57
  basic: {
54
58
  variables: { baseUrl: base },
55
59
  security: { username: kvPath('username'), password: kvPath('password') }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Reuse dataplane auth resolved during sync/upload across verify and test commands.
3
+ *
4
+ * @fileoverview Integration auth context helpers
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { setupIntegrationTestAuth } = require('./test-auth');
12
+ const { getConfig } = require('../core/config');
13
+
14
+ /**
15
+ * @param {Object} options
16
+ * @returns {Object}
17
+ */
18
+ function integrationAuthOptions(options) {
19
+ const base = {
20
+ environment: options.env,
21
+ silentResolve: options.silentResolve === true
22
+ };
23
+ if (options.authConfig && options.dataplaneUrl) {
24
+ return { ...base, authConfig: options.authConfig, dataplaneUrl: options.dataplaneUrl };
25
+ }
26
+ return base;
27
+ }
28
+
29
+ /**
30
+ * @param {string} systemKey
31
+ * @param {Object} options
32
+ * @returns {Promise<{ authConfig: Object, dataplaneUrl: string }>}
33
+ */
34
+ async function resolveIntegrationAuth(systemKey, options) {
35
+ if (options.authConfig && options.dataplaneUrl) {
36
+ return { authConfig: options.authConfig, dataplaneUrl: options.dataplaneUrl };
37
+ }
38
+ const configObj = await getConfig();
39
+ return setupIntegrationTestAuth(systemKey, integrationAuthOptions(options), configObj);
40
+ }
41
+
42
+ module.exports = {
43
+ integrationAuthOptions,
44
+ resolveIntegrationAuth
45
+ };
@@ -26,9 +26,14 @@ const { resolveControllerUrl } = require('../utils/controller-url');
26
26
  * @throws {Error} If authentication fails
27
27
  */
28
28
  async function setupIntegrationTestAuth(appName, options, _config) {
29
+ const opts = options && typeof options === 'object' ? options : {};
30
+ if (opts.authConfig && opts.dataplaneUrl) {
31
+ return { authConfig: opts.authConfig, dataplaneUrl: opts.dataplaneUrl };
32
+ }
33
+
29
34
  const { resolveEnvironment } = require('../core/config');
30
- const environment = (options && options.environment && typeof options.environment === 'string' && options.environment.trim())
31
- ? options.environment.trim()
35
+ const environment = (opts.environment && typeof opts.environment === 'string' && opts.environment.trim())
36
+ ? opts.environment.trim()
32
37
  : await resolveEnvironment();
33
38
  const controllerUrl = await resolveControllerUrl();
34
39
  const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
@@ -38,10 +43,12 @@ async function setupIntegrationTestAuth(appName, options, _config) {
38
43
  }
39
44
 
40
45
  let dataplaneUrl;
41
- if (options && options.dataplane && typeof options.dataplane === 'string' && options.dataplane.trim()) {
42
- dataplaneUrl = options.dataplane.trim();
46
+ if (opts.dataplane && typeof opts.dataplane === 'string' && opts.dataplane.trim()) {
47
+ dataplaneUrl = opts.dataplane.trim();
43
48
  } else {
44
- dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
49
+ dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, {
50
+ silent: opts.silentResolve === true
51
+ });
45
52
  }
46
53
 
47
54
  return { authConfig, dataplaneUrl };
@@ -16,7 +16,7 @@ const { getIntegrationPath } = require('../utils/paths');
16
16
  const { runUnifiedDatasourceValidation } = require('../datasource/unified-validation-run');
17
17
 
18
18
  async function runOneDatasourceKey(appName, datasourceKey, opts) {
19
- const { environment, runType, debug, timeoutMs } = opts;
19
+ const { environment, runType, debug, timeoutMs, authConfig, dataplaneUrl } = opts;
20
20
  try {
21
21
  const uni = await runUnifiedDatasourceValidation(datasourceKey, {
22
22
  app: appName,
@@ -26,7 +26,9 @@ async function runOneDatasourceKey(appName, datasourceKey, opts) {
26
26
  verbose: false,
27
27
  timeout: timeoutMs,
28
28
  async: true,
29
- noAsync: false
29
+ noAsync: false,
30
+ authConfig,
31
+ dataplaneUrl
30
32
  });
31
33
 
32
34
  if (uni.apiError) {
@@ -89,7 +91,17 @@ async function maybeWriteSystemRunLog(appName, systemKey, datasourceKeys, runTyp
89
91
  * @param {number} [params.timeout] - Request timeout
90
92
  * @returns {Promise<{success: boolean, datasourceResults: Object[]}>}
91
93
  */
92
- async function runSystemLevelTest({ appName, systemKey, datasourceKeys, environment, runType, debug, timeout }) {
94
+ async function runSystemLevelTest({
95
+ appName,
96
+ systemKey,
97
+ datasourceKeys,
98
+ environment,
99
+ runType,
100
+ debug,
101
+ timeout,
102
+ authConfig,
103
+ dataplaneUrl
104
+ }) {
93
105
  if (!Array.isArray(datasourceKeys) || datasourceKeys.length === 0) {
94
106
  return { success: true, datasourceResults: [] };
95
107
  }
@@ -100,7 +112,16 @@ async function runSystemLevelTest({ appName, systemKey, datasourceKeys, environm
100
112
  // Keep sequential to avoid spiking external APIs / dataplane concurrency in CLI runs.
101
113
  // (Plan allows parallel later, but UX needs stable logs first.)
102
114
  // eslint-disable-next-line no-await-in-loop -- intentional sequential execution
103
- results.push(await runOneDatasourceKey(appName, key, { environment, runType, debug, timeoutMs }));
115
+ results.push(
116
+ await runOneDatasourceKey(appName, key, {
117
+ environment,
118
+ runType,
119
+ debug,
120
+ timeoutMs,
121
+ authConfig,
122
+ dataplaneUrl
123
+ })
124
+ );
104
125
  }
105
126
 
106
127
  const success = results.every(r => r.success);
@@ -38,6 +38,7 @@ const {
38
38
  } = require('./test-execution');
39
39
  const { runSystemLevelTest } = require('./test-system-level');
40
40
  const { shouldUseSystemLevelIntegrationCall } = require('./integration-test-dispatch');
41
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
41
42
 
42
43
  /**
43
44
  * Loads and parses application config file
@@ -412,7 +413,8 @@ function determineDatasourcesToTest(datasourceFiles, datasourceFilter) {
412
413
  * @param {string} [options.controller] - Controller URL
413
414
  * @param {boolean} [options.verbose] - Show detailed test output
414
415
  * @param {number} [options.timeout] - Request timeout in milliseconds
415
- * @param {boolean} [options.sync] - When true, run full `aifabrix upload`-equivalent publish before tests
416
+ * @param {boolean} [options.noSync] - When true, skip publish (exercise dataplane config already deployed)
417
+ * @param {boolean} [options.sync] - Legacy explicit sync; ignored when `noSync` / `--no-sync` is set
416
418
  * @returns {Promise<Object>} Integration test results
417
419
  * @throws {Error} If testing fails
418
420
  */
@@ -457,13 +459,11 @@ async function testExternalSystemIntegration(appName, options = {}) {
457
459
 
458
460
  const { systemKey, authConfig, dataplaneUrl, datasourcesToTest, customPayload } = await prepareIntegrationTestEnvironment(appName, options);
459
461
 
460
- if (options.sync === true) {
462
+ if (!cliOptsSkipSync(options)) {
461
463
  logger.log(chalk.cyan('Syncing local config to dataplane…'));
462
464
  const { uploadExternalSystem } = require('../commands/upload');
463
- await uploadExternalSystem(systemKey, {
464
- verbose: !!options.verbose,
465
- minimal: true
466
- });
465
+ const { buildMinimalUploadSyncOptions } = require('../utils/upload-sync-options');
466
+ await uploadExternalSystem(systemKey, buildMinimalUploadSyncOptions(options));
467
467
  logger.log(formatSuccessLine('Sync complete'));
468
468
  }
469
469
 
@@ -486,7 +486,9 @@ async function testExternalSystemIntegration(appName, options = {}) {
486
486
  environment: options.environment,
487
487
  runType: 'integration',
488
488
  debug: options.debug,
489
- timeout
489
+ timeout,
490
+ authConfig,
491
+ dataplaneUrl
490
492
  });
491
493
  results.success = systemResult.success;
492
494
  results.datasourceResults = systemResult.datasourceResults;
@@ -22,6 +22,19 @@ const { loadVariables, loadRbac } = require('./helpers');
22
22
  * @param {Object} systemJson - System JSON object
23
23
  * @param {Object|null} rbac - RBAC configuration
24
24
  */
25
+ /**
26
+ * Maps legacy integration auth method names to schema 1.3.0 values before upload/deploy.
27
+ * @param {Object} systemJson - System JSON object (mutated in place)
28
+ */
29
+ function normalizeLegacyAuthMethod(systemJson) {
30
+ const auth = systemJson && systemJson.authentication;
31
+ if (!auth || typeof auth !== 'object') return;
32
+ const method = String(auth.method || '').trim().toLowerCase();
33
+ if (method === 'bearerkey') {
34
+ auth.method = 'bearerToken';
35
+ }
36
+ }
37
+
25
38
  function mergeRbacIntoSystemJson(systemJson, rbac) {
26
39
  if (!rbac) {
27
40
  return;
@@ -78,6 +91,7 @@ function extractAppMetadata(variables, appName) {
78
91
  */
79
92
  async function loadSystemWithRbac(appPath, schemaBasePath, systemFile) {
80
93
  const systemJson = await loadSystemFile(appPath, schemaBasePath, systemFile);
94
+ normalizeLegacyAuthMethod(systemJson);
81
95
  const rbacPath = resolveRbacPath(appPath);
82
96
  const rbac = rbacPath ? loadRbac(rbacPath) : null;
83
97
  mergeRbacIntoSystemJson(systemJson, rbac);
@@ -187,6 +201,7 @@ function toDeployJsonShape(manifest) {
187
201
  }
188
202
 
189
203
  module.exports = {
204
+ normalizeLegacyAuthMethod,
190
205
  generateControllerManifest,
191
206
  toDeployJsonShape
192
207
  };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @fileoverview Governance CLI error messages (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ /**
8
+ * @param {Error|string} err
9
+ * @returns {boolean}
10
+ */
11
+ function isGovernancePackNotFoundError(err) {
12
+ const raw = (err && err.message) || String(err);
13
+ return /not found/i.test(raw) || /\b404\b/.test(raw);
14
+ }
15
+
16
+ /**
17
+ * @param {Error|string} err
18
+ * @returns {boolean}
19
+ */
20
+ function isGovernanceCoverageSchemaError(err) {
21
+ const raw = (err && err.message) || String(err);
22
+ return (
23
+ raw.includes('\'coverage\' is a required property') || /spec\.coverage/i.test(raw)
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Short, non-duplicative CLI message from a dataplane governance API error.
29
+ * @param {Error|string} err
30
+ * @returns {string}
31
+ */
32
+ function shortGovernanceApiError(err) {
33
+ const raw = ((err && err.message) || String(err)).trim();
34
+ if (isGovernancePackNotFoundError(err)) {
35
+ return 'No active pack on dataplane';
36
+ }
37
+ if (isGovernanceCoverageSchemaError(err)) {
38
+ return 'Pack missing spec.coverage (424.0 schema)';
39
+ }
40
+ const firstLine = raw.split('\n').find(line => line.trim()) || raw;
41
+ if (/^Governance scenario pack not found/i.test(firstLine)) {
42
+ return 'No active pack on dataplane';
43
+ }
44
+ return firstLine.length > 100 ? `${firstLine.slice(0, 97)}...` : firstLine;
45
+ }
46
+
47
+ /**
48
+ * User-facing hint after a failed governance pack upload (validation).
49
+ * @param {string} systemKey
50
+ * @returns {string}
51
+ */
52
+ function governanceUploadSchemaHint(systemKey) {
53
+ return (
54
+ 'Scenario pack does not meet the 424.0 schema. Use dataplane-first generation:\n' +
55
+ ` aifabrix governance generate ${systemKey} --subject-email <email>\n` +
56
+ 'Or: aifabrix verify-governance ' +
57
+ `${systemKey} --subject-email <email>`
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Clarify subject-resolution 400s (dataplane reachable; User row missing).
63
+ * @param {Error} err
64
+ * @param {{ subjectEmails?: string[] }} [context]
65
+ * @returns {Error}
66
+ */
67
+ function enrichUnresolvableGovernanceSubjectsError(err, context = {}) {
68
+ const hay = `${err.message || ''} ${err.formatted || ''}`;
69
+ if (!hay.includes('No resolvable subjects')) {
70
+ return err;
71
+ }
72
+ const emails = Array.isArray(context.subjectEmails)
73
+ ? context.subjectEmails.filter(Boolean)
74
+ : [];
75
+ const lines = [
76
+ '',
77
+ 'The dataplane is reachable. This is not a network or controller error.',
78
+ 'Pack generation needs --subject-email values that match existing dataplane User rows.',
79
+ emails.length > 0 ? `Subject email(s) you passed: ${emails.join(', ')}` : null,
80
+ 'Check:',
81
+ ' aifabrix identity user list --search <email>',
82
+ 'If the user is missing, apply your identity CSV first:',
83
+ ' aifabrix identity apply --file integration/<systemKey>/fixtures/<users>.csv --filter-prefix <systemKey>',
84
+ ' aifabrix identity sync -e dev --purge-cache'
85
+ ].filter(line => line !== null);
86
+ err.formatted = `${err.formatted || err.message}${lines.join('\n')}`;
87
+ return err;
88
+ }
89
+
90
+ module.exports = {
91
+ isGovernancePackNotFoundError,
92
+ isGovernanceCoverageSchemaError,
93
+ shortGovernanceApiError,
94
+ governanceUploadSchemaHint,
95
+ enrichUnresolvableGovernanceSubjectsError
96
+ };