@aifabrix/builder 2.45.5 → 2.47.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 (355) 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/project-rules.mdc +1 -0
  4. package/.npmrc.token +1 -0
  5. package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
  6. package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
  7. package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
  8. package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
  9. package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
  10. package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
  11. package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
  12. package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
  13. package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
  14. package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
  15. package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
  16. package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
  17. package/.temp/validation/summary.txt +5 -0
  18. package/README.md +43 -17
  19. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  20. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  21. package/jest.config.default.js +2 -1
  22. package/jest.config.isolated.js +2 -1
  23. package/jest.global-live-fabrix-hooks.js +36 -0
  24. package/jest.isolated-projects.js +146 -0
  25. package/jest.projects.js +65 -136
  26. package/lib/api/api-response-errors.js +124 -0
  27. package/lib/api/dataplane-health.api.js +19 -7
  28. package/lib/api/governance-scenario-pack.api.js +159 -0
  29. package/lib/api/index.js +43 -3
  30. package/lib/api/lifecycle.api.js +98 -0
  31. package/lib/api/pipeline.api.js +3 -1
  32. package/lib/api/records-bulk.api.js +14 -1
  33. package/lib/api/resource-types.api.js +107 -0
  34. package/lib/api/runtime-configuration.api.js +72 -0
  35. package/lib/api/types/resource-types.types.js +45 -0
  36. package/lib/api/user-groups.api.js +9 -9
  37. package/lib/api/users.api.js +8 -8
  38. package/lib/api/validation-runner.js +3 -1
  39. package/lib/api/wizard.api.js +10 -2
  40. package/lib/app/deploy-config.js +4 -1
  41. package/lib/app/down.js +78 -27
  42. package/lib/app/index.js +2 -2
  43. package/lib/app/push.js +4 -5
  44. package/lib/app/register.js +7 -4
  45. package/lib/app/run-env-compose.js +84 -30
  46. package/lib/app/run-resolve-image.js +2 -1
  47. package/lib/app/run-stop-containers.js +113 -0
  48. package/lib/app/run.js +1 -31
  49. package/lib/app/show-display.js +35 -0
  50. package/lib/app/show.js +32 -1
  51. package/lib/build/index.js +22 -5
  52. package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
  53. package/lib/certification/sync-system-certification.js +13 -6
  54. package/lib/channels/add-channel.js +348 -0
  55. package/lib/channels/channel-artifact-generator.js +141 -0
  56. package/lib/channels/channel-audit.js +101 -0
  57. package/lib/channels/channel-auth-context.js +37 -0
  58. package/lib/channels/channel-credential.js +200 -0
  59. package/lib/channels/channel-display-name.js +64 -0
  60. package/lib/channels/channel-external-system.js +106 -0
  61. package/lib/channels/channel-integration-generator.js +274 -0
  62. package/lib/channels/channel-probe.js +89 -0
  63. package/lib/channels/channel-runtime-resolver.js +152 -0
  64. package/lib/channels/channel-status-display.js +141 -0
  65. package/lib/channels/channel-targets.js +115 -0
  66. package/lib/cli/index.js +20 -4
  67. package/lib/cli/infra-guided-auth.js +109 -0
  68. package/lib/cli/infra-guided.js +84 -81
  69. package/lib/cli/setup-app.help.js +0 -31
  70. package/lib/cli/setup-app.js +82 -5
  71. package/lib/cli/setup-app.test-commands.js +5 -57
  72. package/lib/cli/setup-auth.js +25 -0
  73. package/lib/cli/setup-certification-commands.js +147 -0
  74. package/lib/cli/setup-certification.help.js +141 -0
  75. package/lib/cli/setup-environment.js +19 -2
  76. package/lib/cli/setup-external-system-help.js +62 -0
  77. package/lib/cli/setup-external-system.js +25 -3
  78. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  79. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  80. package/lib/cli/setup-infra.js +9 -1
  81. package/lib/cli/setup-platform.js +6 -1
  82. package/lib/cli/setup-secrets.js +33 -1
  83. package/lib/cli/setup-utility-help-after.js +25 -2
  84. package/lib/cli/setup-utility-repair.js +198 -0
  85. package/lib/cli/setup-utility-resolve.js +44 -5
  86. package/lib/cli/setup-utility-validate.js +197 -0
  87. package/lib/cli/setup-utility.js +4 -239
  88. package/lib/commands/app-logs.js +54 -26
  89. package/lib/commands/app.js +41 -0
  90. package/lib/commands/auth-status-client-tokens.js +214 -0
  91. package/lib/commands/auth-status-dataplane-version.js +142 -68
  92. package/lib/commands/auth-status.js +43 -54
  93. package/lib/commands/channel.js +58 -0
  94. package/lib/commands/datasource-test-trust-cli.js +27 -23
  95. package/lib/commands/datasource-unified-test-cli.js +6 -3
  96. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  97. package/lib/commands/datasource.js +8 -8
  98. package/lib/commands/dev-show-display.js +8 -1
  99. package/lib/commands/governance-cli-shared.js +94 -0
  100. package/lib/commands/governance-command-action.js +132 -0
  101. package/lib/commands/governance-download-external.js +34 -0
  102. package/lib/commands/governance-generate-external.js +71 -0
  103. package/lib/commands/governance-upload-external.js +36 -0
  104. package/lib/commands/governance-verify-external.js +64 -0
  105. package/lib/commands/governance.js +144 -0
  106. package/lib/commands/identity-apply-sync.js +49 -8
  107. package/lib/commands/identity-group-cli.js +66 -26
  108. package/lib/commands/identity-membership-role-cache.js +58 -37
  109. package/lib/commands/identity-shared.js +22 -1
  110. package/lib/commands/identity-user-cli.js +80 -34
  111. package/lib/commands/lifecycle-command-action.js +123 -0
  112. package/lib/commands/lifecycle-external.js +268 -0
  113. package/lib/commands/login.js +11 -2
  114. package/lib/commands/repair-auth-apply.js +153 -0
  115. package/lib/commands/repair-auth-config.js +8 -13
  116. package/lib/commands/repair-changed-files.js +41 -0
  117. package/lib/commands/repair-datasource-com-v2.js +15 -0
  118. package/lib/commands/repair-datasource-keys.js +4 -1
  119. package/lib/commands/repair-datasource-run.js +59 -0
  120. package/lib/commands/repair-datasource-service-contract.js +206 -0
  121. package/lib/commands/repair-datasource-test-payload.js +259 -0
  122. package/lib/commands/repair-datasource.js +49 -64
  123. package/lib/commands/repair-env-template.js +76 -25
  124. package/lib/commands/repair-external-run.js +216 -0
  125. package/lib/commands/repair-openapi-sync.js +49 -4
  126. package/lib/commands/repair-persist.js +4 -0
  127. package/lib/commands/repair-rbac.js +52 -1
  128. package/lib/commands/repair-steps.js +160 -0
  129. package/lib/commands/repair-system-alignment.js +7 -4
  130. package/lib/commands/repair.js +29 -296
  131. package/lib/commands/resource-type.js +371 -0
  132. package/lib/commands/secrets-get.js +184 -0
  133. package/lib/commands/secrets-remove-all.js +3 -1
  134. package/lib/commands/setup-image-refresh.js +146 -0
  135. package/lib/commands/setup-modes.js +158 -162
  136. package/lib/commands/setup-platform-auth.js +76 -0
  137. package/lib/commands/setup-prompts-admin.js +244 -0
  138. package/lib/commands/setup-prompts-platform-mode.js +70 -0
  139. package/lib/commands/setup-prompts-validators.js +36 -0
  140. package/lib/commands/setup-prompts.js +24 -76
  141. package/lib/commands/setup-run-context.js +45 -0
  142. package/lib/commands/setup-spinners.js +39 -0
  143. package/lib/commands/setup.js +27 -5
  144. package/lib/commands/teardown.js +39 -0
  145. package/lib/commands/test-e2e-external.js +24 -8
  146. package/lib/commands/test-governance-external.js +3 -24
  147. package/lib/commands/test-trust-external.js +48 -100
  148. package/lib/commands/up-common.js +57 -13
  149. package/lib/commands/up-dataplane.js +56 -15
  150. package/lib/commands/upload-publish-context.js +75 -0
  151. package/lib/commands/upload-sidecar-followups.js +89 -0
  152. package/lib/commands/upload.js +113 -110
  153. package/lib/commands/verify-governance-combined-external.js +61 -0
  154. package/lib/commands/verify-governance-command-action.js +64 -0
  155. package/lib/commands/verify-governance-db.js +102 -0
  156. package/lib/commands/verify-governance-external.js +77 -0
  157. package/lib/commands/verify-governance-result-builders.js +71 -0
  158. package/lib/commands/verify-operations-command-action.js +68 -0
  159. package/lib/commands/verify-operations-external.js +268 -0
  160. package/lib/commands/verify-operations-persist.js +75 -0
  161. package/lib/commands/verify-operations-step-plan.js +68 -0
  162. package/lib/commands/verify-operations-steps.js +111 -0
  163. package/lib/commands/verify-trust-command-action.js +73 -0
  164. package/lib/commands/verify-trust-external.js +59 -0
  165. package/lib/commands/wizard-config-normalizer.js +43 -23
  166. package/lib/commands/wizard-core.js +2 -0
  167. package/lib/core/admin-password-generator.js +38 -0
  168. package/lib/core/admin-secrets-bundle.js +111 -0
  169. package/lib/core/admin-secrets-env-overlay.js +62 -0
  170. package/lib/core/admin-secrets.js +32 -4
  171. package/lib/core/config-attach-extensions.js +3 -0
  172. package/lib/core/config-platform-controller.js +44 -0
  173. package/lib/core/config-setup-installation-profile.js +42 -0
  174. package/lib/core/config-setup-platform-mode.js +46 -0
  175. package/lib/core/config.js +46 -36
  176. package/lib/core/secrets-admin-env.js +6 -2
  177. package/lib/core/secrets-ensure.js +4 -0
  178. package/lib/core/secrets-env-content.js +54 -48
  179. package/lib/core/secrets-env-write.js +10 -1
  180. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  181. package/lib/core/setup-installation-profile.js +43 -0
  182. package/lib/core/setup-platform-mode.js +90 -0
  183. package/lib/datasource/agent-trust-debug-log.js +1 -1
  184. package/lib/datasource/agent-trust-run.js +5 -3
  185. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  186. package/lib/datasource/audit-evidence-extract.js +7 -4
  187. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  188. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  189. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  190. package/lib/datasource/capability/capability-manifest.js +254 -0
  191. package/lib/datasource/capability/capability-resolve.js +3 -4
  192. package/lib/datasource/capability/copy-operations.js +5 -3
  193. package/lib/datasource/capability/remove-operations.js +2 -1
  194. package/lib/datasource/capability/run-capability-copy.js +32 -4
  195. package/lib/datasource/capability/run-capability-diff.js +3 -3
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  197. package/lib/datasource/capability/run-capability-edit.js +4 -7
  198. package/lib/datasource/capability/run-capability-relate.js +2 -4
  199. package/lib/datasource/capability/run-capability-remove.js +2 -4
  200. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  201. package/lib/datasource/datasource-identifier.js +2 -1
  202. package/lib/datasource/datasource-load-export-context.js +3 -0
  203. package/lib/datasource/datasource-validate-display.js +1 -1
  204. package/lib/datasource/field-reference-validator.js +24 -3
  205. package/lib/datasource/log-cleaner.js +2 -2
  206. package/lib/datasource/log-viewer.js +8 -8
  207. package/lib/datasource/orphan-profiles-validator.js +98 -0
  208. package/lib/datasource/test-e2e.js +4 -1
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run.js +5 -2
  211. package/lib/datasource/validate.js +41 -23
  212. package/lib/deployment/environment-poll-status.js +138 -0
  213. package/lib/deployment/environment.js +42 -34
  214. package/lib/external-system/download-helpers.js +87 -0
  215. package/lib/external-system/download.js +78 -138
  216. package/lib/external-system/generator.js +1 -55
  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.js +16 -21
  220. package/lib/generator/builders.js +11 -1
  221. package/lib/generator/external-controller-manifest.js +18 -0
  222. package/lib/generator/split.js +3 -1
  223. package/lib/generator/wizard.js +1 -28
  224. package/lib/governance/governance-cli-errors.js +96 -0
  225. package/lib/governance/governance-download-display.js +38 -0
  226. package/lib/governance/governance-run-display.js +215 -25
  227. package/lib/identity/fk-reference.js +65 -0
  228. package/lib/identity/identity-apply-core.js +13 -58
  229. package/lib/identity/identity-apply-phases.js +5 -1
  230. package/lib/identity/identity-display.js +243 -0
  231. package/lib/identity/identity-response-helpers.js +52 -0
  232. package/lib/identity/identity-sync-errors.js +62 -0
  233. package/lib/identity/resolve-user-id.js +129 -0
  234. package/lib/identity/username-default.js +31 -0
  235. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  236. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  237. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  238. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  239. package/lib/infrastructure/helpers.js +23 -129
  240. package/lib/infrastructure/index.js +5 -6
  241. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  242. package/lib/lifecycle/certification-progress.js +231 -0
  243. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  244. package/lib/lifecycle/exit-codes.js +24 -0
  245. package/lib/lifecycle/json-envelope.js +25 -0
  246. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  247. package/lib/lifecycle/logger-quiet.js +29 -0
  248. package/lib/lifecycle/open-api-published.js +59 -0
  249. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  250. package/lib/lifecycle/product-model.js +196 -0
  251. package/lib/lifecycle/report-display-trust.js +70 -0
  252. package/lib/lifecycle/report-display.js +472 -0
  253. package/lib/lifecycle/scenario-upload.js +191 -0
  254. package/lib/lifecycle/verify-step-progress.js +87 -0
  255. package/lib/parameters/infra-kv-discovery.js +27 -2
  256. package/lib/protection/auth-context.js +10 -4
  257. package/lib/resolvers/datasource-resolver.js +2 -3
  258. package/lib/resolvers/resource-type-file.js +159 -0
  259. package/lib/schema/application-schema.json +17 -1
  260. package/lib/schema/datasource-test-run.schema.json +89 -6
  261. package/lib/schema/deployment-rules.yaml +2 -1
  262. package/lib/schema/external-datasource.schema.json +2947 -2619
  263. package/lib/schema/external-system.schema.json +1418 -761
  264. package/lib/schema/infra.parameter.yaml +45 -0
  265. package/lib/schema/type/credential-field-registry.json +103 -97
  266. package/lib/schema/type/document-storage.json +9 -5
  267. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  268. package/lib/utils/api.js +55 -31
  269. package/lib/utils/app-relative-path-env.js +112 -0
  270. package/lib/utils/app-run-containers.js +25 -2
  271. package/lib/utils/authentication-shape.js +224 -0
  272. package/lib/utils/build-copy.js +36 -5
  273. package/lib/utils/build-resolve-image.js +66 -12
  274. package/lib/utils/celery-companion-apps.js +53 -0
  275. package/lib/utils/cli-secrets-error-format.js +83 -21
  276. package/lib/utils/cli-utils.js +20 -4
  277. package/lib/utils/compose-celery-worker.js +23 -0
  278. package/lib/utils/compose-generate-docker-compose.js +13 -1
  279. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  280. package/lib/utils/config-secrets-path.js +46 -0
  281. package/lib/utils/configuration-env-resolver.js +36 -0
  282. package/lib/utils/controller-url.js +88 -1
  283. package/lib/utils/credential-secrets-env-kv.js +216 -0
  284. package/lib/utils/credential-secrets-env.js +175 -257
  285. package/lib/utils/dataplane-resolver.js +18 -2
  286. package/lib/utils/declarative-url-ports.js +27 -4
  287. package/lib/utils/deployment-auth-mode.js +41 -0
  288. package/lib/utils/developer-volume-id.js +33 -0
  289. package/lib/utils/docker-build.js +1 -4
  290. package/lib/utils/env-copy.js +14 -9
  291. package/lib/utils/env-kv-orphan-lines.js +43 -0
  292. package/lib/utils/error-formatters/network-errors.js +13 -4
  293. package/lib/utils/external-env-template.js +11 -3
  294. package/lib/utils/external-system-readiness-core.js +10 -0
  295. package/lib/utils/external-system-readiness-display.js +80 -23
  296. package/lib/utils/external-system-validators.js +8 -2
  297. package/lib/utils/health-check.js +4 -1
  298. package/lib/utils/help-builder.js +13 -8
  299. package/lib/utils/keycloak-realm-ready.js +101 -0
  300. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  301. package/lib/utils/manifest-source-emit.js +2 -0
  302. package/lib/utils/paths.js +25 -0
  303. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  304. package/lib/utils/platform-controller-url.js +157 -0
  305. package/lib/utils/platform-kv-ref.js +57 -0
  306. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  307. package/lib/utils/postgres-wipe.js +9 -2
  308. package/lib/utils/resolve-env-mode.js +47 -0
  309. package/lib/utils/runtime-configuration-download.js +189 -0
  310. package/lib/utils/runtime-configuration-values.js +119 -0
  311. package/lib/utils/schema-loader.js +19 -4
  312. package/lib/utils/secrets-generator.js +31 -25
  313. package/lib/utils/secrets-missing-error.js +164 -0
  314. package/lib/utils/service-url-label.js +81 -0
  315. package/lib/utils/setup-quiet-output.js +25 -0
  316. package/lib/utils/template-helpers.js +15 -0
  317. package/lib/utils/token-manager-client-credentials.js +201 -0
  318. package/lib/utils/token-manager-messages.js +8 -0
  319. package/lib/utils/token-manager-refresh.js +12 -2
  320. package/lib/utils/token-manager.js +86 -105
  321. package/lib/utils/upload-pipeline-ui.js +12 -5
  322. package/lib/utils/upload-sync-options.js +42 -0
  323. package/lib/utils/url-declarative-public-base.js +21 -3
  324. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  325. package/lib/utils/validation-poll-ui.js +8 -0
  326. package/lib/utils/validation-run-stage.js +45 -0
  327. package/lib/utils/worker-sidecars.js +142 -0
  328. package/lib/validation/external-manifest-validator.js +2 -0
  329. package/lib/validation/runtime-configuration-rules.js +98 -0
  330. package/lib/validation/validate-display-dimensions.js +154 -0
  331. package/lib/validation/validate-display-sections.js +276 -0
  332. package/lib/validation/validate-display-steps.js +67 -0
  333. package/lib/validation/validate-display.js +24 -402
  334. package/package.json +1 -1
  335. package/templates/applications/dataplane/application.yaml +7 -1
  336. package/templates/applications/dataplane/env.template +50 -14
  337. package/templates/applications/dataplane/rbac.yaml +108 -1
  338. package/templates/applications/miso-controller/application.yaml +2 -2
  339. package/templates/applications/miso-controller/env.template +53 -122
  340. package/templates/applications/miso-controller/rbac.yaml +28 -13
  341. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  342. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  343. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  344. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  345. package/templates/channels/slack/install.template.md.hbs +49 -0
  346. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  347. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  348. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  349. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  350. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  351. package/templates/external-system/README.md.hbs +74 -17
  352. package/templates/python/docker-compose.hbs +75 -18
  353. package/templates/typescript/docker-compose.hbs +70 -17
  354. package/lib/commands/test-governance-command-action.js +0 -55
  355. package/lib/commands/test-trust-command-action.js +0 -118
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @fileoverview governance upload — push integration/<systemKey>/scenarios/*.yaml (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const { uploadGovernanceScenarioPacks } = require('../lifecycle/scenario-upload');
8
+ const { resolveGovernanceIntegrationContext } = require('./governance-cli-shared');
9
+
10
+ /**
11
+ * @async
12
+ * @param {string} systemKey
13
+ * @param {Object} options
14
+ * @returns {Promise<Object>}
15
+ */
16
+ async function runGovernanceUploadForExternalSystem(systemKey, options = {}) {
17
+ const { resolvedKey, authConfig, dataplaneUrl } = await resolveGovernanceIntegrationContext(
18
+ systemKey,
19
+ options
20
+ );
21
+ const uploaded = await uploadGovernanceScenarioPacks(
22
+ resolvedKey,
23
+ dataplaneUrl,
24
+ authConfig,
25
+ { silent: options.json === true }
26
+ );
27
+ return {
28
+ systemKey: resolvedKey,
29
+ command: 'governance upload',
30
+ uploaded
31
+ };
32
+ }
33
+
34
+ module.exports = {
35
+ runGovernanceUploadForExternalSystem
36
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @fileoverview governance verify — run persisted packs (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const { runVerifyGovernanceFromDbPacks } = require('./verify-governance-db');
8
+ const {
9
+ sumEnforcementScenarios,
10
+ aggregateCoverage,
11
+ buildPackOverrideResult
12
+ } = require('./verify-governance-result-builders');
13
+ const { VERDICT, verdictFromPassed } = require('../lifecycle/product-model');
14
+
15
+ /**
16
+ * @async
17
+ * @param {string} systemKey
18
+ * @param {Object} options
19
+ * @returns {Promise<Object>}
20
+ */
21
+ async function runGovernanceVerifyForExternalSystem(systemKey, options = {}) {
22
+ const pathsUtil = require('../utils/paths');
23
+ const appType = await pathsUtil.detectAppType(systemKey).catch(() => null);
24
+ if (!appType || appType.baseDir !== 'integration') {
25
+ throw new Error(
26
+ 'governance verify applies to external integration folders only (integration/<systemKey>/).'
27
+ );
28
+ }
29
+
30
+ if (options.pack) {
31
+ const { runTestGovernanceForExternalSystem } = require('./test-governance-external');
32
+ const runPayload = await runTestGovernanceForExternalSystem(systemKey, options);
33
+ return buildPackOverrideResult(systemKey, runPayload, 'governance verify');
34
+ }
35
+
36
+ const { resolvedKey, datasourceRows } = await runVerifyGovernanceFromDbPacks(systemKey, options);
37
+ if (datasourceRows.length === 0) {
38
+ return {
39
+ systemKey: resolvedKey,
40
+ command: 'governance verify',
41
+ verdict: VERDICT.FAILED,
42
+ policyCoveragePercent: 0,
43
+ dimensionCoveragePercent: 0,
44
+ enforcementScenarios: { passed: 0, total: 0 },
45
+ datasourceRows: []
46
+ };
47
+ }
48
+
49
+ const allVerified = datasourceRows.every(r => r.verdict === VERDICT.VERIFIED);
50
+ const coverage = aggregateCoverage(datasourceRows);
51
+ return {
52
+ systemKey: resolvedKey,
53
+ command: 'governance verify',
54
+ verdict: verdictFromPassed(allVerified),
55
+ policyCoveragePercent: coverage.policy ?? (allVerified ? 100 : 0),
56
+ dimensionCoveragePercent: coverage.dimension ?? (allVerified ? 100 : 0),
57
+ enforcementScenarios: sumEnforcementScenarios(datasourceRows),
58
+ datasourceRows
59
+ };
60
+ }
61
+
62
+ module.exports = {
63
+ runGovernanceVerifyForExternalSystem
64
+ };
@@ -0,0 +1,144 @@
1
+ /**
2
+ * @fileoverview Integration validation scenario packs — governance command family (424.0).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const {
8
+ collectString,
9
+ governanceGenerateHandler,
10
+ governanceVerifyHandler,
11
+ governanceUploadHandler,
12
+ governanceDownloadHandler
13
+ } = require('./governance-command-action');
14
+ const {
15
+ GOVERNANCE_GENERATE_HELP_AFTER,
16
+ GOVERNANCE_VERIFY_HELP_AFTER,
17
+ GOVERNANCE_UPLOAD_HELP_AFTER,
18
+ GOVERNANCE_DOWNLOAD_HELP_AFTER,
19
+ GOVERNANCE_PARENT_HELP_AFTER
20
+ } = require('../cli/setup-certification.help');
21
+
22
+ const SHARED_OPTS = [
23
+ ['-e, --env <env>', 'Environment: dev, tst, or pro', 'dev'],
24
+ ['--no-sync', 'Skip publishing local integration files before dataplane calls'],
25
+ [
26
+ '--force',
27
+ 'With default sync, pass force to upload (same as aifabrix upload --force)'
28
+ ]
29
+ ];
30
+
31
+ /**
32
+ * @param {import('commander').Command} cmd
33
+ */
34
+ function applySharedGovernanceOpts(cmd) {
35
+ for (const [flags, desc, defaultValue] of SHARED_OPTS) {
36
+ cmd.option(flags, desc, defaultValue);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @param {import('commander').Command} governance
42
+ */
43
+ function registerGovernanceGenerate(governance) {
44
+ const cmd = governance
45
+ .command('generate <systemKey>')
46
+ .description('Generate integration validation scenario packs on the dataplane')
47
+ .option(
48
+ '--certification-profile <profile>',
49
+ 'Certification profile (bronze, silver, gold, …)',
50
+ 'bronze'
51
+ )
52
+ .option(
53
+ '--subject-user-id <id>',
54
+ 'Subject user id for generated scenarios (repeatable)',
55
+ collectString,
56
+ []
57
+ )
58
+ .option(
59
+ '--subject-email <email>',
60
+ 'Subject email for generated scenarios (repeatable)',
61
+ collectString,
62
+ []
63
+ )
64
+ .option(
65
+ '--max-samples-per-partition <n>',
66
+ 'Max keys per mustInclude/mustExclude partition',
67
+ '25'
68
+ )
69
+ .option('-v, --verbose', 'Verbose output')
70
+ .option('--json', 'Machine-readable envelope')
71
+ .addHelpText('after', GOVERNANCE_GENERATE_HELP_AFTER)
72
+ .action(governanceGenerateHandler);
73
+ applySharedGovernanceOpts(cmd);
74
+ }
75
+
76
+ /**
77
+ * @param {import('commander').Command} governance
78
+ */
79
+ function registerGovernanceVerify(governance) {
80
+ const cmd = governance
81
+ .command('verify <systemKey>')
82
+ .description('Run persisted scenario packs (governance:evaluate)')
83
+ .option(
84
+ '--pack <path>',
85
+ 'Authoring override: local scenario pack YAML instead of dataplane active pack'
86
+ )
87
+ .option('-a, --app <path>', 'Integration folder override (with --pack)')
88
+ .option('--scenario <id>', 'Scenario subset when using --pack (repeatable)', collectString, [])
89
+ .option('-v, --verbose', 'Per-datasource breakdown')
90
+ .option('--json', 'Machine-readable envelope')
91
+ .addHelpText('after', GOVERNANCE_VERIFY_HELP_AFTER)
92
+ .action(governanceVerifyHandler);
93
+ applySharedGovernanceOpts(cmd);
94
+ }
95
+
96
+ /**
97
+ * @param {import('commander').Command} governance
98
+ */
99
+ function registerGovernanceUpload(governance) {
100
+ const cmd = governance
101
+ .command('upload <systemKey>')
102
+ .description('Upload integration/<systemKey>/scenarios/*.yaml to the dataplane')
103
+ .option('--json', 'Machine-readable envelope')
104
+ .addHelpText('after', GOVERNANCE_UPLOAD_HELP_AFTER)
105
+ .action(governanceUploadHandler);
106
+ applySharedGovernanceOpts(cmd);
107
+ }
108
+
109
+ /**
110
+ * @param {import('commander').Command} governance
111
+ */
112
+ function registerGovernanceDownload(governance) {
113
+ const cmd = governance
114
+ .command('download <systemKey>')
115
+ .description('Download active packs to integration/<systemKey>/scenarios/')
116
+ .option('--json', 'Machine-readable envelope')
117
+ .addHelpText('after', GOVERNANCE_DOWNLOAD_HELP_AFTER)
118
+ .action(governanceDownloadHandler);
119
+ applySharedGovernanceOpts(cmd);
120
+ }
121
+
122
+ /**
123
+ * @param {import('commander').Command} program
124
+ */
125
+ function setupGovernanceCommands(program) {
126
+ const governance = program
127
+ .command('governance')
128
+ .description(
129
+ 'Integration validation scenario packs (generate, verify, download, upload)'
130
+ )
131
+ .addHelpText('after', GOVERNANCE_PARENT_HELP_AFTER);
132
+
133
+ applySharedGovernanceOpts(governance);
134
+
135
+ registerGovernanceGenerate(governance);
136
+ registerGovernanceVerify(governance);
137
+ registerGovernanceUpload(governance);
138
+ registerGovernanceDownload(governance);
139
+ }
140
+
141
+ module.exports = {
142
+ setupGovernanceCommands,
143
+ collectString
144
+ };
@@ -7,14 +7,15 @@
7
7
  'use strict';
8
8
 
9
9
  const logger = require('../utils/logger');
10
- const { formatBlockingError, formatSuccessLine } = require('../utils/cli-layout-chalk');
10
+ const { formatSuccessLine, headerKeyValue } = require('../utils/cli-layout-chalk');
11
11
  const {
12
12
  resolveControllerAndAuth,
13
13
  resolveEnvKey,
14
14
  requireNonEmpty,
15
15
  unwrapControllerData,
16
16
  throwIfApiFailed,
17
- assertSyncStatsOk
17
+ assertSyncStatsOk,
18
+ logIdentityCliError
18
19
  } = require('./identity-shared');
19
20
  const { clearAuthCache } = require('../api/auth-cache.api');
20
21
  const { fullSyncToDataplane } = require('../api/dataplane-sync.api');
@@ -70,7 +71,7 @@ function setupIdentityApplyCommand(identityCmd) {
70
71
  );
71
72
  }
72
73
  } catch (e) {
73
- logger.error(formatBlockingError(e.message));
74
+ logIdentityCliError(e.message);
74
75
  process.exit(1);
75
76
  }
76
77
  });
