@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,131 @@
1
+ /**
2
+ * @fileoverview Identity apply and sync commands
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const logger = require('../utils/logger');
10
+ const { formatBlockingError, formatSuccessLine } = require('../utils/cli-layout-chalk');
11
+ const {
12
+ resolveControllerAndAuth,
13
+ resolveEnvKey,
14
+ requireNonEmpty,
15
+ unwrapControllerData,
16
+ throwIfApiFailed,
17
+ assertSyncStatsOk
18
+ } = require('./identity-shared');
19
+ const { clearAuthCache } = require('../api/auth-cache.api');
20
+ const { fullSyncToDataplane } = require('../api/dataplane-sync.api');
21
+ const { runIdentityApply } = require('../identity/identity-apply-service');
22
+
23
+ function printJson(data) {
24
+ logger.log(JSON.stringify(data, null, 2));
25
+ }
26
+
27
+ /**
28
+ * @param {import('commander').Command} identityCmd
29
+ */
30
+ function setupIdentityApplyCommand(identityCmd) {
31
+ identityCmd
32
+ .command('apply')
33
+ .description('Apply users/groups/memberships from a CSV file (users.csv shape)')
34
+ .requiredOption('--file <path>', 'CSV file path')
35
+ .option('-e, --env <key>', 'Environment for --sync')
36
+ .option('--filter-prefix <prefix>', 'Only rows whose Id starts with this prefix')
37
+ .option('--dry-run', 'Parse and plan only; no writes')
38
+ .option('--sync', 'Run dataplane full sync after apply')
39
+ .option('--purge-cache', 'Clear controller cache before sync')
40
+ .option('--allow-empty-sync', 'Do not fail when sync reports usersProcessed=0')
41
+ .option('--json', 'JSON summary')
42
+ .action(async(options) => {
43
+ try {
44
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
45
+ const envKey = options.sync ? await resolveEnvKey(options) : undefined;
46
+ const summary = await runIdentityApply(controllerUrl, authConfig, {
47
+ filePath: requireNonEmpty(options.file, 'file'),
48
+ filterPrefix: options.filterPrefix,
49
+ dryRun: Boolean(options.dryRun),
50
+ purgeCache: Boolean(options.purgeCache),
51
+ sync: Boolean(options.sync),
52
+ envKey,
53
+ allowEmptySync: Boolean(options.allowEmptySync)
54
+ });
55
+ if (options.json) {
56
+ printJson(summary);
57
+ return;
58
+ }
59
+ logger.log(formatSuccessLine('Identity apply finished'));
60
+ logger.log(
61
+ ` groups: ${summary.groupsProcessed} processed (${summary.groupsCreated} created)`
62
+ );
63
+ logger.log(` users: ${summary.usersProcessed} processed (${summary.usersCreated} created)`);
64
+ logger.log(
65
+ ` memberships: ${summary.membershipsProcessed} processed (${summary.membershipsCreated} created, ${summary.membershipsSkipped} skipped)`
66
+ );
67
+ if (summary.syncStats) {
68
+ logger.log(
69
+ ` sync: usersProcessed=${summary.syncStats.usersProcessed ?? 0} groupsProcessed=${summary.syncStats.groupsProcessed ?? 0}`
70
+ );
71
+ }
72
+ } catch (e) {
73
+ logger.error(formatBlockingError(e.message));
74
+ process.exit(1);
75
+ }
76
+ });
77
+ }
78
+
79
+ /**
80
+ * @param {import('commander').Command} identityCmd
81
+ */
82
+ function setupIdentitySyncCommand(identityCmd) {
83
+ identityCmd
84
+ .command('sync')
85
+ .description('Full identity sync from controller to dataplane')
86
+ .option('-e, --env <key>', 'Environment key')
87
+ .option('--purge-cache', 'Clear controller cache before sync')
88
+ .option('--allow-empty-sync', 'Do not fail when usersProcessed=0')
89
+ .option('--json', 'JSON stats')
90
+ .action(async(options) => runIdentitySyncAction(options));
91
+ }
92
+
93
+ /**
94
+ * @param {Object} options
95
+ * @returns {Promise<void>}
96
+ */
97
+ async function maybePurgeCache(controllerUrl, authConfig, purgeCache) {
98
+ if (!purgeCache) {
99
+ return;
100
+ }
101
+ const cacheRes = await clearAuthCache(controllerUrl, authConfig);
102
+ throwIfApiFailed(cacheRes);
103
+ logger.log(formatSuccessLine('Controller cache cleared'));
104
+ }
105
+
106
+ async function runIdentitySyncAction(options) {
107
+ try {
108
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
109
+ const envKey = await resolveEnvKey(options);
110
+ await maybePurgeCache(controllerUrl, authConfig, options.purgeCache);
111
+ const res = await fullSyncToDataplane(controllerUrl, authConfig, envKey);
112
+ throwIfApiFailed(res);
113
+ const stats = unwrapControllerData(res);
114
+ assertSyncStatsOk(stats, { allowEmptySync: options.allowEmptySync });
115
+ if (options.json) {
116
+ printJson(stats);
117
+ return;
118
+ }
119
+ logger.log(formatSuccessLine(`Identity synced to dataplane (${envKey})`));
120
+ logger.log(` usersProcessed=${stats?.usersProcessed ?? 0}`);
121
+ logger.log(` groupsProcessed=${stats?.groupsProcessed ?? 0}`);
122
+ } catch (e) {
123
+ logger.error(formatBlockingError(e.message));
124
+ process.exit(1);
125
+ }
126
+ }
127
+
128
+ module.exports = {
129
+ setupIdentityApplyCommand,
130
+ setupIdentitySyncCommand
131
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @fileoverview Identity group subcommands
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const logger = require('../utils/logger');
10
+ const { formatBlockingError, formatSuccessLine, headerKeyValue } = require('../utils/cli-layout-chalk');
11
+ const {
12
+ resolveControllerAndAuth,
13
+ requireNonEmpty,
14
+ unwrapControllerData,
15
+ throwIfApiFailed
16
+ } = require('./identity-shared');
17
+ const { listGroups, getGroup, createGroup, listGroupMembers } = require('../api/groups.api');
18
+
19
+ function printJson(data) {
20
+ logger.log(JSON.stringify(data, null, 2));
21
+ }
22
+
23
+ function registerGroupCreate(groupCmd) {
24
+ groupCmd
25
+ .command('create')
26
+ .description('Create a group')
27
+ .requiredOption('--name <name>', 'Group name (unique key)')
28
+ .requiredOption('--display-name <name>', 'Display name')
29
+ .option('--description <text>', 'Description')
30
+ .action(async(options) => {
31
+ try {
32
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
33
+ const res = await createGroup(controllerUrl, authConfig, {
34
+ name: requireNonEmpty(options.name, 'name'),
35
+ displayName: requireNonEmpty(options.displayName, 'display-name'),
36
+ description: options.description
37
+ });
38
+ throwIfApiFailed(res);
39
+ const row = unwrapControllerData(res);
40
+ logger.log(formatSuccessLine(`Group created: ${row.id} (${row.name})`));
41
+ } catch (e) {
42
+ logger.error(formatBlockingError(e.message));
43
+ process.exit(1);
44
+ }
45
+ });
46
+ }
47
+
48
+ function registerGroupList(groupCmd) {
49
+ groupCmd
50
+ .command('list')
51
+ .description('List groups')
52
+ .option('--page <n>', 'Page', (v) => parseInt(v, 10))
53
+ .option('--page-size <n>', 'Page size', (v) => parseInt(v, 10))
54
+ .option('--search <text>', 'Search groups')
55
+ .option('--json', 'JSON output')
56
+ .action(async(options) => {
57
+ try {
58
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
59
+ const res = await listGroups(controllerUrl, authConfig, {
60
+ page: options.page,
61
+ pageSize: options.pageSize,
62
+ search: options.search
63
+ });
64
+ throwIfApiFailed(res);
65
+ const data = unwrapControllerData(res);
66
+ if (options.json) {
67
+ printJson(data);
68
+ return;
69
+ }
70
+ const items = Array.isArray(data) ? data : data?.data || [];
71
+ items.forEach((g) => logger.log(` ${g.id} ${g.name}`));
72
+ logger.log('');
73
+ } catch (e) {
74
+ logger.error(formatBlockingError(e.message));
75
+ process.exit(1);
76
+ }
77
+ });
78
+ }
79
+
80
+ function registerGroupGet(groupCmd) {
81
+ groupCmd
82
+ .command('get <idOrName>')
83
+ .description('Get group by id or name')
84
+ .option('--json', 'JSON output')
85
+ .action(async(idOrName, options) => {
86
+ try {
87
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
88
+ const res = await getGroup(controllerUrl, authConfig, requireNonEmpty(idOrName, 'idOrName'));
89
+ throwIfApiFailed(res);
90
+ const row = unwrapControllerData(res);
91
+ if (options.json) {
92
+ printJson(row);
93
+ return;
94
+ }
95
+ logger.log(headerKeyValue('Id:', row.id));
96
+ logger.log(headerKeyValue('Name:', row.name));
97
+ logger.log('');
98
+ } catch (e) {
99
+ logger.error(formatBlockingError(e.message));
100
+ process.exit(1);
101
+ }
102
+ });
103
+ }
104
+
105
+ function registerGroupMembers(groupCmd) {
106
+ groupCmd
107
+ .command('members <idOrName>')
108
+ .description('List members of a group')
109
+ .option('--json', 'JSON output')
110
+ .action(async(idOrName, options) => {
111
+ try {
112
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
113
+ const res = await listGroupMembers(
114
+ controllerUrl,
115
+ authConfig,
116
+ requireNonEmpty(idOrName, 'idOrName')
117
+ );
118
+ throwIfApiFailed(res);
119
+ const data = unwrapControllerData(res);
120
+ if (options.json) {
121
+ printJson(data);
122
+ return;
123
+ }
124
+ const items = Array.isArray(data) ? data : data?.data || [];
125
+ items.forEach((m) => logger.log(` ${m.userId || m.id} ${m.email || ''}`));
126
+ logger.log('');
127
+ } catch (e) {
128
+ logger.error(formatBlockingError(e.message));
129
+ process.exit(1);
130
+ }
131
+ });
132
+ }
133
+
134
+ /**
135
+ * @param {import('commander').Command} groupCmd
136
+ */
137
+ function setupIdentityGroupCommands(groupCmd) {
138
+ registerGroupCreate(groupCmd);
139
+ registerGroupList(groupCmd);
140
+ registerGroupGet(groupCmd);
141
+ registerGroupMembers(groupCmd);
142
+ }
143
+
144
+ module.exports = {
145
+ setupIdentityGroupCommands
146
+ };
@@ -0,0 +1,175 @@
1
+ /**
2
+ * @fileoverview Identity membership, role, and cache subcommands
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const logger = require('../utils/logger');
10
+ const { formatBlockingError, formatSuccessLine } = require('../utils/cli-layout-chalk');
11
+ const {
12
+ resolveControllerAndAuth,
13
+ resolveEnvKey,
14
+ requireNonEmpty,
15
+ parseGroupsList,
16
+ unwrapControllerData,
17
+ throwIfApiFailed
18
+ } = require('./identity-shared');
19
+ const { addUserToGroup, removeUserFromGroup } = require('../api/user-groups.api');
20
+ const { listEnvironmentRoles, updateRoleGroups } = require('../api/environments.api');
21
+ const { clearAuthCache, invalidateAuthCache } = require('../api/auth-cache.api');
22
+
23
+ function printJson(data) {
24
+ logger.log(JSON.stringify(data, null, 2));
25
+ }
26
+
27
+ /**
28
+ * @param {import('commander').Command} memCmd
29
+ */
30
+ function setupIdentityMembershipCommands(memCmd) {
31
+ memCmd
32
+ .command('add <userId> <groupId>')
33
+ .description('Add user to group (controller ids)')
34
+ .action(async(userId, groupId) => {
35
+ try {
36
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
37
+ const res = await addUserToGroup(
38
+ controllerUrl,
39
+ authConfig,
40
+ requireNonEmpty(userId, 'userId'),
41
+ requireNonEmpty(groupId, 'groupId'),
42
+ {}
43
+ );
44
+ throwIfApiFailed(res);
45
+ logger.log(formatSuccessLine(`Membership created: ${userId} → ${groupId}`));
46
+ } catch (e) {
47
+ logger.error(formatBlockingError(e.message));
48
+ process.exit(1);
49
+ }
50
+ });
51
+
52
+ memCmd
53
+ .command('remove <userId> <groupId>')
54
+ .description('Remove user from group')
55
+ .action(async(userId, groupId) => {
56
+ try {
57
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
58
+ const res = await removeUserFromGroup(
59
+ controllerUrl,
60
+ authConfig,
61
+ requireNonEmpty(userId, 'userId'),
62
+ requireNonEmpty(groupId, 'groupId')
63
+ );
64
+ throwIfApiFailed(res);
65
+ logger.log(formatSuccessLine(`Membership removed: ${userId} → ${groupId}`));
66
+ } catch (e) {
67
+ logger.error(formatBlockingError(e.message));
68
+ process.exit(1);
69
+ }
70
+ });
71
+ }
72
+
73
+ /**
74
+ * @param {import('commander').Command} roleCmd
75
+ */
76
+ function setupIdentityRoleCommands(roleCmd) {
77
+ roleCmd
78
+ .command('list')
79
+ .description('List roles and group mappings for an environment')
80
+ .requiredOption('-e, --env <key>', 'Environment key (dev, tst, pro)')
81
+ .option('--json', 'JSON output')
82
+ .action(async(options) => {
83
+ 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('');
99
+ } catch (e) {
100
+ logger.error(formatBlockingError(e.message));
101
+ process.exit(1);
102
+ }
103
+ });
104
+
105
+ roleCmd
106
+ .command('set-groups <roleValue>')
107
+ .description('Map role to groups in an environment')
108
+ .requiredOption('-e, --env <key>', 'Environment key')
109
+ .requiredOption('--groups <names>', 'Comma-separated group names')
110
+ .action(async(roleValue, options) => {
111
+ try {
112
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
113
+ const envKey = await resolveEnvKey(options);
114
+ const groups = parseGroupsList(options.groups);
115
+ const res = await updateRoleGroups(
116
+ controllerUrl,
117
+ envKey,
118
+ requireNonEmpty(roleValue, 'roleValue'),
119
+ authConfig,
120
+ groups
121
+ );
122
+ throwIfApiFailed(res);
123
+ logger.log(formatSuccessLine(`Role ${roleValue} mapped to ${groups.length} group(s) in ${envKey}`));
124
+ } catch (e) {
125
+ logger.error(formatBlockingError(e.message));
126
+ process.exit(1);
127
+ }
128
+ });
129
+ }
130
+
131
+ /**
132
+ * @param {import('commander').Command} cacheCmd
133
+ */
134
+ function setupIdentityCacheCommands(cacheCmd) {
135
+ cacheCmd
136
+ .command('clear')
137
+ .description('Clear all controller auth/RBAC cache entries')
138
+ .action(async() => {
139
+ try {
140
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
141
+ const res = await clearAuthCache(controllerUrl, authConfig);
142
+ throwIfApiFailed(res);
143
+ logger.log(formatSuccessLine('Controller cache cleared'));
144
+ } catch (e) {
145
+ logger.error(formatBlockingError(e.message));
146
+ process.exit(1);
147
+ }
148
+ });
149
+
150
+ cacheCmd
151
+ .command('invalidate')
152
+ .description('Invalidate controller cache entries by pattern')
153
+ .requiredOption('--pattern <pattern>', 'Cache key pattern (e.g. permissions:*)')
154
+ .action(async(options) => {
155
+ try {
156
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
157
+ const res = await invalidateAuthCache(
158
+ controllerUrl,
159
+ authConfig,
160
+ requireNonEmpty(options.pattern, 'pattern')
161
+ );
162
+ throwIfApiFailed(res);
163
+ logger.log(formatSuccessLine(`Cache invalidated for pattern: ${options.pattern}`));
164
+ } catch (e) {
165
+ logger.error(formatBlockingError(e.message));
166
+ process.exit(1);
167
+ }
168
+ });
169
+ }
170
+
171
+ module.exports = {
172
+ setupIdentityMembershipCommands,
173
+ setupIdentityRoleCommands,
174
+ setupIdentityCacheCommands
175
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview Shared auth/helpers for identity CLI commands
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { resolveControllerUrl } = require('../utils/controller-url');
10
+ const { normalizeControllerUrl, resolveEnvironment } = require('../core/config');
11
+ const { getOrRefreshDeviceToken } = require('../utils/token-manager');
12
+ const {
13
+ unwrapControllerData,
14
+ throwIfApiFailed,
15
+ assertSyncStatsOk,
16
+ apiErrorMessage
17
+ } = require('../identity/identity-apply-core');
18
+
19
+ /**
20
+ * @returns {Promise<{ controllerUrl: string, authConfig: Object }>}
21
+ */
22
+ async function resolveControllerAndAuth() {
23
+ const controllerUrl = await resolveControllerUrl();
24
+ if (!controllerUrl) {
25
+ throw new Error('Controller URL is required. Run "aifabrix login" first.');
26
+ }
27
+ const normalized = normalizeControllerUrl(controllerUrl);
28
+ const deviceToken = await getOrRefreshDeviceToken(normalized);
29
+ if (!deviceToken || !deviceToken.token) {
30
+ throw new Error(
31
+ `Not authenticated for controller: ${controllerUrl}. Run "aifabrix login" and try again.`
32
+ );
33
+ }
34
+ return {
35
+ controllerUrl: deviceToken.controller || normalized,
36
+ authConfig: { type: 'bearer', token: deviceToken.token }
37
+ };
38
+ }
39
+
40
+ /**
41
+ * @param {Object} [options]
42
+ * @returns {Promise<string>}
43
+ */
44
+ async function resolveEnvKey(options = {}) {
45
+ const fromFlag = options.env || options.environment;
46
+ if (fromFlag) {
47
+ return String(fromFlag).trim();
48
+ }
49
+ const env = await resolveEnvironment();
50
+ return env || 'dev';
51
+ }
52
+
53
+ /**
54
+ * @param {string} raw
55
+ * @param {string} label
56
+ * @returns {string}
57
+ */
58
+ function requireNonEmpty(raw, label) {
59
+ const s = String(raw || '').trim();
60
+ if (!s) {
61
+ throw new Error(`${label} is required.`);
62
+ }
63
+ return s;
64
+ }
65
+
66
+ /**
67
+ * @param {string} groupsArg
68
+ * @returns {string[]}
69
+ */
70
+ function parseGroupsList(groupsArg) {
71
+ const raw = String(groupsArg || '').trim();
72
+ if (!raw) {
73
+ throw new Error('--groups is required (comma-separated group names).');
74
+ }
75
+ const parts = raw
76
+ .split(',')
77
+ .map((g) => g.trim())
78
+ .filter(Boolean);
79
+ if (parts.length === 0) {
80
+ throw new Error('At least one group name is required in --groups.');
81
+ }
82
+ return parts;
83
+ }
84
+
85
+ module.exports = {
86
+ resolveControllerAndAuth,
87
+ resolveEnvKey,
88
+ requireNonEmpty,
89
+ parseGroupsList,
90
+ unwrapControllerData,
91
+ throwIfApiFailed,
92
+ assertSyncStatsOk,
93
+ apiErrorMessage
94
+ };
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @fileoverview Identity user subcommands
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const chalk = require('chalk');
10
+ const logger = require('../utils/logger');
11
+ const { formatBlockingError, formatSuccessLine, headerKeyValue } = require('../utils/cli-layout-chalk');
12
+ const {
13
+ resolveControllerAndAuth,
14
+ requireNonEmpty,
15
+ unwrapControllerData,
16
+ throwIfApiFailed
17
+ } = require('./identity-shared');
18
+ const {
19
+ listUsers,
20
+ getUser,
21
+ createUser,
22
+ listUserGroups
23
+ } = require('../api/users.api');
24
+
25
+ function printJson(data) {
26
+ logger.log(JSON.stringify(data, null, 2));
27
+ }
28
+
29
+ function registerUserCreate(userCmd) {
30
+ userCmd
31
+ .command('create')
32
+ .description('Create a user on the controller')
33
+ .requiredOption('--email <email>', 'User email')
34
+ .option('--first-name <name>', 'First name')
35
+ .option('--last-name <name>', 'Last name')
36
+ .option('--display-name <name>', 'Display name')
37
+ .option('--username <name>', 'Username')
38
+ .action(async(options) => {
39
+ try {
40
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
41
+ const res = await createUser(controllerUrl, authConfig, {
42
+ email: requireNonEmpty(options.email, 'email'),
43
+ firstName: options.firstName,
44
+ lastName: options.lastName,
45
+ displayName: options.displayName,
46
+ username: options.username,
47
+ status: 'active'
48
+ });
49
+ throwIfApiFailed(res);
50
+ const row = unwrapControllerData(res);
51
+ logger.log(formatSuccessLine(`User created: ${row.id} (${row.email})`));
52
+ } catch (e) {
53
+ logger.error(formatBlockingError(e.message));
54
+ process.exit(1);
55
+ }
56
+ });
57
+ }
58
+
59
+ function registerUserList(userCmd) {
60
+ userCmd
61
+ .command('list')
62
+ .description('List users')
63
+ .option('--page <n>', 'Page', (v) => parseInt(v, 10))
64
+ .option('--page-size <n>', 'Page size', (v) => parseInt(v, 10))
65
+ .option('--search <text>', 'Search users')
66
+ .option('--json', 'JSON output')
67
+ .action(async(options) => {
68
+ try {
69
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
70
+ const res = await listUsers(controllerUrl, authConfig, {
71
+ page: options.page,
72
+ pageSize: options.pageSize,
73
+ search: options.search
74
+ });
75
+ throwIfApiFailed(res);
76
+ const data = unwrapControllerData(res);
77
+ if (options.json) {
78
+ printJson(data);
79
+ return;
80
+ }
81
+ const items = Array.isArray(data) ? data : data?.data || [];
82
+ logger.log(chalk.bold(`\nUsers (${items.length} shown):\n`));
83
+ items.forEach((u) => logger.log(` ${u.id} ${u.email}`));
84
+ logger.log('');
85
+ } catch (e) {
86
+ logger.error(formatBlockingError(e.message));
87
+ process.exit(1);
88
+ }
89
+ });
90
+ }
91
+
92
+ function registerUserGet(userCmd) {
93
+ userCmd
94
+ .command('get <idOrEmail>')
95
+ .description('Get user by id or email')
96
+ .option('--json', 'JSON output')
97
+ .action(async(idOrEmail, options) => {
98
+ try {
99
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
100
+ const res = await getUser(controllerUrl, authConfig, requireNonEmpty(idOrEmail, 'idOrEmail'));
101
+ throwIfApiFailed(res);
102
+ const row = unwrapControllerData(res);
103
+ if (options.json) {
104
+ printJson(row);
105
+ return;
106
+ }
107
+ logger.log(headerKeyValue('Id:', row.id));
108
+ logger.log(headerKeyValue('Email:', row.email));
109
+ logger.log('');
110
+ } catch (e) {
111
+ logger.error(formatBlockingError(e.message));
112
+ process.exit(1);
113
+ }
114
+ });
115
+ }
116
+
117
+ function registerUserGroups(userCmd) {
118
+ userCmd
119
+ .command('groups <idOrEmail>')
120
+ .description('List groups for a user')
121
+ .option('--json', 'JSON output')
122
+ .action(async(idOrEmail, options) => {
123
+ try {
124
+ const { controllerUrl, authConfig } = await resolveControllerAndAuth();
125
+ const res = await listUserGroups(
126
+ controllerUrl,
127
+ authConfig,
128
+ requireNonEmpty(idOrEmail, 'idOrEmail')
129
+ );
130
+ throwIfApiFailed(res);
131
+ const data = unwrapControllerData(res);
132
+ if (options.json) {
133
+ printJson(data);
134
+ return;
135
+ }
136
+ const items = Array.isArray(data) ? data : data?.data || [];
137
+ items.forEach((g) => logger.log(` ${g.id || g.groupId} ${g.name || ''}`));
138
+ logger.log('');
139
+ } catch (e) {
140
+ logger.error(formatBlockingError(e.message));
141
+ process.exit(1);
142
+ }
143
+ });
144
+ }
145
+
146
+ /**
147
+ * @param {import('commander').Command} userCmd
148
+ */
149
+ function setupIdentityUserCommands(userCmd) {
150
+ registerUserCreate(userCmd);
151
+ registerUserList(userCmd);
152
+ registerUserGet(userCmd);
153
+ registerUserGroups(userCmd);
154
+ }
155
+
156
+ module.exports = {
157
+ setupIdentityUserCommands
158
+ };