@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,244 @@
1
+ /**
2
+ * @fileoverview Phased execution for identity apply
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { getGroup, createGroup } = require('../api/groups.api');
10
+ const { createUser } = require('../api/users.api');
11
+ const { addUserToGroup } = require('../api/user-groups.api');
12
+ const { clearAuthCache } = require('../api/auth-cache.api');
13
+ const { fullSyncToDataplane } = require('../api/dataplane-sync.api');
14
+ const {
15
+ unwrapControllerData,
16
+ throwIfApiFailed,
17
+ assertSyncStatsOk,
18
+ apiErrorMessage,
19
+ findExistingUser
20
+ } = require('./identity-apply-core');
21
+
22
+ /**
23
+ * @param {string} msg
24
+ * @returns {boolean}
25
+ */
26
+ function isAlreadyMemberError(msg) {
27
+ return /already a member|409|Conflict/i.test(String(msg || ''));
28
+ }
29
+
30
+ /**
31
+ * @async
32
+ * @param {string} controllerUrl
33
+ * @param {Object} authConfig
34
+ * @param {{ name: string, displayName: string }} spec
35
+ * @param {boolean} dryRun
36
+ * @returns {Promise<{ id: string, created: boolean }>}
37
+ */
38
+ async function ensureGroup(controllerUrl, authConfig, spec, dryRun) {
39
+ const name = spec.name;
40
+ const existingRes = await getGroup(controllerUrl, authConfig, name);
41
+ const existing = unwrapControllerData(existingRes);
42
+ if (existing && existing.id) {
43
+ return { id: existing.id, created: false };
44
+ }
45
+ if (dryRun) {
46
+ return { id: `(dry-run:${name})`, created: true };
47
+ }
48
+ const createdRes = await createGroup(controllerUrl, authConfig, {
49
+ name,
50
+ displayName: spec.displayName || name
51
+ });
52
+ throwIfApiFailed(createdRes);
53
+ const created = unwrapControllerData(createdRes);
54
+ return { id: created.id, created: true };
55
+ }
56
+
57
+ /**
58
+ * @async
59
+ * @param {string} controllerUrl
60
+ * @param {Object} authConfig
61
+ * @param {Object} userSpec
62
+ * @param {boolean} dryRun
63
+ * @returns {Promise<{ id: string, created: boolean }>}
64
+ */
65
+ async function ensureUser(controllerUrl, authConfig, userSpec, dryRun) {
66
+ const existing = await findExistingUser(controllerUrl, authConfig, userSpec);
67
+ if (existing && existing.id) {
68
+ return { id: existing.id, created: false };
69
+ }
70
+ if (dryRun) {
71
+ return { id: `(dry-run:${userSpec.email})`, created: true };
72
+ }
73
+ const body = {
74
+ email: userSpec.email,
75
+ firstName: userSpec.firstName,
76
+ lastName: userSpec.lastName,
77
+ displayName: userSpec.displayName,
78
+ username: userSpec.username,
79
+ status: 'active'
80
+ };
81
+ Object.keys(body).forEach((k) => {
82
+ if (body[k] === undefined || body[k] === '') {
83
+ delete body[k];
84
+ }
85
+ });
86
+ const createdRes = await createUser(controllerUrl, authConfig, body);
87
+ throwIfApiFailed(createdRes);
88
+ const created = unwrapControllerData(createdRes);
89
+ return { id: created.id, created: true };
90
+ }
91
+
92
+ /**
93
+ * @async
94
+ * @param {string} controllerUrl
95
+ * @param {Object} authConfig
96
+ * @param {Map} groups
97
+ * @param {boolean} dryRun
98
+ * @returns {Promise<{ map: Map<string, string>, created: number, processed: number }>}
99
+ */
100
+ async function applyGroupsPhase(controllerUrl, authConfig, groups, dryRun) {
101
+ const map = new Map();
102
+ let created = 0;
103
+ let processed = 0;
104
+ for (const spec of groups.values()) {
105
+ const out = await ensureGroup(controllerUrl, authConfig, spec, dryRun);
106
+ map.set(spec.name, out.id);
107
+ processed += 1;
108
+ if (out.created) {
109
+ created += 1;
110
+ }
111
+ }
112
+ return { map, created, processed };
113
+ }
114
+
115
+ /**
116
+ * @async
117
+ * @param {string} controllerUrl
118
+ * @param {Object} authConfig
119
+ * @param {Map} users
120
+ * @param {boolean} dryRun
121
+ * @returns {Promise<{ map: Map<string, string>, created: number, processed: number }>}
122
+ */
123
+ async function applyUsersPhase(controllerUrl, authConfig, users, dryRun) {
124
+ const map = new Map();
125
+ let created = 0;
126
+ let processed = 0;
127
+ for (const [userKey, userSpec] of users.entries()) {
128
+ const out = await ensureUser(controllerUrl, authConfig, userSpec, dryRun);
129
+ map.set(userKey, out.id);
130
+ processed += 1;
131
+ if (out.created) {
132
+ created += 1;
133
+ }
134
+ }
135
+ return { map, created, processed };
136
+ }
137
+
138
+ /**
139
+ * @async
140
+ * @param {string} controllerUrl
141
+ * @param {Object} authConfig
142
+ * @param {Object[]} memberships
143
+ * @param {Map<string, string>} userIdByKey
144
+ * @param {Map<string, string>} groupIdByName
145
+ * @param {boolean} dryRun
146
+ * @returns {Promise<{ created: number, processed: number, skipped: number }>}
147
+ */
148
+ /**
149
+ * @async
150
+ * @param {string} controllerUrl
151
+ * @param {Object} authConfig
152
+ * @param {Object} m
153
+ * @param {Map<string, string>} userIdByKey
154
+ * @param {Map<string, string>} groupIdByName
155
+ * @param {boolean} dryRun
156
+ * @returns {Promise<'created'|'skipped'|'dry'>}
157
+ */
158
+ async function applyOneMembership(controllerUrl, authConfig, m, userIdByKey, groupIdByName, dryRun) {
159
+ const userId = userIdByKey.get(m.userKey);
160
+ const groupId = groupIdByName.get(m.groupName);
161
+ if (!userId || !groupId) {
162
+ return 'skipped';
163
+ }
164
+ if (dryRun) {
165
+ return 'dry';
166
+ }
167
+ const res = await addUserToGroup(controllerUrl, authConfig, userId, groupId, {});
168
+ if (res && res.success !== false) {
169
+ return 'created';
170
+ }
171
+ if (isAlreadyMemberError(apiErrorMessage(res))) {
172
+ return 'skipped';
173
+ }
174
+ throw new Error(apiErrorMessage(res));
175
+ }
176
+
177
+ async function applyMembershipsPhase(
178
+ controllerUrl,
179
+ authConfig,
180
+ memberships,
181
+ userIdByKey,
182
+ groupIdByName,
183
+ dryRun
184
+ ) {
185
+ let created = 0;
186
+ let processed = 0;
187
+ let skipped = 0;
188
+ for (const m of memberships) {
189
+ const userId = userIdByKey.get(m.userKey);
190
+ const groupId = groupIdByName.get(m.groupName);
191
+ if (!userId || !groupId) {
192
+ continue;
193
+ }
194
+ processed += 1;
195
+ const outcome = await applyOneMembership(
196
+ controllerUrl,
197
+ authConfig,
198
+ m,
199
+ userIdByKey,
200
+ groupIdByName,
201
+ dryRun
202
+ );
203
+ if (outcome === 'created' || outcome === 'dry') {
204
+ created += 1;
205
+ } else if (outcome === 'skipped') {
206
+ skipped += 1;
207
+ }
208
+ }
209
+ return { created, processed, skipped };
210
+ }
211
+
212
+ /**
213
+ * @async
214
+ * @param {string} controllerUrl
215
+ * @param {Object} authConfig
216
+ * @param {Object} opts
217
+ * @returns {Promise<Object|null>}
218
+ */
219
+ async function maybePurgeAndSync(controllerUrl, authConfig, opts) {
220
+ if (opts.dryRun) {
221
+ return null;
222
+ }
223
+ if (opts.purgeCache) {
224
+ const cacheRes = await clearAuthCache(controllerUrl, authConfig);
225
+ throwIfApiFailed(cacheRes);
226
+ }
227
+ if (!opts.sync || !opts.envKey) {
228
+ return null;
229
+ }
230
+ const syncRes = await fullSyncToDataplane(controllerUrl, authConfig, opts.envKey);
231
+ throwIfApiFailed(syncRes);
232
+ const stats = unwrapControllerData(syncRes);
233
+ assertSyncStatsOk(stats, { allowEmptySync: opts.allowEmptySync });
234
+ return stats;
235
+ }
236
+
237
+ module.exports = {
238
+ ensureGroup,
239
+ ensureUser,
240
+ applyGroupsPhase,
241
+ applyUsersPhase,
242
+ applyMembershipsPhase,
243
+ maybePurgeAndSync
244
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @fileoverview Bulk identity apply (CSV) against miso-controller
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { parseUsersCsvFile, buildApplyPlanFromRows } = require('./identity-csv-parser');
10
+ const {
11
+ applyGroupsPhase,
12
+ applyUsersPhase,
13
+ applyMembershipsPhase,
14
+ maybePurgeAndSync
15
+ } = require('./identity-apply-phases');
16
+
17
+ const {
18
+ unwrapControllerData,
19
+ apiErrorMessage,
20
+ throwIfApiFailed,
21
+ findExistingUser
22
+ } = require('./identity-apply-core');
23
+
24
+ /**
25
+ * @async
26
+ * @param {string} controllerUrl
27
+ * @param {Object} authConfig
28
+ * @param {Object} options
29
+ * @returns {Promise<Object>}
30
+ */
31
+ async function runIdentityApply(controllerUrl, authConfig, options) {
32
+ const { rows } = parseUsersCsvFile(options.filePath, options.filterPrefix);
33
+ if (!rows.length) {
34
+ throw new Error('No data rows to apply after parsing CSV');
35
+ }
36
+ const plan = buildApplyPlanFromRows(rows);
37
+ const dryRun = Boolean(options.dryRun);
38
+
39
+ const groupPhase = await applyGroupsPhase(controllerUrl, authConfig, plan.groups, dryRun);
40
+ const userPhase = await applyUsersPhase(controllerUrl, authConfig, plan.users, dryRun);
41
+ const memPhase = await applyMembershipsPhase(
42
+ controllerUrl,
43
+ authConfig,
44
+ plan.memberships,
45
+ userPhase.map,
46
+ groupPhase.map,
47
+ dryRun
48
+ );
49
+
50
+ const syncStats = await maybePurgeAndSync(controllerUrl, authConfig, {
51
+ dryRun,
52
+ purgeCache: Boolean(options.purgeCache),
53
+ sync: Boolean(options.sync),
54
+ envKey: options.envKey,
55
+ allowEmptySync: Boolean(options.allowEmptySync)
56
+ });
57
+
58
+ return {
59
+ groupsProcessed: groupPhase.processed,
60
+ groupsCreated: groupPhase.created,
61
+ usersProcessed: userPhase.processed,
62
+ usersCreated: userPhase.created,
63
+ membershipsProcessed: memPhase.processed,
64
+ membershipsCreated: memPhase.created,
65
+ membershipsSkipped: memPhase.skipped,
66
+ dryRun,
67
+ syncStats
68
+ };
69
+ }
70
+
71
+ module.exports = {
72
+ unwrapControllerData,
73
+ apiErrorMessage,
74
+ throwIfApiFailed,
75
+ runIdentityApply,
76
+ findExistingUser
77
+ };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @fileoverview Parse controller users.csv-style files for identity apply
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { readFileSync } = require('../internal/fs-real-sync');
10
+
11
+ /**
12
+ * Parse one CSV line respecting double-quoted fields.
13
+ * @param {string} line
14
+ * @returns {string[]}
15
+ */
16
+ function parseCsvLine(line) {
17
+ const out = [];
18
+ let cur = '';
19
+ let inQuotes = false;
20
+ for (let i = 0; i < line.length; i += 1) {
21
+ const ch = line[i];
22
+ if (ch === '"') {
23
+ if (inQuotes && line[i + 1] === '"') {
24
+ cur += '"';
25
+ i += 1;
26
+ } else {
27
+ inQuotes = !inQuotes;
28
+ }
29
+ } else if (ch === ',' && !inQuotes) {
30
+ out.push(cur.trim());
31
+ cur = '';
32
+ } else {
33
+ cur += ch;
34
+ }
35
+ }
36
+ out.push(cur.trim());
37
+ return out;
38
+ }
39
+
40
+ /**
41
+ * @param {string} filePath
42
+ * @param {string} [filterPrefix]
43
+ * @returns {{ headers: string[], rows: Object[] }}
44
+ */
45
+ function parseUsersCsvFile(filePath, filterPrefix = '') {
46
+ const raw = readFileSync(filePath, 'utf8');
47
+ const lines = raw.split(/\r?\n/).filter((l) => l.trim().length > 0);
48
+ if (lines.length < 2) {
49
+ throw new Error('CSV must include a header row and at least one data row');
50
+ }
51
+ const headers = parseCsvLine(lines[0]);
52
+ const prefix = String(filterPrefix || '').trim();
53
+ const rows = [];
54
+ for (let i = 1; i < lines.length; i += 1) {
55
+ const cells = parseCsvLine(lines[i]);
56
+ const row = {};
57
+ headers.forEach((h, idx) => {
58
+ row[h] = cells[idx] !== undefined ? cells[idx] : '';
59
+ });
60
+ const id = String(row.Id || row.id || '').trim();
61
+ if (prefix && id && !id.startsWith(prefix)) {
62
+ continue;
63
+ }
64
+ rows.push(row);
65
+ }
66
+ return { headers, rows };
67
+ }
68
+
69
+ /**
70
+ * Build unique entities from CSV rows (users.csv shape).
71
+ * @param {Object[]} rows
72
+ * @returns {{ groups: Map<string, { name: string, displayName: string }>, users: Map<string, Object>, memberships: { userKey: string, groupName: string }[] }}
73
+ */
74
+ // eslint-disable-next-line complexity -- row shape varies; logic is linear per field
75
+ function buildApplyPlanFromRows(rows) {
76
+ const groups = new Map();
77
+ const users = new Map();
78
+ const memberships = [];
79
+
80
+ for (const row of rows) {
81
+ const csvId = String(row.Id || row.id || '').trim();
82
+ const email = String(row.Email || row.email || '').trim();
83
+ if (!email) {
84
+ continue;
85
+ }
86
+ const userKey = csvId || email;
87
+ const groupName = String(row.GroupId || row.groupId || '').trim();
88
+ const groupDisplay = String(row.GroupName || row.groupName || groupName).trim();
89
+ if (groupName) {
90
+ if (!groups.has(groupName)) {
91
+ groups.set(groupName, { name: groupName, displayName: groupDisplay || groupName });
92
+ }
93
+ memberships.push({ userKey, groupName });
94
+ }
95
+ if (!users.has(userKey)) {
96
+ users.set(userKey, {
97
+ csvId,
98
+ email,
99
+ firstName: String(row.FirstName || row.firstName || '').trim() || undefined,
100
+ lastName: String(row.LastName || row.lastName || '').trim() || undefined,
101
+ displayName: String(row.DisplayName || row.displayName || '').trim() || undefined,
102
+ username: String(row.UserPrincipalName || row.userPrincipalName || row.username || '').trim() || undefined
103
+ });
104
+ }
105
+ }
106
+
107
+ return { groups, users, memberships };
108
+ }
109
+
110
+ module.exports = {
111
+ parseCsvLine,
112
+ parseUsersCsvFile,
113
+ buildApplyPlanFromRows
114
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Write admin-secrets.env from setup password bundles.
3
+ *
4
+ * @fileoverview Apply dev/pro bundles to admin-secrets.env
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fsRealSync = require('../internal/fs-real-sync');
12
+ const logger = require('../utils/logger');
13
+ const { applyPasswordBundleToAdminObj } = require('../core/admin-secrets-bundle');
14
+
15
+ const DEFAULT_ADMIN_OBJ = {
16
+ REDIS_HOST: 'local:redis:6379:0:',
17
+ REDIS_COMMANDER_USER: 'admin'
18
+ };
19
+
20
+ /**
21
+ * @param {() => object} getCoreSecrets
22
+ * @param {string} adminSecretsPath
23
+ * @param {Object.<string, string>} merged
24
+ * @param {string|null} logMessage
25
+ */
26
+ async function writeAdminSecretsFile(getCoreSecrets, adminSecretsPath, merged, logMessage) {
27
+ const { assertWritableSecretsPathForTests } = require('../utils/aifabrix-test-runtime-guard');
28
+ assertWritableSecretsPathForTests(adminSecretsPath);
29
+ const content = await getCoreSecrets().formatAdminSecretsContent(merged);
30
+ fsRealSync.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
31
+ if (logMessage) {
32
+ logger.log(logMessage);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @param {Object} ctx
38
+ * @param {() => object} ctx.getCoreSecrets
39
+ * @param {(bundle: object) => Promise<void>} [ctx.syncAdminKvFromPasswordBundle]
40
+ * @param {(bundle: object) => Promise<void>} [ctx.syncRequiredKvFromPasswordBundle]
41
+ * @param {boolean} [ctx.syncAdminKv]
42
+ * @param {(dir: string) => void} ctx.logVolumeResetHint
43
+ * @param {string} ctx.infraDirForHint
44
+ * @param {string} ctx.adminSecretsPath
45
+ * @param {Object} ctx.adminObj
46
+ * @param {Object} ctx.bundle
47
+ * @param {string} [ctx.email]
48
+ */
49
+ async function applyAdminSecretsFromBundle(ctx) {
50
+ const {
51
+ getCoreSecrets,
52
+ syncAdminKvFromPasswordBundle,
53
+ syncRequiredKvFromPasswordBundle,
54
+ syncAdminKv,
55
+ logVolumeResetHint,
56
+ infraDirForHint,
57
+ adminSecretsPath,
58
+ adminObj,
59
+ bundle,
60
+ email
61
+ } = ctx;
62
+ let merged = applyPasswordBundleToAdminObj({ ...DEFAULT_ADMIN_OBJ, ...adminObj }, bundle);
63
+ if (email && String(email).trim()) {
64
+ merged = { ...merged, PGADMIN_DEFAULT_EMAIL: String(email).trim() };
65
+ }
66
+ await writeAdminSecretsFile(
67
+ getCoreSecrets,
68
+ adminSecretsPath,
69
+ merged,
70
+ 'Updated admin credentials in admin-secrets.env.'
71
+ );
72
+ if (syncAdminKv && typeof syncAdminKvFromPasswordBundle === 'function') {
73
+ await syncAdminKvFromPasswordBundle(bundle);
74
+ } else if (typeof syncRequiredKvFromPasswordBundle === 'function') {
75
+ await syncRequiredKvFromPasswordBundle(bundle);
76
+ }
77
+ logVolumeResetHint(infraDirForHint);
78
+ }
79
+
80
+ module.exports = {
81
+ DEFAULT_ADMIN_OBJ,
82
+ writeAdminSecretsFile,
83
+ applyAdminSecretsFromBundle
84
+ };
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Ensure `admin-secrets.env` exists and reflects CLI / setup password bundles.
3
+ *
4
+ * @fileoverview ensureAdminSecrets for up-infra and setup
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fsRealSync = require('../internal/fs-real-sync');
13
+ const adminSecrets = require('../core/admin-secrets');
14
+ const logger = require('../utils/logger');
15
+ const paths = require('../utils/paths');
16
+ const {
17
+ loadAdminMergedDefaultsForInfra,
18
+ resolveAdminPasswordAndEmailCli,
19
+ computeAdminSecretsBackfillFlags,
20
+ resolvePasswordForAdminFile
21
+ } = require('./admin-secrets-resolve');
22
+ const { syncAdminKvFromPasswordBundle, syncRequiredKvFromPasswordBundle } = require('./admin-secrets-sync');
23
+ const { applyAdminSecretsFromBundle: applyAdminSecretsFromBundleImpl } = require('./admin-secrets-apply');
24
+
25
+ /**
26
+ * @param {() => object} getCoreSecrets
27
+ * @param {(devId: number|string) => string} getInfraDirName
28
+ * @param {(infraDir: string) => void} logVolumeResetHint
29
+ * @param {Function} applyAdminSecretsUpdate
30
+ * @param {Object} [options]
31
+ * @returns {Promise<string>}
32
+ */
33
+ async function ensureAdminSecrets(
34
+ getCoreSecrets,
35
+ getInfraDirName,
36
+ logVolumeResetHint,
37
+ applyAdminSecretsUpdate,
38
+ options = {}
39
+ ) {
40
+ const { passwordBundleFromCliOptions } = require('../core/admin-secrets-bundle');
41
+ const bundle = options.passwordBundle || passwordBundleFromCliOptions(options);
42
+ const mergedDefaults = loadAdminMergedDefaultsForInfra(options);
43
+ const { adminPwdOverride, passwordToUse, emailOverride, emailToUse } = resolveAdminPasswordAndEmailCli(
44
+ options,
45
+ mergedDefaults
46
+ );
47
+ const adminSecretsPath = path.join(paths.getAifabrixSystemDir(), 'admin-secrets.env');
48
+
49
+ if (!fsRealSync.existsSync(adminSecretsPath)) {
50
+ logger.log('Generating admin-secrets.env...');
51
+ await getCoreSecrets().generateAdminSecretsEnv(undefined);
52
+ }
53
+
54
+ if (bundle) {
55
+ const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
56
+ await applyAdminSecretsFromBundleImpl({
57
+ getCoreSecrets,
58
+ syncAdminKvFromPasswordBundle,
59
+ syncRequiredKvFromPasswordBundle,
60
+ syncAdminKv: options.syncAdminKv === true,
61
+ logVolumeResetHint,
62
+ infraDirForHint: path.join(paths.getAifabrixSystemDir(), getInfraDirName(0)),
63
+ adminSecretsPath,
64
+ adminObj,
65
+ bundle,
66
+ email: emailToUse
67
+ });
68
+ return adminSecretsPath;
69
+ }
70
+
71
+ const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
72
+ return applyLegacyAdminSecretsUpdate(
73
+ applyAdminSecretsUpdate,
74
+ adminSecretsPath,
75
+ adminObj,
76
+ {
77
+ adminPwdOverride,
78
+ passwordToUse,
79
+ emailOverride,
80
+ emailToUse,
81
+ mergedDefaults,
82
+ syncAdminKv: options.syncAdminKv === true
83
+ }
84
+ );
85
+ }
86
+
87
+ /**
88
+ * @param {Function} applyAdminSecretsUpdate
89
+ * @param {string} adminSecretsPath
90
+ * @param {Object} adminObj
91
+ * @param {Object} ctx
92
+ * @returns {Promise<string>}
93
+ */
94
+ async function applyLegacyAdminSecretsUpdate(
95
+ applyAdminSecretsUpdate,
96
+ adminSecretsPath,
97
+ adminObj,
98
+ ctx
99
+ ) {
100
+ const { needsPasswordBackfill, needsEmailBackfill } = computeAdminSecretsBackfillFlags(adminObj);
101
+ const shouldOverwriteWithAdminPwd = ctx.adminPwdOverride !== null;
102
+ const shouldOverwriteEmail = ctx.emailOverride !== null;
103
+ const updateEmail = shouldOverwriteEmail || needsEmailBackfill;
104
+
105
+ if (!shouldOverwriteWithAdminPwd && !needsPasswordBackfill && !updateEmail) {
106
+ return adminSecretsPath;
107
+ }
108
+
109
+ const passwordForFile = resolvePasswordForAdminFile(
110
+ shouldOverwriteWithAdminPwd,
111
+ needsPasswordBackfill,
112
+ ctx.passwordToUse,
113
+ adminObj,
114
+ ctx.mergedDefaults
115
+ );
116
+
117
+ await applyAdminSecretsUpdate(
118
+ adminSecretsPath,
119
+ adminObj,
120
+ passwordForFile,
121
+ shouldOverwriteWithAdminPwd,
122
+ { updateEmail, emailToUse: ctx.emailToUse },
123
+ ctx.syncAdminKv === true
124
+ );
125
+ return adminSecretsPath;
126
+ }
127
+
128
+ module.exports = {
129
+ ensureAdminSecrets
130
+ };