@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
@@ -41,6 +41,9 @@ function attachConfigExtensions(exportsObj, deps) {
41
41
 
42
42
  const { createScopedResourcesPreferenceFunctions } = require('../utils/config-scoped-resources-preference');
43
43
  Object.assign(exportsObj, createScopedResourcesPreferenceFunctions(getConfig, saveConfig));
44
+
45
+ const { createSecretsPathFunctions } = require('../utils/config-secrets-path');
46
+ Object.assign(exportsObj, createSecretsPathFunctions(getConfig, saveConfig));
44
47
  }
45
48
 
46
49
  module.exports = { attachConfigExtensions };
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Persist dataplane version metadata under `device.<controllerUrl>` in
3
+ * `~/.aifabrix/config.yaml` for the Builder CLI compatibility gate (plan 142.0).
4
+ *
5
+ * Only public dataplane fields are written here. Token / refresh-token entries
6
+ * on the same device entry are preserved untouched.
7
+ *
8
+ * Schema additions (kebab-case, matching `developer-id` style):
9
+ * - `dataplane-version` ← health `version`
10
+ * - `dataplane-min-cli-version` ← health `minBuilderCliVersion` (omitted when not enforced)
11
+ * - `dataplane-checked-at` ← ISO timestamp for cache TTL (default 5 min)
12
+ *
13
+ * @fileoverview Cache dataplane health under existing `device.<controllerUrl>` entry
14
+ * @author AI Fabrix Team
15
+ * @version 2.0.0
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ const { getConfig, saveConfig, normalizeControllerUrl } = require('./config');
21
+
22
+ const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
23
+
24
+ /**
25
+ * Resolve the device entry key matching `controllerUrl`, taking URL
26
+ * normalization into account so callers do not need to worry about trailing
27
+ * slashes or scheme differences.
28
+ * @private
29
+ * @param {Object} deviceMap - `config.device` map
30
+ * @param {string} normalizedUrl
31
+ * @returns {string|null} The key under `config.device` or null when missing
32
+ */
33
+ function findDeviceKey(deviceMap, normalizedUrl) {
34
+ if (!deviceMap || typeof deviceMap !== 'object') return null;
35
+ if (deviceMap[normalizedUrl]) return normalizedUrl;
36
+ for (const storedUrl of Object.keys(deviceMap)) {
37
+ if (normalizeControllerUrl(storedUrl) === normalizedUrl) {
38
+ return storedUrl;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
44
+ /**
45
+ * Read the dataplane cache currently persisted under `device.<controllerUrl>`.
46
+ *
47
+ * @async
48
+ * @param {string} controllerUrl
49
+ * @returns {Promise<{
50
+ * version?: string,
51
+ * minBuilderCliVersion?: string,
52
+ * checkedAt?: string
53
+ * }|null>} The cached snapshot, or null when no entry exists
54
+ * @throws {Error} If controllerUrl is missing
55
+ */
56
+ async function getDeviceDataplaneVersions(controllerUrl) {
57
+ if (!controllerUrl || typeof controllerUrl !== 'string') {
58
+ throw new Error('controllerUrl is required and must be a string');
59
+ }
60
+ const config = await getConfig();
61
+ const normalizedUrl = normalizeControllerUrl(controllerUrl);
62
+ const key = findDeviceKey(config.device, normalizedUrl);
63
+ if (!key) return null;
64
+ const entry = config.device[key];
65
+ if (!entry || typeof entry !== 'object') return null;
66
+ const snapshot = {};
67
+ if (typeof entry['dataplane-version'] === 'string') {
68
+ snapshot.version = entry['dataplane-version'];
69
+ }
70
+ if (typeof entry['dataplane-min-cli-version'] === 'string') {
71
+ snapshot.minBuilderCliVersion = entry['dataplane-min-cli-version'];
72
+ }
73
+ if (typeof entry['dataplane-checked-at'] === 'string') {
74
+ snapshot.checkedAt = entry['dataplane-checked-at'];
75
+ }
76
+ if (Object.keys(snapshot).length === 0) return null;
77
+ return snapshot;
78
+ }
79
+
80
+ /**
81
+ * Apply the `dataplane-version` field to a device entry in-place.
82
+ * @private
83
+ * @param {Object} entry
84
+ * @param {string|null|undefined} version
85
+ */
86
+ function applyDataplaneVersionField(entry, version) {
87
+ if (typeof version === 'string' && version.trim()) {
88
+ entry['dataplane-version'] = version.trim();
89
+ } else if (version === null || version === '') {
90
+ delete entry['dataplane-version'];
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Apply the `dataplane-min-cli-version` field to a device entry in-place.
96
+ * Treat absent / empty as "no enforcement" → clear the cached floor so an
97
+ * older value never lingers after the operator removes the env variable.
98
+ * @private
99
+ * @param {Object} entry
100
+ * @param {string|null|undefined} minCli
101
+ */
102
+ function applyMinCliField(entry, minCli) {
103
+ if (typeof minCli === 'string' && minCli.trim()) {
104
+ entry['dataplane-min-cli-version'] = minCli.trim();
105
+ return;
106
+ }
107
+ if (minCli === null || minCli === '' || minCli === undefined) {
108
+ delete entry['dataplane-min-cli-version'];
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Compute the timestamp persisted as `dataplane-checked-at`.
114
+ * @private
115
+ * @param {string|undefined} checkedAt
116
+ * @returns {string} ISO timestamp
117
+ */
118
+ function resolveCheckedAt(checkedAt) {
119
+ if (typeof checkedAt === 'string' && checkedAt.trim()) {
120
+ return checkedAt.trim();
121
+ }
122
+ return new Date().toISOString();
123
+ }
124
+
125
+ /**
126
+ * Persist dataplane version metadata under the device entry for `controllerUrl`.
127
+ *
128
+ * Token / refresh-token entries are preserved; only the `dataplane-*` keys are
129
+ * written. When `versions.minBuilderCliVersion` is empty/undefined the cache
130
+ * key is removed (dataplane does not enforce a minimum).
131
+ *
132
+ * @async
133
+ * @param {string} controllerUrl - Controller URL (will be normalized)
134
+ * @param {{
135
+ * version?: string,
136
+ * minBuilderCliVersion?: string|null,
137
+ * checkedAt?: string
138
+ * }} versions
139
+ * @returns {Promise<void>}
140
+ * @throws {Error} If controllerUrl is missing
141
+ */
142
+ async function updateDeviceDataplaneVersions(controllerUrl, versions = {}) {
143
+ if (!controllerUrl || typeof controllerUrl !== 'string') {
144
+ throw new Error('controllerUrl is required and must be a string');
145
+ }
146
+ const config = await getConfig();
147
+ if (!config.device || typeof config.device !== 'object') {
148
+ config.device = {};
149
+ }
150
+ const normalizedUrl = normalizeControllerUrl(controllerUrl);
151
+ const key = findDeviceKey(config.device, normalizedUrl) || normalizedUrl;
152
+ if (!config.device[key] || typeof config.device[key] !== 'object') {
153
+ config.device[key] = {};
154
+ }
155
+ const entry = config.device[key];
156
+
157
+ applyDataplaneVersionField(entry, versions.version);
158
+ applyMinCliField(entry, versions.minBuilderCliVersion);
159
+ entry['dataplane-checked-at'] = resolveCheckedAt(versions.checkedAt);
160
+
161
+ await saveConfig(config);
162
+ }
163
+
164
+ /**
165
+ * True when the cached snapshot is older than the configured TTL (or missing).
166
+ *
167
+ * @param {{checkedAt?: string}|null} snapshot
168
+ * @param {number} [ttlMs] - TTL in milliseconds (defaults to 5 minutes)
169
+ * @param {Date|number} [now] - Override "now" for tests
170
+ * @returns {boolean}
171
+ */
172
+ function isCacheStale(snapshot, ttlMs = DEFAULT_CACHE_TTL_MS, now = Date.now()) {
173
+ if (!snapshot || !snapshot.checkedAt) return true;
174
+ const checkedAtMs = Date.parse(snapshot.checkedAt);
175
+ if (Number.isNaN(checkedAtMs)) return true;
176
+ const reference = typeof now === 'number' ? now : now.getTime();
177
+ return reference - checkedAtMs >= ttlMs;
178
+ }
179
+
180
+ module.exports = {
181
+ getDeviceDataplaneVersions,
182
+ updateDeviceDataplaneVersions,
183
+ isCacheStale,
184
+ DEFAULT_CACHE_TTL_MS
185
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `platform-controller` field in `config.yaml` — absolute Miso URL for setup / platform flows.
3
+ *
4
+ * @fileoverview config.yaml platform-controller helpers
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {() => Promise<Object>} getConfig
13
+ * @returns {Promise<string|null>}
14
+ */
15
+ async function getPlatformControllerUrlFromConfig(getConfig) {
16
+ const cfg = await getConfig();
17
+ const raw = cfg && cfg['platform-controller'];
18
+ if (typeof raw === 'string' && raw.trim()) {
19
+ return raw.trim().replace(/\/+$/, '');
20
+ }
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * @param {() => Promise<Object>} getConfig
26
+ * @param {(data: Object) => Promise<void>} saveConfig
27
+ * @param {(url: string) => string} normalizeControllerUrl
28
+ * @param {string} url
29
+ * @returns {Promise<void>}
30
+ */
31
+ async function setPlatformControllerUrlInConfig(getConfig, saveConfig, normalizeControllerUrl, url) {
32
+ if (!url || typeof url !== 'string') {
33
+ throw new Error('Platform controller URL is required and must be a string');
34
+ }
35
+ const normalized = normalizeControllerUrl(url);
36
+ const cfg = await getConfig();
37
+ cfg['platform-controller'] = normalized;
38
+ await saveConfig(cfg);
39
+ }
40
+
41
+ module.exports = {
42
+ getPlatformControllerUrlFromConfig,
43
+ setPlatformControllerUrlInConfig
44
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `setupInstallationProfile` field in `config.yaml` (`dev` | `pro`).
3
+ *
4
+ * @fileoverview Persists setup wizard installation profile
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { normalizeInstallationProfile, INSTALLATION_PROFILE } = require('./setup-installation-profile');
12
+
13
+ /**
14
+ * @param {() => Promise<Object>} getConfig
15
+ * @returns {Promise<'dev'|'pro'>}
16
+ */
17
+ async function getSetupInstallationProfileFromConfig(getConfig) {
18
+ const cfg = await getConfig();
19
+ if (cfg && cfg.setupInstallationProfile) {
20
+ return normalizeInstallationProfile(cfg.setupInstallationProfile);
21
+ }
22
+ return INSTALLATION_PROFILE.DEV;
23
+ }
24
+
25
+ /**
26
+ * @param {() => Promise<Object>} getConfig
27
+ * @param {(data: Object) => Promise<void>} saveConfig
28
+ * @param {unknown} profile
29
+ * @returns {Promise<'dev'|'pro'>}
30
+ */
31
+ async function setSetupInstallationProfileInConfig(getConfig, saveConfig, profile) {
32
+ const normalized = normalizeInstallationProfile(profile);
33
+ const cfg = await getConfig();
34
+ cfg.setupInstallationProfile = normalized;
35
+ await saveConfig(cfg);
36
+ return normalized;
37
+ }
38
+
39
+ module.exports = {
40
+ getSetupInstallationProfileFromConfig,
41
+ setSetupInstallationProfileInConfig
42
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `setupPlatformMode` field in `config.yaml` (`single` | `full`).
3
+ *
4
+ * @fileoverview Persists setup wizard platform mode
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ normalizeSetupPlatformMode,
13
+ SETUP_PLATFORM_MODE
14
+ } = require('./setup-platform-mode');
15
+
16
+ /**
17
+ * @param {() => Promise<Object>} getConfig
18
+ * @returns {Promise<'single'|'full'>}
19
+ */
20
+ async function getSetupPlatformModeFromConfig(getConfig) {
21
+ const cfg = await getConfig();
22
+ if (cfg && cfg.setupPlatformMode) {
23
+ return normalizeSetupPlatformMode(cfg.setupPlatformMode);
24
+ }
25
+ return SETUP_PLATFORM_MODE.SINGLE;
26
+ }
27
+
28
+ /**
29
+ * @param {() => Promise<Object>} getConfig
30
+ * @param {(data: Object) => Promise<void>} saveConfig
31
+ * @param {unknown} mode
32
+ * @returns {Promise<'single'|'full'>}
33
+ */
34
+ async function setSetupPlatformModeInConfig(getConfig, saveConfig, mode) {
35
+ const normalized = normalizeSetupPlatformMode(mode);
36
+ const cfg = await getConfig();
37
+ cfg.setupPlatformMode = normalized;
38
+ await saveConfig(cfg);
39
+ return normalized;
40
+ }
41
+
42
+ module.exports = {
43
+ getSetupPlatformModeFromConfig,
44
+ setSetupPlatformModeInConfig
45
+ };
46
+
@@ -9,14 +9,24 @@
9
9
  */
10
10
 
11
11
  const fs = require('fs').promises;
12
- const path = require('path');
13
12
  const yaml = require('js-yaml');
14
- const os = require('os');
15
13
  const { encryptToken, decryptToken, isTokenEncrypted } = require('../utils/token-encryption');
16
14
  const { ensureSecureFilePermissions, ensureSecureDirPermissions } = require('../utils/secure-file-permissions');
17
15
  const { getRuntimeConfigDir, getRuntimeConfigFile } = require('./config-runtime-paths');
18
16
  const { getAdminEmailFromConfig, setAdminEmailInConfig } = require('./config-admin-email');
17
+ const {
18
+ getSetupInstallationProfileFromConfig,
19
+ setSetupInstallationProfileInConfig
20
+ } = require('./config-setup-installation-profile');
21
+ const {
22
+ getSetupPlatformModeFromConfig,
23
+ setSetupPlatformModeInConfig
24
+ } = require('./config-setup-platform-mode');
19
25
  const { getRegisteredControllerUrlsWithLoader } = require('./config-registered-controller-urls');
26
+ const {
27
+ getPlatformControllerUrlFromConfig,
28
+ setPlatformControllerUrlInConfig
29
+ } = require('./config-platform-controller');
20
30
  const { normalizeControllerUrl, validateAndNormalizeDeveloperId } = require('./config-normalize');
21
31
  // Avoid importing paths.js here to prevent circular dependency; use shared runtime config dir helper.
22
32
  // Config location: AIFABRIX_CONFIG dirname → AIFABRIX_HOME (with ~/.aifabrix fallback when config lives there) → ~/.aifabrix
@@ -34,6 +44,9 @@ function applyConfigDefaults(config) {
34
44
  if (typeof config.environment === 'undefined') {
35
45
  config.environment = 'dev';
36
46
  }
47
+ if (typeof config.setupPlatformMode === 'undefined') {
48
+ config.setupPlatformMode = 'single';
49
+ }
37
50
  // Ensure environments object exists
38
51
  if (typeof config.environments !== 'object' || config.environments === null) {
39
52
  config.environments = {};
@@ -59,6 +72,7 @@ function getDefaultConfig() {
59
72
  return {
60
73
  'developer-id': '0',
61
74
  environment: 'dev',
75
+ setupPlatformMode: 'single',
62
76
  controller: undefined,
63
77
  environments: {},
64
78
  device: {},
@@ -230,6 +244,12 @@ async function setAdminEmail(email) {
230
244
  return setAdminEmailInConfig(getConfig, saveConfig, email);
231
245
  }
232
246
 
247
+ /** @returns {Promise<'dev'|'pro'>} */
248
+ const getSetupInstallationProfile = () => getSetupInstallationProfileFromConfig(getConfig);
249
+ /** @param {unknown} profile @returns {Promise<'dev'|'pro'>} */
250
+ const setSetupInstallationProfile = (profile) =>
251
+ setSetupInstallationProfileInConfig(getConfig, saveConfig, profile);
252
+
233
253
  /**
234
254
  * Whether Traefik is enabled (`traefik: true` in config; infra compose includes the proxy).
235
255
  * @returns {Promise<boolean>}
@@ -259,6 +279,21 @@ async function setCurrentEnvironment(environment) {
259
279
  await saveConfig(config);
260
280
  }
261
281
 
282
+ /**
283
+ * @returns {Promise<'single'|'full'>}
284
+ */
285
+ async function getSetupPlatformMode() {
286
+ return getSetupPlatformModeFromConfig(getConfig);
287
+ }
288
+
289
+ /**
290
+ * @param {unknown} mode
291
+ * @returns {Promise<'single'|'full'>}
292
+ */
293
+ async function setSetupPlatformMode(mode) {
294
+ return setSetupPlatformModeInConfig(getConfig, saveConfig, mode);
295
+ }
296
+
262
297
  /**
263
298
  * Set controller URL in configuration
264
299
  * @async
@@ -392,38 +427,11 @@ async function setSecretsEncryptionKey(key) {
392
427
  */
393
428
  async function ensureSecretsEncryptionKey() {
394
429
  const { ensureSecretsEncryptionKey: run } = require('./ensure-encryption-key');
430
+ const { createSecretsPathFunctions } = require('../utils/config-secrets-path');
431
+ const { getSecretsPath } = createSecretsPathFunctions(getConfig, saveConfig);
395
432
  await run({ getSecretsEncryptionKey, setSecretsEncryptionKey, getSecretsPath });
396
433
  }
397
434
 
398
- /**
399
- * Expand leading ~ to home directory so config paths like ~/.aifabrix/secrets.local.yaml resolve correctly.
400
- * @param {string} filePath - Path that may start with ~ or ~/
401
- * @returns {string} Path with ~ expanded, or unchanged if no leading ~
402
- */
403
- function expandTilde(filePath) {
404
- if (!filePath || typeof filePath !== 'string') return filePath;
405
- if (filePath === '~') return os.homedir();
406
- if (filePath.startsWith('~/') || filePath.startsWith('~' + path.sep)) {
407
- return path.join(os.homedir(), filePath.slice(2));
408
- }
409
- return filePath;
410
- }
411
-
412
- async function getSecretsPath() {
413
- const config = await getConfig();
414
- const raw = config['aifabrix-secrets'] || config['secrets-path'] || null;
415
- return raw ? expandTilde(raw) : null;
416
- }
417
-
418
- async function setSecretsPath(secretsPath) {
419
- if (typeof secretsPath !== 'string') {
420
- throw new Error('Secrets path is required and must be a string');
421
- }
422
- const config = await getConfig();
423
- config['aifabrix-secrets'] = secretsPath.trim() || undefined;
424
- await saveConfig(config);
425
- }
426
-
427
435
  // Create exports object
428
436
  const exportsObj = {
429
437
  getConfig,
@@ -436,6 +444,10 @@ const exportsObj = {
436
444
  getTlsEnabled,
437
445
  getAdminEmail,
438
446
  setAdminEmail,
447
+ getSetupInstallationProfile,
448
+ setSetupInstallationProfile,
449
+ getSetupPlatformMode,
450
+ setSetupPlatformMode,
439
451
  getTraefikEnabled,
440
452
  setCurrentEnvironment,
441
453
  resolveEnvironment,
@@ -446,11 +458,11 @@ const exportsObj = {
446
458
  getSecretsEncryptionKey,
447
459
  setSecretsEncryptionKey,
448
460
  ensureSecretsEncryptionKey,
449
- getSecretsPath,
450
- setSecretsPath,
451
461
  normalizeControllerUrl,
452
462
  setControllerUrl,
453
463
  getControllerUrl,
464
+ getPlatformControllerUrl: () => getPlatformControllerUrlFromConfig(getConfig),
465
+ setPlatformControllerUrl: (url) => setPlatformControllerUrlInConfig(getConfig, saveConfig, normalizeControllerUrl, url),
454
466
  getRegisteredControllerUrls,
455
467
  get CONFIG_DIR() {
456
468
  return getRuntimeConfigDir();
@@ -460,9 +472,7 @@ const exportsObj = {
460
472
  }
461
473
  };
462
474
 
463
- // Add developerId as a property getter for direct access
464
- // After getConfig() or getDeveloperId() is called, config.developerId will be available
465
- // Developer ID: 0 = default infra, > 0 = developer-specific
475
+ // Developer ID: 0 = default infra, > 0 = developer-specific (getter after getConfig loads cache)
466
476
  Object.defineProperty(exportsObj, 'developerId', {
467
477
  get() {
468
478
  return cachedDeveloperId !== null ? cachedDeveloperId : '0'; // Default to "0" if not loaded yet
@@ -20,6 +20,7 @@ const {
20
20
  const { createDefaultSecrets } = require('../utils/secrets-generator');
21
21
  const pathsUtil = require('../utils/paths');
22
22
  const { loadSecrets } = require('./secrets-load');
23
+ const { formatAdminEnvLine } = require('./admin-secrets');
23
24
 
24
25
  /**
25
26
  * Writes admin env key-value pairs to content; encrypts values when encryption key is set.
@@ -34,7 +35,7 @@ async function formatAdminSecretsContent(adminObj) {
34
35
  for (const [k, v] of Object.entries(adminObj)) {
35
36
  const value = (v === null || v === undefined) ? '' : String(v).replace(/\n/g, ' ').trim();
36
37
  const valueToWrite = encryptionKey ? encryptSecret(value, encryptionKey) : value;
37
- lines.push(`${k}=${valueToWrite}`);
38
+ lines.push(formatAdminEnvLine(k, valueToWrite));
38
39
  }
39
40
  return lines.join('\n');
40
41
  }
@@ -76,7 +77,10 @@ function buildLocalAdminSecretsObject(secrets, infraDefaults) {
76
77
  PGADMIN_DEFAULT_PASSWORD: postgresPassword,
77
78
  REDIS_HOST: 'local:redis:6379:0:',
78
79
  REDIS_COMMANDER_USER: 'admin',
79
- REDIS_COMMANDER_PASSWORD: postgresPassword
80
+ REDIS_COMMANDER_PASSWORD: postgresPassword,
81
+ KEYCLOAK_ADMIN_USERNAME: 'admin',
82
+ KEYCLOAK_ADMIN_PASSWORD: postgresPassword,
83
+ PLATFORM_ADMIN_PASSWORD: postgresPassword
80
84
  };
81
85
  }
82
86
 
@@ -204,6 +204,8 @@ function summarizeSecretsStoreForLog(target) {
204
204
  */
205
205
  function logNewSecretValuesWritten(added, target) {
206
206
  if (!Array.isArray(added) || added.length === 0) return;
207
+ const { isSetupQuietOutput } = require('../utils/setup-quiet-output');
208
+ if (isSetupQuietOutput()) return;
207
209
  const where = summarizeSecretsStoreForLog(target);
208
210
  logger.log(
209
211
  formatSuccessLine(
@@ -352,6 +354,8 @@ async function ensureSecretsFromEnvTemplate(envTemplatePathOrContent, options =
352
354
  * @returns {Promise<void>}
353
355
  */
354
356
  async function writeSecretToStoreFile(filePath, key, strValue) {
357
+ const { assertWritableSecretsPathForTests } = require('../utils/aifabrix-test-runtime-guard');
358
+ assertWritableSecretsPathForTests(filePath);
355
359
  const existing = loadExistingSecrets(filePath);
356
360
  const encryptionKey = await config.getSecretsEncryptionKey();
357
361
  let valueToWrite = strValue;