@@ -103,6 +104,46 @@ async function maybePurgeCache(controllerUrl, authConfig, purgeCache) {
103
104
  logger.log(formatSuccessLine('Controller cache cleared'));
104
105
  }
105
106
 
107
+ /**
108
+ * @param {string} controllerUrl
109
+ * @param {string} envKey
110
+ * @param {Object} authConfig
111
+ * @returns {Promise<string>}
112
+ */
113
+ async function resolveSyncDataplaneUrl(controllerUrl, envKey, authConfig) {
114
+ try {
115
+ const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
116
+ return await resolveDataplaneUrl(controllerUrl, envKey, authConfig, { silent: true });
117
+ } catch {
118
+ return '—';
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @param {Object} params
124
+ * @param {string} params.controllerUrl
125
+ * @param {string} params.envKey
126
+ * @param {Object} params.authConfig
127
+ * @param {Object|null|undefined} params.stats
128
+ */
129
+ async function printIdentitySyncSuccess({ controllerUrl, envKey, authConfig, stats }) {
130
+ const dataplaneUrl = await resolveSyncDataplaneUrl(controllerUrl, envKey, authConfig);
131
+ const usersProcessed = Number(stats?.usersProcessed ?? 0);
132
+ const groupsProcessed = Number(stats?.groupsProcessed ?? 0);
133
+ const syncErrors = Number(stats?.errors ?? 0);
134
+
135
+ logger.log('');
136
+ logger.log(formatSuccessLine(`Identity synced to dataplane (${envKey})`));
137
+ logger.log(headerKeyValue(' Controller:', controllerUrl));
138
+ logger.log(headerKeyValue(' Dataplane:', dataplaneUrl));
139
+ logger.log('');
140
+ logger.log(` Users: ${usersProcessed} processed`);
141
+ logger.log(` Groups: ${groupsProcessed} processed`);
142
+ if (syncErrors > 0) {
143
+ logger.log(` Errors: ${syncErrors}`);
144
+ }
145
+ }
146
+
106
147
  async function runIdentitySyncAction(options) {
107
148
  try {
108
149
  const { controllerUrl, authConfig } = await resolveControllerAndAuth();
@@ -116,16 +157,16 @@ async function runIdentitySyncAction(options) {
116
157
  printJson(stats);
117
158
  return;
118
159
  }
119
- logger.log(formatSuccessLine(`Identity synced to dataplane (${envKey})`));
120
- logger.log(` usersProcessed=${stats?.usersProcessed ?? 0}`);
121
- logger.log(` groupsProcessed=${stats?.groupsProcessed ?? 0}`);
160
+ await printIdentitySyncSuccess({ controllerUrl, envKey, authConfig, stats });
122
161
  } catch (e) {
123
- logger.error(formatBlockingError(e.message));
162
+ logIdentityCliError(e.message);
124
163
  process.exit(1);
125
164
  }
126
165
  }
127
166
 
128
167
  module.exports = {
129
168
  setupIdentityApplyCommand,
130
- setupIdentitySyncCommand
169
+ setupIdentitySyncCommand,
170
+ printIdentitySyncSuccess,
171
+ resolveSyncDataplaneUrl
131
172
  };
@@ -7,7 +7,7 @@
7
7
  'use strict';
8
8
 
9
9
  const logger = require('../utils/logger');
10
- const { formatBlockingError, formatSuccessLine, headerKeyValue } = require('../utils/cli-layout-chalk');
10
+ const { formatBlockingError, formatSuccessLine } = require('../utils/cli-layout-chalk');
11
11
  const {
12
12
  resolveControllerAndAuth,
13
13
  requireNonEmpty,
@@ -15,11 +15,26 @@ const {
15
15
  throwIfApiFailed
16
16
  } = require('./identity-shared');
17
17
  const { listGroups, getGroup, createGroup, listGroupMembers } = require('../api/groups.api');
18
+ const {
19
+ extractPaginatedList,
20
+ printGroupMembersTable,
21
+ printGroupRecord,
22
+ printIdentityEmpty,
23
+ printIdentityListHeader,
24
+ printIdentityNotFound,
25
+ printIdentityTable,
26
+ printListPaginationFooter,
27
+ GROUP_LIST_COLUMNS
28
+ } = require('../identity/identity-display');
18
29
 
19
30
  function printJson(data) {
20
31
  logger.log(JSON.stringify(data, null, 2));
21
32
  }
22
33
 
34
+ function groupNotFoundHints() {
35
+ return ['af identity group list --search <text>', 'Use group name or id from group list'];
36
+ }
37
+
23
38
  function registerGroupCreate(groupCmd) {
24
39
  groupCmd
25
40
  .command('create')
@@ -51,7 +66,7 @@ function registerGroupList(groupCmd) {
51
66
  .description('List groups')
52
67
  .option('--page <n>', 'Page', (v) => parseInt(v, 10))
53
68
  .option('--page-size <n>', 'Page size', (v) => parseInt(v, 10))
54
- .option('--search <text>', 'Search groups')
69
+ .option('--search <text>', 'Search groups by name or display name')
55
70
  .option('--json', 'JSON output')
56
71
  .action(async(options) => {
57
72
  try {
@@ -62,14 +77,24 @@ function registerGroupList(groupCmd) {
62
77
  search: options.search
63
78
  });
64
79
  throwIfApiFailed(res);
65
- const data = unwrapControllerData(res);
66
80
  if (options.json) {
67
- printJson(data);
81
+ printJson(res.data ?? res);
68
82
  return;
69
83
  }
70
- const items = Array.isArray(data) ? data : data?.data || [];
71
- items.forEach((g) => logger.log(` ${g.id} ${g.name}`));
72
- logger.log('');
84
+ const { items, meta } = extractPaginatedList(res);
85
+ printIdentityListHeader({
86
+ emoji: '👥',
87
+ title: 'Groups',
88
+ context: controllerUrl,
89
+ shown: items.length,
90
+ total: meta?.totalItems ?? items.length
91
+ });
92
+ if (items.length === 0) {
93
+ printIdentityEmpty('No groups found.', ['Try --search']);
94
+ return;
95
+ }
96
+ printIdentityTable(GROUP_LIST_COLUMNS, items);
97
+ printListPaginationFooter(meta);
73
98
  } catch (e) {
74
99
  logger.error(formatBlockingError(e.message));
75
100
  process.exit(1);
@@ -79,22 +104,28 @@ function registerGroupList(groupCmd) {
79
104
 
80
105
  function registerGroupGet(groupCmd) {
81
106
  groupCmd
82
- .command('get <idOrName>')
107
+ .command('get <groupIdOrName>')
83
108
  .description('Get group by id or name')
84
109
  .option('--json', 'JSON output')
85
- .action(async(idOrName, options) => {
110
+ .action(async(groupIdOrName, options) => {
86
111
  try {
87
112
  const { controllerUrl, authConfig } = await resolveControllerAndAuth();
88
- const res = await getGroup(controllerUrl, authConfig, requireNonEmpty(idOrName, 'idOrName'));
89
- throwIfApiFailed(res);
113
+ const value = requireNonEmpty(groupIdOrName, 'groupIdOrName');
114
+ const res = await getGroup(controllerUrl, authConfig, value);
115
+ if (!res || res.success === false) {
116
+ printIdentityNotFound('Group', value, groupNotFoundHints());
117
+ process.exit(1);
118
+ }
90
119
  const row = unwrapControllerData(res);
120
+ if (!row?.id) {
121
+ printIdentityNotFound('Group', value, groupNotFoundHints());
122
+ process.exit(1);
123
+ }
91
124
  if (options.json) {
92
125
  printJson(row);
93
126
  return;
94
127
  }
95
- logger.log(headerKeyValue('Id:', row.id));
96
- logger.log(headerKeyValue('Name:', row.name));
97
- logger.log('');
128
+ printGroupRecord(row);
98
129
  } catch (e) {
99
130
  logger.error(formatBlockingError(e.message));
100
131
  process.exit(1);
@@ -104,26 +135,35 @@ function registerGroupGet(groupCmd) {
104
135
 
105
136
  function registerGroupMembers(groupCmd) {
106
137
  groupCmd
107
- .command('members <idOrName>')
138
+ .command('members <groupIdOrName>')
108
139
  .description('List members of a group')
109
140
  .option('--json', 'JSON output')
110
- .action(async(idOrName, options) => {
141
+ .action(async(groupIdOrName, options) => {
111
142
  try {
112
143
  const { controllerUrl, authConfig } = await resolveControllerAndAuth();
113
- const res = await listGroupMembers(
114
- controllerUrl,
115
- authConfig,
116
- requireNonEmpty(idOrName, 'idOrName')
117
- );
118
- throwIfApiFailed(res);
144
+ const value = requireNonEmpty(groupIdOrName, 'groupIdOrName');
145
+ const res = await listGroupMembers(controllerUrl, authConfig, value);
146
+ if (!res || res.success === false) {
147
+ printIdentityNotFound('Group', value, groupNotFoundHints());
148
+ process.exit(1);
149
+ }
119
150
  const data = unwrapControllerData(res);
151
+ const items = Array.isArray(data) ? data : data?.data || [];
120
152
  if (options.json) {
121
- printJson(data);
153
+ printJson(items);
122
154
  return;
123
155
  }
124
- const items = Array.isArray(data) ? data : data?.data || [];
125
- items.forEach((m) => logger.log(` ${m.userId || m.id} ${m.email || ''}`));
126
- logger.log('');
156
+ printIdentityListHeader({
157
+ emoji: '👤',
158
+ title: `Members of ${value}`,
159
+ shown: items.length,
160
+ total: items.length
161
+ });
162
+ if (items.length === 0) {
163
+ printIdentityEmpty('No members in this group.');
164
+ return;
165
+ }
166
+ printGroupMembersTable(items);
127
167
  } catch (e) {
128
168
  logger.error(formatBlockingError(e.message));
129
169
  process.exit(1);
@@ -19,30 +19,43 @@ const {
19
19
  const { addUserToGroup, removeUserFromGroup } = require('../api/user-groups.api');
20
20
  const { listEnvironmentRoles, updateRoleGroups } = require('../api/environments.api');
21
21
  const { clearAuthCache, invalidateAuthCache } = require('../api/auth-cache.api');
22
+ const {
23
+ printIdentityEmpty,
24
+ printIdentityListHeader,
25
+ printIdentityTable
26
+ } = require('../identity/identity-display');
22
27
 
23
28
  function printJson(data) {
24
29
  logger.log(JSON.stringify(data, null, 2));
25
30
  }
26
31
 
32
+ const ROLE_LIST_COLUMNS = [
33
+ { label: 'Role', width: 36, value: (r) => r.value || r.role || r.name || '—' },
34
+ {
35
+ label: 'Groups',
36
+ width: 44,
37
+ value: (r) => {
38
+ const groups = Array.isArray(r.groups) ? r.groups.filter(Boolean) : [];
39
+ return groups.length > 0 ? groups.join(', ') : '—';
40
+ }
41
+ }
42
+ ];
43
+
27
44
  /**
28
45
  * @param {import('commander').Command} memCmd
29
46
  */
30
47
  function setupIdentityMembershipCommands(memCmd) {
31
48
  memCmd
32
- .command('add <userId> <groupId>')
33
- .description('Add user to group (controller ids)')
34
- .action(async(userId, groupId) => {
49
+ .command('add <userIdOrEmail> <groupIdOrName>')
50
+ .description('Add user to group (id, email, username + group id or name)')
51
+ .action(async(userIdOrEmail, groupIdOrName) => {
35
52
  try {
36
53
  const { controllerUrl, authConfig } = await resolveControllerAndAuth();
37
- const res = await addUserToGroup(
38
- controllerUrl,
39
- authConfig,
40
- requireNonEmpty(userId, 'userId'),
41
- requireNonEmpty(groupId, 'groupId'),
42
- {}
43
- );
54
+ const userRef = requireNonEmpty(userIdOrEmail, 'userIdOrEmail');
55
+ const groupRef = requireNonEmpty(groupIdOrName, 'groupIdOrName');
56
+ const res = await addUserToGroup(controllerUrl, authConfig, userRef, groupRef, {});
44
57
  throwIfApiFailed(res);
45
- logger.log(formatSuccessLine(`Membership created: ${userId} → ${groupId}`));
58
+ logger.log(formatSuccessLine(`Membership created: ${userRef} → ${groupRef}`));
46
59
  } catch (e) {
47
60
  logger.error(formatBlockingError(e.message));
48
61
  process.exit(1);
@@ -50,19 +63,16 @@ function setupIdentityMembershipCommands(memCmd) {
50
63
  });
51
64
 
52
65
  memCmd
53
- .command('remove <userId> <groupId>')
66
+ .command('remove <userIdOrEmail> <groupIdOrName>')
54
67
  .description('Remove user from group')
55
- .action(async(userId, groupId) => {
68
+ .action(async(userIdOrEmail, groupIdOrName) => {
56
69
  try {
57
70
  const { controllerUrl, authConfig } = await resolveControllerAndAuth();
58
- const res = await removeUserFromGroup(
59
- controllerUrl,
60
- authConfig,
61
- requireNonEmpty(userId, 'userId'),
62
- requireNonEmpty(groupId, 'groupId')
63
- );
71
+ const userRef = requireNonEmpty(userIdOrEmail, 'userIdOrEmail');
72
+ const groupRef = requireNonEmpty(groupIdOrName, 'groupIdOrName');
73
+ const res = await removeUserFromGroup(controllerUrl, authConfig, userRef, groupRef);
64
74
  throwIfApiFailed(res);
65
- logger.log(formatSuccessLine(`Membership removed: ${userId} → ${groupId}`));
75
+ logger.log(formatSuccessLine(`Membership removed: ${userRef} → ${groupRef}`));
66
76
  } catch (e) {
67
77
  logger.error(formatBlockingError(e.message));
68
78
  process.exit(1);
@@ -73,29 +83,40 @@ function setupIdentityMembershipCommands(memCmd) {
73
83
  /**
74
84
  * @param {import('commander').Command} roleCmd
75
85
  */
86
+ async function handleRoleListAction(options) {
87
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
88
+ const envKey = await resolveEnvKey(options);
89
+ const res = await listEnvironmentRoles(controllerUrl, envKey, authConfig);
90
+ throwIfApiFailed(res);
91
+ const data = unwrapControllerData(res);
92
+ if (options.json) {
93
+ printJson(data);
94
+ return;
95
+ }
96
+ const items = Array.isArray(data) ? data : data?.data || data?.roles || [];
97
+ printIdentityListHeader({
98
+ emoji: '🔐',
99
+ title: 'Role → group mappings',
100
+ context: `environment: ${envKey}`,
101
+ shown: (items || []).length,
102
+ total: (items || []).length
103
+ });
104
+ if (!items || items.length === 0) {
105
+ printIdentityEmpty('No roles found for this environment.');
106
+ return;
107
+ }
108
+ printIdentityTable(ROLE_LIST_COLUMNS, items);
109
+ }
110
+
76
111
  function setupIdentityRoleCommands(roleCmd) {
77
112
  roleCmd
78
113
  .command('list')
79
114
  .description('List roles and group mappings for an environment')
80
- .requiredOption('-e, --env <key>', 'Environment key (dev, tst, pro)')
115
+ .option('-e, --env <key>', 'Environment key (defaults from aifabrix config)')
81
116
  .option('--json', 'JSON output')
82
117
  .action(async(options) => {
83
118
  try {
84
- const { controllerUrl, authConfig } = await resolveControllerAndAuth();
85
- const envKey = await resolveEnvKey(options);
86
- const res = await listEnvironmentRoles(controllerUrl, envKey, authConfig);
87
- throwIfApiFailed(res);
88
- const data = unwrapControllerData(res);
89
- if (options.json) {
90
- printJson(data);
91
- return;
92
- }
93
- const items = Array.isArray(data) ? data : data?.data || data?.roles || [];
94
- (items || []).forEach((r) => {
95
- const groups = Array.isArray(r.groups) ? r.groups.join(', ') : '';
96
- logger.log(` ${r.value || r.role || r.name} → ${groups || '—'}`);
97
- });
98
- logger.log('');
119
+ await handleRoleListAction(options);
99
120
  } catch (e) {
100
121
  logger.error(formatBlockingError(e.message));
101
122
  process.exit(1);
@@ -105,7 +126,7 @@ function setupIdentityRoleCommands(roleCmd) {
105
126
  roleCmd
106
127
  .command('set-groups <roleValue>')
107
128
  .description('Map role to groups in an environment')
108
- .requiredOption('-e, --env <key>', 'Environment key')
129
+ .option('-e, --env <key>', 'Environment key (defaults from aifabrix config)')
109
130
  .requiredOption('--groups <names>', 'Comma-separated group names')
110
131
  .action(async(roleValue, options) => {
111
132
  try {
@@ -6,6 +6,9 @@
6
6
 
7
7
  'use strict';
8
8
 
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const { formatBlockingError } = require('../utils/cli-layout-chalk');
9
12
  const { resolveControllerUrl } = require('../utils/controller-url');
10
13
  const { normalizeControllerUrl, resolveEnvironment } = require('../core/config');
11
14
  const { getOrRefreshDeviceToken } = require('../utils/token-manager');
@@ -82,6 +85,23 @@ function parseGroupsList(groupsArg) {
82
85
  return parts;
83
86
  }
84
87
 
88
+ /**
89
+ * Print a blocking CLI error with optional multi-line guidance in gray.
90
+ *
91
+ * @param {string} message
92
+ * @returns {void}
93
+ */
94
+ function logIdentityCliError(message) {
95
+ const lines = String(message || '').split('\n');
96
+ const head = lines[0] || 'Request failed';
97
+ logger.error(formatBlockingError(head));
98
+ lines.slice(1).forEach((line) => {
99
+ if (line.trim()) {
100
+ logger.log(chalk.gray(line));
101
+ }
102
+ });
103
+ }
104
+
85
105
  module.exports = {
86
106
  resolveControllerAndAuth,
87
107
  resolveEnvKey,
@@ -90,5 +110,6 @@ module.exports = {
90
110
  unwrapControllerData,
91
111
  throwIfApiFailed,
92
112
  assertSyncStatsOk,
93
- apiErrorMessage
113
+ apiErrorMessage,
114
+ logIdentityCliError
94
115
  };