@aifabrix/builder 2.45.5 → 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 (231) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/project-rules.mdc +1 -0
  4. package/README.md +43 -17
  5. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  6. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.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 +41 -136
  12. package/lib/api/api-response-errors.js +124 -0
  13. package/lib/api/governance-scenario-pack.api.js +159 -0
  14. package/lib/api/index.js +43 -3
  15. package/lib/api/lifecycle.api.js +77 -0
  16. package/lib/api/pipeline.api.js +3 -1
  17. package/lib/api/records-bulk.api.js +14 -1
  18. package/lib/api/validation-runner.js +3 -1
  19. package/lib/app/deploy-config.js +4 -1
  20. package/lib/app/down.js +20 -1
  21. package/lib/app/push.js +4 -5
  22. package/lib/app/register.js +7 -4
  23. package/lib/app/run-env-compose.js +84 -30
  24. package/lib/build/index.js +5 -0
  25. package/lib/certification/sync-system-certification.js +13 -6
  26. package/lib/cli/index.js +4 -0
  27. package/lib/cli/infra-guided-auth.js +109 -0
  28. package/lib/cli/infra-guided.js +84 -81
  29. package/lib/cli/setup-app.help.js +0 -31
  30. package/lib/cli/setup-app.js +28 -2
  31. package/lib/cli/setup-app.test-commands.js +5 -57
  32. package/lib/cli/setup-auth.js +25 -0
  33. package/lib/cli/setup-certification-commands.js +147 -0
  34. package/lib/cli/setup-certification.help.js +141 -0
  35. package/lib/cli/setup-environment.js +19 -2
  36. package/lib/cli/setup-external-system-help.js +62 -0
  37. package/lib/cli/setup-external-system.js +25 -3
  38. package/lib/cli/setup-infra.js +9 -1
  39. package/lib/cli/setup-platform.js +6 -1
  40. package/lib/cli/setup-secrets.js +33 -1
  41. package/lib/cli/setup-utility-help-after.js +22 -2
  42. package/lib/cli/setup-utility-repair.js +198 -0
  43. package/lib/cli/setup-utility-resolve.js +40 -4
  44. package/lib/cli/setup-utility-validate.js +197 -0
  45. package/lib/cli/setup-utility.js +4 -239
  46. package/lib/commands/auth-status-client-tokens.js +214 -0
  47. package/lib/commands/auth-status-dataplane-version.js +58 -20
  48. package/lib/commands/auth-status.js +41 -47
  49. package/lib/commands/datasource-test-trust-cli.js +27 -23
  50. package/lib/commands/datasource-unified-test-cli.js +6 -3
  51. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  52. package/lib/commands/datasource.js +7 -7
  53. package/lib/commands/dev-show-display.js +8 -1
  54. package/lib/commands/governance-cli-shared.js +94 -0
  55. package/lib/commands/governance-command-action.js +132 -0
  56. package/lib/commands/governance-download-external.js +34 -0
  57. package/lib/commands/governance-generate-external.js +71 -0
  58. package/lib/commands/governance-upload-external.js +36 -0
  59. package/lib/commands/governance-verify-external.js +64 -0
  60. package/lib/commands/governance.js +144 -0
  61. package/lib/commands/lifecycle-command-action.js +123 -0
  62. package/lib/commands/lifecycle-external.js +268 -0
  63. package/lib/commands/login.js +11 -2
  64. package/lib/commands/repair-auth-apply.js +127 -0
  65. package/lib/commands/repair-changed-files.js +41 -0
  66. package/lib/commands/repair-datasource-keys.js +4 -1
  67. package/lib/commands/repair-datasource-run.js +59 -0
  68. package/lib/commands/repair-datasource-test-payload.js +259 -0
  69. package/lib/commands/repair-datasource.js +1 -51
  70. package/lib/commands/repair-env-template.js +64 -13
  71. package/lib/commands/repair-external-run.js +216 -0
  72. package/lib/commands/repair-openapi-sync.js +23 -0
  73. package/lib/commands/repair-persist.js +4 -0
  74. package/lib/commands/repair-rbac.js +52 -1
  75. package/lib/commands/repair-steps.js +145 -0
  76. package/lib/commands/repair.js +29 -296
  77. package/lib/commands/secrets-get.js +184 -0
  78. package/lib/commands/secrets-remove-all.js +3 -1
  79. package/lib/commands/setup-image-refresh.js +146 -0
  80. package/lib/commands/setup-modes.js +158 -162
  81. package/lib/commands/setup-platform-auth.js +76 -0
  82. package/lib/commands/setup-prompts-admin.js +244 -0
  83. package/lib/commands/setup-prompts-platform-mode.js +67 -0
  84. package/lib/commands/setup-prompts-validators.js +36 -0
  85. package/lib/commands/setup-prompts.js +24 -76
  86. package/lib/commands/setup-run-context.js +45 -0
  87. package/lib/commands/setup-spinners.js +39 -0
  88. package/lib/commands/setup.js +27 -5
  89. package/lib/commands/teardown.js +39 -0
  90. package/lib/commands/test-e2e-external.js +24 -8
  91. package/lib/commands/test-governance-external.js +3 -24
  92. package/lib/commands/test-trust-external.js +20 -21
  93. package/lib/commands/up-common.js +25 -13
  94. package/lib/commands/up-dataplane.js +50 -13
  95. package/lib/commands/upload-publish-context.js +75 -0
  96. package/lib/commands/upload-sidecar-followups.js +89 -0
  97. package/lib/commands/upload.js +90 -108
  98. package/lib/commands/verify-governance-combined-external.js +61 -0
  99. package/lib/commands/verify-governance-command-action.js +64 -0
  100. package/lib/commands/verify-governance-db.js +102 -0
  101. package/lib/commands/verify-governance-external.js +77 -0
  102. package/lib/commands/verify-governance-result-builders.js +71 -0
  103. package/lib/commands/verify-operations-command-action.js +68 -0
  104. package/lib/commands/verify-operations-external.js +268 -0
  105. package/lib/commands/verify-operations-persist.js +75 -0
  106. package/lib/commands/verify-operations-step-plan.js +68 -0
  107. package/lib/commands/verify-operations-steps.js +111 -0
  108. package/lib/commands/verify-trust-command-action.js +70 -0
  109. package/lib/commands/verify-trust-external.js +67 -0
  110. package/lib/commands/wizard-config-normalizer.js +43 -23
  111. package/lib/commands/wizard-core.js +2 -0
  112. package/lib/core/admin-password-generator.js +38 -0
  113. package/lib/core/admin-secrets-bundle.js +111 -0
  114. package/lib/core/admin-secrets-env-overlay.js +62 -0
  115. package/lib/core/admin-secrets.js +32 -4
  116. package/lib/core/config-attach-extensions.js +3 -0
  117. package/lib/core/config-platform-controller.js +44 -0
  118. package/lib/core/config-setup-installation-profile.js +42 -0
  119. package/lib/core/config-setup-platform-mode.js +46 -0
  120. package/lib/core/config.js +46 -36
  121. package/lib/core/secrets-admin-env.js +6 -2
  122. package/lib/core/secrets-ensure.js +4 -0
  123. package/lib/core/secrets-env-content.js +54 -48
  124. package/lib/core/secrets-env-write.js +10 -1
  125. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  126. package/lib/core/setup-installation-profile.js +43 -0
  127. package/lib/core/setup-platform-mode.js +55 -0
  128. package/lib/datasource/agent-trust-debug-log.js +1 -1
  129. package/lib/datasource/agent-trust-run.js +5 -3
  130. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  131. package/lib/datasource/audit-evidence-extract.js +7 -4
  132. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  133. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  134. package/lib/datasource/datasource-load-export-context.js +3 -0
  135. package/lib/datasource/log-cleaner.js +2 -2
  136. package/lib/datasource/log-viewer.js +8 -8
  137. package/lib/datasource/test-e2e.js +4 -1
  138. package/lib/datasource/unified-validation-run-body.js +3 -1
  139. package/lib/datasource/unified-validation-run.js +5 -2
  140. package/lib/deployment/environment-poll-status.js +138 -0
  141. package/lib/deployment/environment.js +42 -34
  142. package/lib/external-system/download.js +21 -20
  143. package/lib/external-system/generator.js +5 -1
  144. package/lib/external-system/integration-auth-context.js +45 -0
  145. package/lib/external-system/test-auth.js +12 -5
  146. package/lib/external-system/test.js +6 -6
  147. package/lib/generator/external-controller-manifest.js +15 -0
  148. package/lib/governance/governance-cli-errors.js +96 -0
  149. package/lib/governance/governance-download-display.js +38 -0
  150. package/lib/governance/governance-run-display.js +215 -25
  151. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  152. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  153. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  154. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  155. package/lib/infrastructure/helpers.js +23 -129
  156. package/lib/infrastructure/index.js +5 -6
  157. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  158. package/lib/lifecycle/certification-progress.js +231 -0
  159. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  160. package/lib/lifecycle/exit-codes.js +24 -0
  161. package/lib/lifecycle/json-envelope.js +25 -0
  162. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  163. package/lib/lifecycle/logger-quiet.js +29 -0
  164. package/lib/lifecycle/open-api-published.js +59 -0
  165. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  166. package/lib/lifecycle/product-model.js +196 -0
  167. package/lib/lifecycle/report-display.js +471 -0
  168. package/lib/lifecycle/scenario-upload.js +191 -0
  169. package/lib/lifecycle/verify-step-progress.js +87 -0
  170. package/lib/parameters/infra-kv-discovery.js +27 -2
  171. package/lib/protection/auth-context.js +10 -4
  172. package/lib/schema/application-schema.json +1 -1
  173. package/lib/schema/datasource-test-run.schema.json +89 -6
  174. package/lib/schema/external-datasource.schema.json +2636 -2622
  175. package/lib/schema/external-system.schema.json +1089 -761
  176. package/lib/schema/infra.parameter.yaml +45 -0
  177. package/lib/schema/type/credential-field-registry.json +103 -97
  178. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  179. package/lib/utils/api.js +55 -31
  180. package/lib/utils/app-relative-path-env.js +112 -0
  181. package/lib/utils/cli-secrets-error-format.js +83 -21
  182. package/lib/utils/cli-utils.js +16 -4
  183. package/lib/utils/compose-generate-docker-compose.js +4 -0
  184. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  185. package/lib/utils/config-secrets-path.js +46 -0
  186. package/lib/utils/controller-url.js +88 -1
  187. package/lib/utils/credential-secrets-env.js +137 -9
  188. package/lib/utils/dataplane-resolver.js +129 -2
  189. package/lib/utils/declarative-url-ports.js +3 -3
  190. package/lib/utils/deployment-auth-mode.js +41 -0
  191. package/lib/utils/developer-volume-id.js +33 -0
  192. package/lib/utils/env-copy.js +14 -9
  193. package/lib/utils/env-kv-orphan-lines.js +43 -0
  194. package/lib/utils/error-formatters/network-errors.js +13 -4
  195. package/lib/utils/external-env-template.js +1 -0
  196. package/lib/utils/external-system-readiness-core.js +10 -0
  197. package/lib/utils/external-system-readiness-display.js +80 -23
  198. package/lib/utils/health-check.js +4 -1
  199. package/lib/utils/help-builder.js +11 -8
  200. package/lib/utils/keycloak-realm-ready.js +101 -0
  201. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  202. package/lib/utils/manifest-source-emit.js +2 -0
  203. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  204. package/lib/utils/platform-controller-url.js +156 -0
  205. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  206. package/lib/utils/postgres-wipe.js +9 -2
  207. package/lib/utils/resolve-env-mode.js +47 -0
  208. package/lib/utils/secrets-generator.js +31 -25
  209. package/lib/utils/secrets-missing-error.js +164 -0
  210. package/lib/utils/service-url-label.js +81 -0
  211. package/lib/utils/setup-quiet-output.js +25 -0
  212. package/lib/utils/template-helpers.js +15 -0
  213. package/lib/utils/token-manager-client-credentials.js +201 -0
  214. package/lib/utils/token-manager-messages.js +8 -0
  215. package/lib/utils/token-manager-refresh.js +12 -2
  216. package/lib/utils/token-manager.js +86 -105
  217. package/lib/utils/upload-pipeline-ui.js +12 -5
  218. package/lib/utils/upload-sync-options.js +42 -0
  219. package/lib/utils/url-declarative-public-base.js +21 -3
  220. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  221. package/lib/utils/validation-poll-ui.js +8 -0
  222. package/lib/utils/validation-run-stage.js +45 -0
  223. package/package.json +1 -1
  224. package/templates/applications/dataplane/application.yaml +2 -1
  225. package/templates/applications/dataplane/env.template +24 -1
  226. package/templates/applications/miso-controller/env.template +16 -0
  227. package/templates/external-system/README.md.hbs +74 -17
  228. package/templates/python/docker-compose.hbs +19 -18
  229. package/templates/typescript/docker-compose.hbs +18 -17
  230. package/lib/commands/test-governance-command-action.js +0 -55
  231. package/lib/commands/test-trust-command-action.js +0 -118
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Session-aware platform controller URL + auth for `aifabrix setup`.
3
+ *
4
+ * @fileoverview Setup platform-controller persistence and login gate
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const config = require('../core/config');
12
+ const upCommon = require('./up-common');
13
+ const { resolvePlatformControllerUrl } = require('../utils/platform-controller-url');
14
+ const { isPlatformAuthValidForController } = require('../utils/controller-url');
15
+ const { infoLine } = require('../utils/cli-test-layout-chalk');
16
+ const logger = require('../utils/logger');
17
+
18
+ /**
19
+ * Recompute `controller` and `platform-controller` from traefik / setupPlatformMode / front-door config.
20
+ * Call after setup changes platform topology (fresh install, re-install, wipe-data) so a prior
21
+ * full-platform URL (e.g. `http://localhost:3600/miso`) is not reused in single mode.
22
+ *
23
+ * @async
24
+ * @returns {Promise<string>} Normalized platform controller URL
25
+ */
26
+ async function syncPlatformControllerUrlsInConfig() {
27
+ const platformControllerUrl = await resolvePlatformControllerUrl();
28
+ await config.setPlatformControllerUrl(platformControllerUrl);
29
+ await config.setControllerUrl(platformControllerUrl);
30
+ return platformControllerUrl;
31
+ }
32
+
33
+ /**
34
+ * Resolve absolute platform controller URL, persist `platform-controller` and `controller`,
35
+ * and optionally apply `up-platform --force` config (token clear only when not logged in).
36
+ *
37
+ * @async
38
+ * @param {{ applyForceConfig?: boolean, clearTokensAlways?: boolean }} [opts]
39
+ * @returns {Promise<{
40
+ * platformControllerUrl: string,
41
+ * skipLoginIfAuthenticated: boolean,
42
+ * forceSummary: object|null
43
+ * }>}
44
+ */
45
+ async function ensureSetupPlatformAuth(opts = {}) {
46
+ const platformControllerUrl = await syncPlatformControllerUrlsInConfig();
47
+
48
+ let forceSummary = null;
49
+ let skipLoginIfAuthenticated = await isPlatformAuthValidForController(platformControllerUrl);
50
+ const clearTokensAlways = opts.clearTokensAlways === true;
51
+ if (opts.applyForceConfig === true) {
52
+ forceSummary = await upCommon.applyUpPlatformForceConfig({
53
+ silent: true,
54
+ clearTokens: clearTokensAlways || !skipLoginIfAuthenticated,
55
+ defaultControllerUrl: platformControllerUrl
56
+ });
57
+ if (clearTokensAlways || (forceSummary && forceSummary.deviceCleared > 0)) {
58
+ skipLoginIfAuthenticated = false;
59
+ }
60
+ }
61
+
62
+ if (skipLoginIfAuthenticated) {
63
+ logger.log(infoLine(`Already authenticated to ${platformControllerUrl}`));
64
+ }
65
+
66
+ return {
67
+ platformControllerUrl,
68
+ skipLoginIfAuthenticated,
69
+ forceSummary
70
+ };
71
+ }
72
+
73
+ module.exports = {
74
+ syncPlatformControllerUrlsInConfig,
75
+ ensureSetupPlatformAuth
76
+ };
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Admin credential prompts for `aifabrix setup` (dev / pro installation profiles).
3
+ *
4
+ * @fileoverview Setup installation profile + password bundle prompts
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const inquirer = require('inquirer');
13
+ const logger = require('../utils/logger');
14
+ const config = require('../core/config');
15
+ const { generateProAdminPasswords } = require('../core/admin-password-generator');
16
+ const {
17
+ singlePasswordBundle,
18
+ splitPasswordBundle
19
+ } = require('../core/admin-secrets-bundle');
20
+ const {
21
+ INSTALLATION_PROFILE,
22
+ normalizeInstallationProfile,
23
+ isProInstallationProfile
24
+ } = require('../core/setup-installation-profile');
25
+ const { validateEmail, validatePassword } = require('./setup-prompts-validators');
26
+
27
+ /**
28
+ * Resolve installation profile from CLI options or config (interactive fallback in orchestrator).
29
+ * @param {Object} [options]
30
+ * @returns {Promise<'dev'|'pro'>}
31
+ */
32
+ async function resolveInstallationProfileFromOptions(options = {}) {
33
+ const cli = options.installation ?? options.installationProfile;
34
+ if (cli !== undefined && cli !== null && String(cli).trim() !== '') {
35
+ return normalizeInstallationProfile(cli);
36
+ }
37
+ try {
38
+ return await config.getSetupInstallationProfile();
39
+ } catch {
40
+ return INSTALLATION_PROFILE.DEV;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @async
46
+ * @returns {Promise<'dev'|'pro'>}
47
+ */
48
+ async function promptInstallationProfile() {
49
+ const { profile } = await inquirer.prompt([
50
+ {
51
+ type: 'list',
52
+ name: 'profile',
53
+ message: 'Installation profile:',
54
+ choices: [
55
+ {
56
+ name: 'Development (dev) — one admin password for all services',
57
+ value: INSTALLATION_PROFILE.DEV
58
+ },
59
+ {
60
+ name: 'Production (pro) — autogenerate strong passwords or enter manually',
61
+ value: INSTALLATION_PROFILE.PRO
62
+ }
63
+ ],
64
+ default: INSTALLATION_PROFILE.DEV
65
+ }
66
+ ]);
67
+ return profile;
68
+ }
69
+
70
+ /**
71
+ * @param {Object} [options]
72
+ * @returns {Promise<string>}
73
+ */
74
+ async function promptAdminEmail(options = {}) {
75
+ const cli = String(options.adminEmail || '').trim();
76
+ if (cli) return cli;
77
+
78
+ let defaultEmail;
79
+ try {
80
+ const saved = await config.getAdminEmail();
81
+ defaultEmail = saved && saved.trim() ? saved.trim() : undefined;
82
+ } catch {
83
+ defaultEmail = undefined;
84
+ }
85
+ const emailQuestion = {
86
+ type: 'input',
87
+ name: 'adminEmail',
88
+ message: 'Admin email (Keycloak / pgAdmin login):',
89
+ validate: validateEmail
90
+ };
91
+ if (defaultEmail !== undefined) {
92
+ emailQuestion.default = defaultEmail;
93
+ }
94
+ const { adminEmail } = await inquirer.prompt([emailQuestion]);
95
+ return String(adminEmail).trim();
96
+ }
97
+
98
+ /**
99
+ * @async
100
+ * @returns {Promise<import('../core/admin-secrets-bundle').AdminPasswordBundleSingle>}
101
+ */
102
+ async function promptDevPasswordBundle() {
103
+ const answers = await inquirer.prompt([
104
+ {
105
+ type: 'password',
106
+ name: 'adminPassword',
107
+ message: 'Admin password (Postgres / Keycloak / pgAdmin / platform login):',
108
+ mask: '*',
109
+ validate: validatePassword
110
+ },
111
+ {
112
+ type: 'password',
113
+ name: 'adminPasswordConfirm',
114
+ message: 'Confirm admin password:',
115
+ mask: '*',
116
+ validate(input, all) {
117
+ if (input !== all.adminPassword) return 'Passwords do not match';
118
+ return true;
119
+ }
120
+ }
121
+ ]);
122
+ return singlePasswordBundle(answers.adminPassword);
123
+ }
124
+
125
+ /**
126
+ * Print pro autogenerated passwords once (never log plaintext elsewhere).
127
+ * @param {{ infra: string, keycloak: string, platform: string }} passwords
128
+ */
129
+ function displayProPasswordsOnce(passwords) {
130
+ logger.log('');
131
+ logger.log(chalk.bold.yellow('Save these passwords now — they will not be shown again.'));
132
+ logger.log(chalk.gray('────────────────────────────────────────'));
133
+ logger.log(` Infra (Postgres / pgAdmin / Redis Commander): ${passwords.infra}`);
134
+ logger.log(` Keycloak install admin: ${passwords.keycloak}`);
135
+ logger.log(` Platform UI login (user admin): ${passwords.platform}`);
136
+ logger.log(chalk.gray('────────────────────────────────────────'));
137
+ logger.log('');
138
+ }
139
+
140
+ /**
141
+ * @param {Object} [options]
142
+ * @returns {Promise<import('../core/admin-secrets-bundle').AdminPasswordBundleSingle|import('../core/admin-secrets-bundle').AdminPasswordBundleSplit>}
143
+ */
144
+ async function promptProPasswordBundle(options = {}) {
145
+ const modeCli = String(options.proPasswordMode || '').trim().toLowerCase();
146
+ if (modeCli === 'autogen') {
147
+ const generated = generateProAdminPasswords();
148
+ if (options.quiet !== true) {
149
+ displayProPasswordsOnce(generated);
150
+ }
151
+ return splitPasswordBundle(generated);
152
+ }
153
+ if (modeCli === 'manual' && String(options.adminPassword || '').trim()) {
154
+ return singlePasswordBundle(String(options.adminPassword).trim());
155
+ }
156
+
157
+ const { method } = await inquirer.prompt([
158
+ {
159
+ type: 'list',
160
+ name: 'method',
161
+ message: 'How do you want to set admin passwords (pro installation)?',
162
+ choices: [
163
+ { name: 'Autogenerate strong passwords (recommended, shown once)', value: 'autogen' },
164
+ { name: 'Enter one password manually (same for all roles)', value: 'manual-single' },
165
+ { name: 'Enter separate passwords (infra / Keycloak / platform)', value: 'manual-split' }
166
+ ],
167
+ default: 'autogen'
168
+ }
169
+ ]);
170
+
171
+ if (method === 'autogen') {
172
+ const generated = generateProAdminPasswords();
173
+ displayProPasswordsOnce(generated);
174
+ return splitPasswordBundle(generated);
175
+ }
176
+ if (method === 'manual-split') {
177
+ return promptProManualSplitBundle();
178
+ }
179
+ return promptDevPasswordBundle();
180
+ }
181
+
182
+ /**
183
+ * @async
184
+ * @returns {Promise<import('../core/admin-secrets-bundle').AdminPasswordBundleSplit>}
185
+ */
186
+ async function promptProManualSplitBundle() {
187
+ const answers = await inquirer.prompt([
188
+ {
189
+ type: 'password',
190
+ name: 'infra',
191
+ message: 'Infra password (Postgres / pgAdmin / Redis Commander):',
192
+ mask: '*',
193
+ validate: validatePassword
194
+ },
195
+ {
196
+ type: 'password',
197
+ name: 'keycloak',
198
+ message: 'Keycloak install admin password:',
199
+ mask: '*',
200
+ validate: validatePassword
201
+ },
202
+ {
203
+ type: 'password',
204
+ name: 'platform',
205
+ message: 'Platform UI login password (user admin):',
206
+ mask: '*',
207
+ validate: validatePassword
208
+ }
209
+ ]);
210
+ return splitPasswordBundle(answers);
211
+ }
212
+
213
+ /**
214
+ * @param {Object} [options]
215
+ * @returns {Promise<{ adminEmail: string, passwordBundle: object, profile: 'dev'|'pro' }>}
216
+ */
217
+ async function promptAdminCredentialsWithProfile(options = {}) {
218
+ let profile = await resolveInstallationProfileFromOptions(options);
219
+ const hasCliProfile =
220
+ options.installation !== undefined ||
221
+ options.installationProfile !== undefined;
222
+ if (!hasCliProfile) {
223
+ profile = await promptInstallationProfile();
224
+ }
225
+ await config.setSetupInstallationProfile(profile);
226
+
227
+ const adminEmail = await promptAdminEmail(options);
228
+ const passwordBundle = isProInstallationProfile(profile)
229
+ ? await promptProPasswordBundle(options)
230
+ : await promptDevPasswordBundle();
231
+
232
+ return { adminEmail, passwordBundle, profile };
233
+ }
234
+
235
+ module.exports = {
236
+ INSTALLATION_PROFILE,
237
+ resolveInstallationProfileFromOptions,
238
+ promptInstallationProfile,
239
+ promptAdminEmail,
240
+ promptDevPasswordBundle,
241
+ promptProPasswordBundle,
242
+ displayProPasswordsOnce,
243
+ promptAdminCredentialsWithProfile
244
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Setup prompts for platform topology mode (single vs full).
3
+ *
4
+ * @fileoverview Setup platform mode prompt + CLI option normalization
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const inquirer = require('inquirer');
12
+ const { normalizeSetupPlatformMode, SETUP_PLATFORM_MODE } = require('../core/setup-platform-mode');
13
+
14
+ /**
15
+ * Resolve platform mode from CLI options, if provided.
16
+ * @param {Object} [options]
17
+ * @returns {'single'|'full'|null}
18
+ */
19
+ function resolvePlatformModeFromOptions(options = {}) {
20
+ const raw = options.platform;
21
+ if (raw === undefined || raw === null || String(raw).trim() === '') {
22
+ return null;
23
+ }
24
+ const normalized = normalizeSetupPlatformMode(raw);
25
+ // Guard: only accept exact canonical values from CLI; reject unexpected to avoid silent typos.
26
+ const rawNorm = String(raw).trim().toLowerCase();
27
+ if (rawNorm !== 'single' && rawNorm !== 'full') {
28
+ throw new Error('--platform must be single or full (example: aifabrix setup --platform full)');
29
+ }
30
+ return normalized;
31
+ }
32
+
33
+ /**
34
+ * @async
35
+ * @param {Object} [options]
36
+ * @returns {Promise<'single'|'full'>}
37
+ */
38
+ async function promptSetupPlatformMode(options = {}) {
39
+ const fromCli = resolvePlatformModeFromOptions(options);
40
+ if (fromCli) return fromCli;
41
+
42
+ const { mode } = await inquirer.prompt([
43
+ {
44
+ type: 'list',
45
+ name: 'mode',
46
+ message: 'Platform mode:',
47
+ choices: [
48
+ {
49
+ name: 'Single environment — direct ports (no Traefik front door)',
50
+ value: SETUP_PLATFORM_MODE.SINGLE
51
+ },
52
+ {
53
+ name: 'Full platform — Traefik front door + miso/dev/tst/pro (shared infra)',
54
+ value: SETUP_PLATFORM_MODE.FULL
55
+ }
56
+ ],
57
+ default: SETUP_PLATFORM_MODE.SINGLE
58
+ }
59
+ ]);
60
+ return normalizeSetupPlatformMode(mode);
61
+ }
62
+
63
+ module.exports = {
64
+ resolvePlatformModeFromOptions,
65
+ promptSetupPlatformMode
66
+ };
67
+
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shared validators for setup prompts.
3
+ *
4
+ * @fileoverview Email/password validation for setup wizard
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {string} input
13
+ * @returns {true|string}
14
+ */
15
+ function validateEmail(input) {
16
+ const value = (input || '').trim();
17
+ if (!value) return 'Admin email is required';
18
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'Enter a valid email address';
19
+ return true;
20
+ }
21
+
22
+ /**
23
+ * @param {string} input
24
+ * @returns {true|string}
25
+ */
26
+ function validatePassword(input) {
27
+ const value = String(input ?? '');
28
+ if (value.length === 0) return 'Admin password is required';
29
+ if (value.length < 8) return 'Admin password must be at least 8 characters';
30
+ return true;
31
+ }
32
+
33
+ module.exports = {
34
+ validateEmail,
35
+ validatePassword
36
+ };
@@ -28,16 +28,20 @@ const path = require('path');
28
28
  const inquirer = require('inquirer');
29
29
  const chalk = require('chalk');
30
30
  const logger = require('../utils/logger');
31
- const config = require('../core/config');
32
31
  const secretsCore = require('../core/secrets');
33
32
  const secretsEnsure = require('../core/secrets-ensure');
34
33
  const { formatSuccessLine, infoLine } = require('../utils/cli-test-layout-chalk');
34
+ const { validateEmail, validatePassword } = require('./setup-prompts-validators');
35
+ const {
36
+ promptAdminCredentialsWithProfile,
37
+ promptInstallationProfile,
38
+ INSTALLATION_PROFILE
39
+ } = require('./setup-prompts-admin');
35
40
 
36
41
  /** Mode identifiers. Public so callers can switch on them without string typos. */
37
42
  const MODE = Object.freeze({
38
43
  REINSTALL: 'reinstall',
39
44
  WIPE_DATA: 'wipe-data',
40
- CLEAN_FILES: 'clean-files',
41
45
  UPDATE_IMAGES: 'update-images'
42
46
  });
43
47
 
@@ -76,10 +80,6 @@ async function promptModeSelection() {
76
80
  name: 'Reset databases (keeps volumes, removes DBs/users)',
77
81
  value: MODE.WIPE_DATA
78
82
  },
79
- {
80
- name: 'Refresh platform config (keeps data, re-copy platform files)',
81
- value: MODE.CLEAN_FILES
82
- },
83
83
  {
84
84
  name: 'Update images + restart services (keeps data)',
85
85
  value: MODE.UPDATE_IMAGES
@@ -111,79 +111,23 @@ async function confirmDestructiveMode(mode, assumeYes) {
111
111
  }
112
112
 
113
113
  /**
114
- * Validate an email address (RFC 5322-lite for CLI use).
115
- * @param {string} input
116
- * @returns {true|string}
117
- */
118
- function validateEmail(input) {
119
- const value = (input || '').trim();
120
- if (!value) return 'Admin email is required';
121
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return 'Enter a valid email address';
122
- return true;
123
- }
124
-
125
- /**
126
- * Validate a password (non-empty, at least 8 chars).
127
- * @param {string} input
128
- * @returns {true|string}
129
- */
130
- function validatePassword(input) {
131
- const value = String(input ?? '');
132
- if (value.length === 0) return 'Admin password is required';
133
- if (value.length < 8) return 'Admin password must be at least 8 characters';
134
- return true;
135
- }
136
-
137
- /**
138
- * Prompt for admin email + password (fresh install only).
139
- * Reads {@link module:lib/core/config.getAdminEmail} so a previous `aifabrix setup` can prefill the
140
- * email field (user may change it before continuing).
141
- * Asks once for the password and once for confirmation; returns trimmed email
142
- * and the verbatim password (never logged).
114
+ * Prompt for admin credentials (dev or pro installation profile).
143
115
  *
144
116
  * @async
145
- * @returns {Promise<{ adminEmail: string, adminPassword: string }>}
117
+ * @param {Object} [options] - CLI options (`installation`, `proPasswordMode`, `adminEmail`, `adminPassword`, `quiet`)
118
+ * @returns {Promise<{ adminEmail: string, adminPassword: string, passwordBundle: object, profile: string }>}
146
119
  */
147
- async function promptAdminCredentials() {
148
- let defaultEmail;
149
- try {
150
- const saved = await config.getAdminEmail();
151
- defaultEmail = saved && saved.trim() ? saved.trim() : undefined;
152
- } catch {
153
- defaultEmail = undefined;
154
- }
155
- const emailQuestion = {
156
- type: 'input',
157
- name: 'adminEmail',
158
- message: 'Admin email (Keycloak / pgAdmin login):',
159
- validate: validateEmail
160
- };
161
- if (defaultEmail !== undefined) {
162
- emailQuestion.default = defaultEmail;
163
- }
164
- const answers = await inquirer.prompt([
165
- emailQuestion,
166
- {
167
- type: 'password',
168
- name: 'adminPassword',
169
- message: 'Admin password (Postgres / Keycloak / pgAdmin):',
170
- mask: '*',
171
- validate: validatePassword
172
- },
173
- {
174
- type: 'password',
175
- name: 'adminPasswordConfirm',
176
- message: 'Confirm admin password:',
177
- mask: '*',
178
- validate(input, all) {
179
- if (input !== all.adminPassword) return 'Passwords do not match';
180
- return true;
181
- }
182
- }
183
- ]);
120
+ async function promptAdminCredentials(options = {}) {
121
+ const result = await promptAdminCredentialsWithProfile(options);
122
+ const legacyPassword =
123
+ result.passwordBundle.mode === 'single'
124
+ ? result.passwordBundle.password
125
+ : result.passwordBundle.platform;
184
126
  return {
185
- adminEmail: String(answers.adminEmail).trim(),
186
- adminPassword: answers.adminPassword
127
+ adminEmail: result.adminEmail,
128
+ adminPassword: legacyPassword,
129
+ passwordBundle: result.passwordBundle,
130
+ profile: result.profile
187
131
  };
188
132
  }
189
133
 
@@ -411,11 +355,15 @@ module.exports = {
411
355
  MODE,
412
356
  AI_KEYS,
413
357
  BUILDER_DIR_ACTION,
358
+ INSTALLATION_PROFILE,
414
359
  formatBuilderPlatformReplaceLines,
415
360
  promptModeSelection,
416
361
  confirmDestructiveMode,
362
+ promptInstallationProfile,
417
363
  promptAdminCredentials,
418
364
  promptAiTool,
419
365
  detectAiToolStatus,
420
- promptBuilderDirConflict
366
+ promptBuilderDirConflict,
367
+ validateEmail,
368
+ validatePassword
421
369
  };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Marks when `aifabrix setup` is running platform bring-up (suppress noisy token refresh warnings).
3
+ *
4
+ * @fileoverview Setup flow context flag
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ let setupPlatformFlowActive = false;
12
+ let setupCommandFlowActive = false;
13
+
14
+ function beginSetupCommandFlow() {
15
+ setupCommandFlowActive = true;
16
+ }
17
+
18
+ function endSetupCommandFlow() {
19
+ setupCommandFlowActive = false;
20
+ }
21
+
22
+ function isSetupCommandFlow() {
23
+ return setupCommandFlowActive === true;
24
+ }
25
+
26
+ function beginSetupPlatformFlow() {
27
+ setupPlatformFlowActive = true;
28
+ }
29
+
30
+ function endSetupPlatformFlow() {
31
+ setupPlatformFlowActive = false;
32
+ }
33
+
34
+ function isSetupPlatformFlow() {
35
+ return setupPlatformFlowActive === true;
36
+ }
37
+
38
+ module.exports = {
39
+ beginSetupCommandFlow,
40
+ endSetupCommandFlow,
41
+ isSetupCommandFlow,
42
+ beginSetupPlatformFlow,
43
+ endSetupPlatformFlow,
44
+ isSetupPlatformFlow
45
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Spinner helpers for `aifabrix setup` mode handlers.
3
+ *
4
+ * @fileoverview TTY progress spinners for setup commands
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const ora = require('ora');
12
+ const logger = require('../utils/logger');
13
+ const { formatProgress, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
14
+
15
+ function shouldUseSpinner() {
16
+ return Boolean(process && process.stdout && process.stdout.isTTY);
17
+ }
18
+
19
+ function startSpinner(text) {
20
+ if (!shouldUseSpinner()) {
21
+ logger.log(formatProgress(text));
22
+ return null;
23
+ }
24
+ return ora({ text, spinner: 'dots' }).start();
25
+ }
26
+
27
+ function stopSpinnerSuccess(spinner, text) {
28
+ if (!spinner) {
29
+ logger.log(formatSuccessLine(text));
30
+ return;
31
+ }
32
+ spinner.succeed(text);
33
+ }
34
+
35
+ module.exports = {
36
+ shouldUseSpinner,
37
+ startSpinner,
38
+ stopSpinnerSuccess
39
+ };