@aifabrix/builder 2.44.5 → 2.45.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 (249) hide show
  1. package/.cursor/rules/cli-layout.mdc +8 -4
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/README.md +15 -23
  4. package/integration/hubspot-test/README.md +2 -0
  5. package/integration/hubspot-test/test.js +5 -3
  6. package/jest.projects.js +104 -2
  7. package/lib/api/controller-health.api.js +49 -0
  8. package/lib/api/dimension-values.api.js +82 -0
  9. package/lib/api/dimensions.api.js +114 -0
  10. package/lib/api/external-systems.api.js +1 -0
  11. package/lib/api/integration-clients.api.js +168 -0
  12. package/lib/api/types/dimension-values.types.js +28 -0
  13. package/lib/api/types/dimensions.types.js +31 -0
  14. package/lib/api/types/integration-clients.types.js +45 -0
  15. package/lib/api/validation-runner.js +46 -25
  16. package/lib/app/deploy-config.js +11 -1
  17. package/lib/app/deploy-status-display.js +3 -3
  18. package/lib/app/deploy.js +36 -14
  19. package/lib/app/display.js +15 -11
  20. package/lib/app/helpers.js +3 -3
  21. package/lib/app/index.js +3 -3
  22. package/lib/app/push.js +46 -23
  23. package/lib/app/register.js +7 -6
  24. package/lib/app/restart-display.js +126 -0
  25. package/lib/app/rotate-secret.js +7 -6
  26. package/lib/app/run-container-start.js +12 -6
  27. package/lib/app/run-env-compose.js +30 -1
  28. package/lib/app/run-helpers.js +58 -19
  29. package/lib/app/run-reload-sync.js +148 -0
  30. package/lib/app/run-resolve-image.js +51 -1
  31. package/lib/app/run.js +148 -74
  32. package/lib/app/show-display.js +7 -0
  33. package/lib/app/show.js +87 -5
  34. package/lib/build/index.js +83 -49
  35. package/lib/cli/doctor-check.js +117 -0
  36. package/lib/cli/index.js +8 -2
  37. package/lib/cli/infra-guided.js +460 -0
  38. package/lib/cli/installation-log-command.js +73 -0
  39. package/lib/cli/setup-app.js +31 -3
  40. package/lib/cli/setup-auth.js +98 -27
  41. package/lib/cli/setup-dev-path-commands.js +50 -3
  42. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  43. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  44. package/lib/cli/setup-infra.js +132 -118
  45. package/lib/cli/setup-integration-client.js +182 -0
  46. package/lib/cli/setup-parameters.js +21 -2
  47. package/lib/cli/setup-platform.js +102 -0
  48. package/lib/cli/setup-secrets.js +18 -6
  49. package/lib/cli/setup-utility-resolve.js +132 -0
  50. package/lib/cli/setup-utility.js +143 -84
  51. package/lib/commands/app-logs.js +81 -33
  52. package/lib/commands/auth-config.js +116 -18
  53. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  54. package/lib/commands/datasource-capability-output.js +29 -0
  55. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  56. package/lib/commands/datasource-capability.js +411 -0
  57. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  58. package/lib/commands/datasource.js +53 -13
  59. package/lib/commands/dev-down.js +3 -3
  60. package/lib/commands/dev-infra-gate.js +32 -0
  61. package/lib/commands/dev-init.js +13 -7
  62. package/lib/commands/dimension-value.js +179 -0
  63. package/lib/commands/dimension.js +330 -0
  64. package/lib/commands/integration-client.js +430 -0
  65. package/lib/commands/login-device.js +65 -30
  66. package/lib/commands/login.js +21 -10
  67. package/lib/commands/parameters-validate.js +78 -13
  68. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  69. package/lib/commands/repair-datasource-keys.js +10 -5
  70. package/lib/commands/repair-datasource.js +19 -7
  71. package/lib/commands/repair-env-template.js +4 -1
  72. package/lib/commands/repair-openapi-sync.js +172 -0
  73. package/lib/commands/repair-persist.js +102 -0
  74. package/lib/commands/repair-rbac-extract.js +27 -0
  75. package/lib/commands/repair-rbac-migrate.js +186 -0
  76. package/lib/commands/repair-rbac.js +214 -31
  77. package/lib/commands/repair-system-alignment.js +246 -0
  78. package/lib/commands/repair-system-permissions.js +168 -0
  79. package/lib/commands/repair.js +120 -338
  80. package/lib/commands/secure.js +1 -1
  81. package/lib/commands/setup-modes.js +468 -0
  82. package/lib/commands/setup-prompts.js +421 -0
  83. package/lib/commands/setup.js +254 -0
  84. package/lib/commands/teardown.js +277 -0
  85. package/lib/commands/up-common.js +113 -19
  86. package/lib/commands/up-dataplane.js +44 -19
  87. package/lib/commands/up-miso.js +18 -18
  88. package/lib/commands/upload.js +111 -23
  89. package/lib/commands/wizard-core-helpers.js +14 -11
  90. package/lib/commands/wizard-core.js +6 -5
  91. package/lib/commands/wizard-dataplane.js +2 -2
  92. package/lib/commands/wizard-entity-selection.js +4 -3
  93. package/lib/commands/wizard-headless.js +2 -1
  94. package/lib/commands/wizard.js +2 -1
  95. package/lib/constants/infra-compose-service-names.js +40 -0
  96. package/lib/core/audit-logger.js +1 -34
  97. package/lib/core/config-admin-email.js +56 -0
  98. package/lib/core/config-normalize.js +60 -0
  99. package/lib/core/config-registered-controller-urls.js +54 -0
  100. package/lib/core/config.js +33 -50
  101. package/lib/core/env-reader.js +16 -3
  102. package/lib/core/secrets-admin-env.js +101 -0
  103. package/lib/core/secrets-ensure-infra.js +34 -1
  104. package/lib/core/secrets-ensure.js +88 -66
  105. package/lib/core/secrets-env-content.js +428 -0
  106. package/lib/core/secrets-env-declarative-expand.js +170 -0
  107. package/lib/core/secrets-env-write.js +29 -1
  108. package/lib/core/secrets-load.js +252 -0
  109. package/lib/core/secrets-names.js +32 -0
  110. package/lib/core/secrets.js +17 -757
  111. package/lib/datasource/capability/basic-exposure.js +76 -0
  112. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  113. package/lib/datasource/capability/capability-key.js +34 -0
  114. package/lib/datasource/capability/capability-resolve.js +172 -0
  115. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  116. package/lib/datasource/capability/copy-operations.js +348 -0
  117. package/lib/datasource/capability/copy-test-payload.js +139 -0
  118. package/lib/datasource/capability/create-operations.js +235 -0
  119. package/lib/datasource/capability/dimension-operations.js +151 -0
  120. package/lib/datasource/capability/dimension-validate.js +219 -0
  121. package/lib/datasource/capability/json-pointer.js +31 -0
  122. package/lib/datasource/capability/reference-rewrite.js +51 -0
  123. package/lib/datasource/capability/relate-operations.js +325 -0
  124. package/lib/datasource/capability/relate-validate.js +219 -0
  125. package/lib/datasource/capability/remove-operations.js +275 -0
  126. package/lib/datasource/capability/run-capability-copy.js +152 -0
  127. package/lib/datasource/capability/run-capability-diff.js +135 -0
  128. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  129. package/lib/datasource/capability/run-capability-edit.js +377 -0
  130. package/lib/datasource/capability/run-capability-relate.js +193 -0
  131. package/lib/datasource/capability/run-capability-remove.js +105 -0
  132. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  133. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  134. package/lib/datasource/list.js +136 -23
  135. package/lib/datasource/log-viewer.js +2 -4
  136. package/lib/datasource/unified-validation-run.js +51 -16
  137. package/lib/datasource/validate.js +53 -1
  138. package/lib/deployment/deploy-poll-ui.js +60 -0
  139. package/lib/deployment/deployer-status.js +29 -3
  140. package/lib/deployment/deployer.js +48 -30
  141. package/lib/deployment/environment.js +7 -2
  142. package/lib/deployment/poll-interval.js +72 -0
  143. package/lib/deployment/push.js +11 -9
  144. package/lib/external-system/deploy.js +9 -2
  145. package/lib/external-system/download.js +61 -32
  146. package/lib/external-system/sync-deploy-manifest.js +33 -0
  147. package/lib/infrastructure/index.js +49 -19
  148. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  149. package/lib/internal/node-fs.js +2 -0
  150. package/lib/parameters/infra-kv-discovery.js +29 -4
  151. package/lib/parameters/infra-parameter-catalog.js +6 -3
  152. package/lib/parameters/infra-parameter-validate.js +67 -19
  153. package/lib/resolvers/datasource-resolver.js +53 -0
  154. package/lib/resolvers/dimension-file.js +52 -0
  155. package/lib/resolvers/manifest-resolver.js +133 -0
  156. package/lib/schema/application-schema.json +4 -0
  157. package/lib/schema/external-datasource.schema.json +183 -53
  158. package/lib/schema/external-system.schema.json +23 -10
  159. package/lib/schema/infra.parameter.yaml +26 -1
  160. package/lib/schema/wizard-config.schema.json +1 -1
  161. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  162. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  163. package/lib/utils/app-config-resolver.js +24 -1
  164. package/lib/utils/app-run-containers.js +2 -2
  165. package/lib/utils/applications-config-defaults.js +206 -0
  166. package/lib/utils/auth-config-validator.js +2 -12
  167. package/lib/utils/bash-secret-env.js +59 -0
  168. package/lib/utils/cli-secrets-error-format.js +78 -0
  169. package/lib/utils/cli-test-layout-chalk.js +31 -9
  170. package/lib/utils/cli-utils.js +4 -36
  171. package/lib/utils/compose-generate-docker-compose.js +111 -6
  172. package/lib/utils/compose-generator.js +17 -8
  173. package/lib/utils/controller-url.js +50 -7
  174. package/lib/utils/datasource-test-run-display.js +8 -0
  175. package/lib/utils/dev-hosts-helper.js +3 -2
  176. package/lib/utils/dev-init-ssh-merge.js +2 -1
  177. package/lib/utils/docker-build.js +17 -9
  178. package/lib/utils/docker-reload-mount.js +127 -0
  179. package/lib/utils/env-copy.js +99 -14
  180. package/lib/utils/env-template.js +5 -1
  181. package/lib/utils/external-readme.js +71 -2
  182. package/lib/utils/external-system-local-test-tty.js +3 -2
  183. package/lib/utils/external-system-readiness-core.js +45 -12
  184. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  185. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  186. package/lib/utils/external-system-readiness-display.js +10 -1
  187. package/lib/utils/file-upload.js +40 -3
  188. package/lib/utils/health-check-db-init.js +107 -0
  189. package/lib/utils/health-check-public-warn.js +69 -0
  190. package/lib/utils/health-check-url.js +28 -10
  191. package/lib/utils/health-check.js +139 -107
  192. package/lib/utils/help-builder.js +5 -1
  193. package/lib/utils/image-name.js +34 -7
  194. package/lib/utils/infra-optional-service-flags.js +69 -0
  195. package/lib/utils/installation-log-core.js +282 -0
  196. package/lib/utils/installation-log-record.js +237 -0
  197. package/lib/utils/installation-log.js +123 -0
  198. package/lib/utils/integration-file-backup.js +74 -0
  199. package/lib/utils/log-redaction.js +105 -0
  200. package/lib/utils/manifest-location.js +164 -0
  201. package/lib/utils/manifest-source-emit.js +162 -0
  202. package/lib/utils/mutagen-install.js +30 -3
  203. package/lib/utils/paths.js +308 -76
  204. package/lib/utils/postgres-wipe.js +212 -0
  205. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  206. package/lib/utils/remote-dev-auth.js +21 -5
  207. package/lib/utils/remote-docker-env.js +9 -1
  208. package/lib/utils/remote-secrets-loader.js +49 -4
  209. package/lib/utils/resolve-docker-image-ref.js +9 -3
  210. package/lib/utils/run-cli-flags.js +29 -0
  211. package/lib/utils/secrets-ancestor-paths.js +47 -0
  212. package/lib/utils/secrets-canonical.js +10 -3
  213. package/lib/utils/secrets-helpers.js +17 -10
  214. package/lib/utils/secrets-kv-refs.js +42 -0
  215. package/lib/utils/secrets-kv-scope.js +19 -2
  216. package/lib/utils/secrets-materialize-local.js +134 -0
  217. package/lib/utils/secrets-path.js +26 -13
  218. package/lib/utils/secrets-utils.js +20 -10
  219. package/lib/utils/system-builder-root.js +42 -0
  220. package/lib/utils/url-declarative-public-base.js +80 -12
  221. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  222. package/lib/utils/url-declarative-resolve-build.js +24 -388
  223. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  224. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  225. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  226. package/lib/utils/url-declarative-resolve.js +47 -7
  227. package/lib/utils/url-declarative-runtime-base-path.js +52 -0
  228. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  229. package/lib/utils/urls-local-registry-scan.js +103 -0
  230. package/lib/utils/urls-local-registry.js +158 -76
  231. package/lib/utils/validation-poll-ui.js +81 -0
  232. package/lib/utils/validation-run-poll.js +29 -5
  233. package/lib/utils/with-muted-logger.js +53 -0
  234. package/package.json +3 -1
  235. package/templates/applications/dataplane/application.yaml +5 -1
  236. package/templates/applications/dataplane/rbac.yaml +10 -10
  237. package/templates/applications/keycloak/env.template +8 -6
  238. package/templates/applications/miso-controller/application.yaml +9 -0
  239. package/templates/applications/miso-controller/env.template +27 -29
  240. package/templates/applications/miso-controller/rbac.yaml +9 -9
  241. package/templates/external-system/README.md.hbs +83 -123
  242. package/.npmrc.token +0 -1
  243. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  244. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  245. package/.nyc_output/processinfo/index.json +0 -1
  246. package/lib/api/service-users.api.js +0 -150
  247. package/lib/api/types/service-users.types.js +0 -65
  248. package/lib/cli/setup-service-user.js +0 -187
  249. package/lib/commands/service-user.js +0 -429
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Mode handlers for `aifabrix setup`.
3
+ *
4
+ * Five small handlers, one per branch of the setup decision tree:
5
+ * - {@link runFreshInstall} — no infra: wizard for admin creds + AI tool, then up-infra + up-platform.
6
+ * - {@link runReinstall} — Mode 1: down-infra -v, wipe local secrets, up-infra, up-platform --force.
7
+ * - {@link runWipeData} — Mode 2: drop DBs/roles, wipe local secrets, up-infra, up-platform --force.
8
+ * - {@link runCleanInstallFiles}— Mode 3: wipe local secrets, up-infra, up-platform --force (templates re-copied).
9
+ * - {@link runUpdateImages} — Mode 4: docker pull infra + platform images, up-infra, up-platform.
10
+ *
11
+ * Every handler ends with `up-infra` (idempotent, reads flags from
12
+ * `config.yaml`) followed by `up-platform`. Optional service keys
13
+ * (`traefik`, `pgadmin`, `redisCommander`) are written when missing so the
14
+ * file matches effective compose defaults. Modes 1/2/3 also remove
15
+ * `secrets.local.yaml` so the next platform bootstrap re-resolves
16
+ * service secrets from the catalog and re-prompts the AI tool wizard.
17
+ *
18
+ * @fileoverview aifabrix setup mode handlers
19
+ * @author AI Fabrix Team
20
+ * @version 2.0.0
21
+ */
22
+
23
+ 'use strict';
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+ const chalk = require('chalk');
28
+ const ora = require('ora');
29
+
30
+ const config = require('../core/config');
31
+ const infra = require('../infrastructure');
32
+ const pathsUtil = require('../utils/paths');
33
+ const logger = require('../utils/logger');
34
+ const dockerUtils = require('../utils/docker');
35
+ const dockerExec = require('../utils/docker-exec');
36
+ const {
37
+ formatSuccessLine,
38
+ formatProgress,
39
+ infoLine,
40
+ sectionTitle,
41
+ formatDatasourceListRow,
42
+ successGlyph
43
+ } = require('../utils/cli-test-layout-chalk');
44
+ const { withMutedLogger } = require('../utils/with-muted-logger');
45
+ const infraHelpers = require('../infrastructure/helpers');
46
+
47
+ const upMiso = require('./up-miso');
48
+ const upDataplane = require('./up-dataplane');
49
+ const upCommon = require('./up-common');
50
+ const postgresWipe = require('../utils/postgres-wipe');
51
+ const setupPrompts = require('./setup-prompts');
52
+ const { handleLogin } = require('./login');
53
+ const infraGuided = require('../cli/infra-guided');
54
+ const {
55
+ computeEffectiveInfraOptionalFlags,
56
+ persistMissingInfraOptionalServiceFlags
57
+ } = require('../utils/infra-optional-service-flags');
58
+
59
+ /** Builder app keys touched by `up-platform --force`. */
60
+ const PLATFORM_APPS = ['keycloak', 'miso-controller', 'dataplane'];
61
+
62
+ function formatBackupSuffix() {
63
+ const d = new Date();
64
+ const pad = (n) => String(n).padStart(2, '0');
65
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
66
+ }
67
+
68
+ function listNonDotEntries(dir) {
69
+ try {
70
+ return fs.readdirSync(dir).filter((n) => n && !String(n).startsWith('.'));
71
+ } catch {
72
+ return [];
73
+ }
74
+ }
75
+
76
+ function isExistingNonEmptyDir(dir) {
77
+ if (!fs.existsSync(dir)) return false;
78
+ try {
79
+ const st = fs.statSync(dir);
80
+ if (!st || typeof st.isDirectory !== 'function' || !st.isDirectory()) return false;
81
+ } catch {
82
+ return false;
83
+ }
84
+ return listNonDotEntries(dir).length > 0;
85
+ }
86
+
87
+ function makeUniqueBackupPath(parentDir, appName, suffix) {
88
+ const base = path.join(parentDir, `${appName}.backup-${suffix}`);
89
+ if (!fs.existsSync(base)) return base;
90
+ for (let i = 2; i < 1000; i++) {
91
+ const candidate = path.join(parentDir, `${appName}.backup-${suffix}-${i}`);
92
+ if (!fs.existsSync(candidate)) return candidate;
93
+ }
94
+ throw new Error(`Could not create unique backup path for ${appName} under ${parentDir}`);
95
+ }
96
+
97
+ /**
98
+ * Non-empty platform app dirs under {@link pathsUtil.getSystemBuilderRoot}.
99
+ * @returns {{ sysRoot: string|null, apps: string[] }}
100
+ */
101
+ function getNonEmptySystemPlatformApps() {
102
+ let sysRoot = null;
103
+ try {
104
+ sysRoot = pathsUtil.getSystemBuilderRoot();
105
+ } catch {
106
+ return { sysRoot: null, apps: [] };
107
+ }
108
+ if (!sysRoot || !fs.existsSync(sysRoot)) {
109
+ return { sysRoot, apps: [] };
110
+ }
111
+ const apps = [];
112
+ for (const app of PLATFORM_APPS) {
113
+ const p = path.join(sysRoot, app);
114
+ if (isExistingNonEmptyDir(p)) apps.push(app);
115
+ }
116
+ return { sysRoot, apps };
117
+ }
118
+
119
+ function backupPlatformAppDirs(platformApps) {
120
+ const suffix = formatBackupSuffix();
121
+ const backedUp = [];
122
+ for (const appName of platformApps) {
123
+ const appDir = path.resolve(pathsUtil.getBuilderPath(appName));
124
+ if (!isExistingNonEmptyDir(appDir)) continue;
125
+ const parentDir = path.dirname(appDir);
126
+ const dest = makeUniqueBackupPath(parentDir, appName, suffix);
127
+ fs.renameSync(appDir, dest);
128
+ backedUp.push({ appName, from: appDir, to: dest });
129
+ }
130
+ return backedUp;
131
+ }
132
+
133
+ function getBuilderRootEntriesOrNull() {
134
+ const builderRoot = pathsUtil.getBuilderRoot();
135
+ if (!fs.existsSync(builderRoot)) return null;
136
+ let st;
137
+ try {
138
+ st = fs.statSync(builderRoot);
139
+ } catch (err) {
140
+ throw new Error(`Could not read builder folder at ${builderRoot}: ${err.message}`);
141
+ }
142
+ if (!st || typeof st.isDirectory !== 'function' || !st.isDirectory()) {
143
+ throw new Error(`Builder root exists but is not a directory: ${builderRoot}`);
144
+ }
145
+ const entries = listNonDotEntries(builderRoot);
146
+ if (entries.length === 0) return null;
147
+ return { builderRoot, entries };
148
+ }
149
+
150
+ function handleBuilderDirAction(action) {
151
+ if (action === setupPrompts.BUILDER_DIR_ACTION.ABORT) {
152
+ logger.log(chalk.yellow('Aborted by user.'));
153
+ return { aborted: true, backedUp: [], skipClean: false };
154
+ }
155
+ if (action === setupPrompts.BUILDER_DIR_ACTION.KEEP_FILES) {
156
+ return { aborted: false, backedUp: [], skipClean: true };
157
+ }
158
+ if (action === setupPrompts.BUILDER_DIR_ACTION.BACKUP) {
159
+ const backedUp = backupPlatformAppDirs(PLATFORM_APPS);
160
+ for (const item of backedUp) {
161
+ logger.log(
162
+ chalk.gray(` ${successGlyph()} Backed up ${item.from} → ${path.basename(item.to)}/`)
163
+ );
164
+ }
165
+ return { aborted: false, backedUp, skipClean: false };
166
+ }
167
+ return { aborted: false, backedUp: [], skipClean: false };
168
+ }
169
+
170
+ async function maybePromptForExistingBuilderDir({ force }) {
171
+ if (force !== true) return { aborted: false, backedUp: [], skipClean: false };
172
+ const existing = getBuilderRootEntriesOrNull();
173
+ const { sysRoot, apps: systemPlatformApps } = getNonEmptySystemPlatformApps();
174
+ if (!existing && systemPlatformApps.length === 0) {
175
+ return { aborted: false, backedUp: [], skipClean: false };
176
+ }
177
+ const builderRoot = existing ? existing.builderRoot : pathsUtil.getBuilderRoot();
178
+ const totalEntries = existing ? existing.entries.length : systemPlatformApps.length;
179
+ const action = await setupPrompts.promptBuilderDirConflict({
180
+ builderRoot,
181
+ totalEntries,
182
+ platformApps: PLATFORM_APPS,
183
+ systemBuilderRoot: sysRoot || undefined,
184
+ systemPlatformApps: systemPlatformApps.length ? systemPlatformApps : undefined
185
+ });
186
+ return handleBuilderDirAction(action);
187
+ }
188
+
189
+ function shouldUseSpinner() {
190
+ return Boolean(process && process.stdout && process.stdout.isTTY);
191
+ }
192
+
193
+ function startSpinner(text) {
194
+ if (!shouldUseSpinner()) {
195
+ logger.log(formatProgress(text));
196
+ return null;
197
+ }
198
+ return ora({ text, spinner: 'dots' }).start();
199
+ }
200
+
201
+ function stopSpinnerSuccess(spinner, text) {
202
+ if (!spinner) {
203
+ logger.log(formatSuccessLine(text));
204
+ return;
205
+ }
206
+ spinner.succeed(text);
207
+ }
208
+
209
+ /**
210
+ * Start infrastructure using the developer's `config.yaml` flags.
211
+ * Mirrors the relevant slice of `runUpInfraCommand` from `lib/cli/setup-infra.js`
212
+ * without re-applying CLI flag persistence (setup passes no Commander flags).
213
+ * After a successful start, missing `traefik` / `pgadmin` / `redisCommander`
214
+ * keys are written to match effective {@link infra.startInfra} options.
215
+ *
216
+ * @async
217
+ * @param {Object} [overrides] - Optional admin overrides (fresh install only)
218
+ * @param {string} [overrides.adminEmail] - Override pgAdmin / Keycloak admin email
219
+ * @param {string} [overrides.adminPassword] - Override Postgres / pgAdmin / Keycloak admin password
220
+ * @returns {Promise<void>}
221
+ */
222
+ async function startInfraFromConfig(overrides = {}) {
223
+ await config.ensureSecretsEncryptionKey();
224
+ const cfg = await config.getConfig();
225
+ const emailOverride = String(overrides.adminEmail || '').trim();
226
+ const emailFromConfig = typeof cfg.adminEmail === 'string' ? cfg.adminEmail.trim() : '';
227
+ const adminEmailMerged = emailOverride || emailFromConfig || undefined;
228
+ const effective = computeEffectiveInfraOptionalFlags(cfg, {});
229
+ await withMutedLogger(async() => {
230
+ await infra.startInfra(null, {
231
+ traefik: effective.traefik,
232
+ pgadmin: effective.pgadmin,
233
+ redisCommander: effective.redisCommander,
234
+ adminPassword: overrides.adminPassword,
235
+ adminPwd: overrides.adminPassword,
236
+ adminEmail: adminEmailMerged,
237
+ tlsEnabled: cfg.tlsEnabled === true
238
+ });
239
+ });
240
+ await persistMissingInfraOptionalServiceFlags(cfg, effective);
241
+ }
242
+
243
+ /**
244
+ * Run the platform after infra is up: optionally apply force config + clean
245
+ * builder dirs, then `up-miso` followed by `up-dataplane`.
246
+ *
247
+ * @async
248
+ * @param {{ force?: boolean }} [opts]
249
+ * @returns {Promise<void>}
250
+ */
251
+ async function runUpPlatform(opts = {}) {
252
+ const preflight = await maybePromptForExistingBuilderDir({ force: opts.force === true });
253
+ if (preflight.aborted) return;
254
+
255
+ let forceCleanSummary = null;
256
+ if (opts.force === true) {
257
+ const forceSummary = await upCommon.applyUpPlatformForceConfig({ silent: true });
258
+ if (preflight.skipClean !== true) {
259
+ const cleanedApps = await upCommon.cleanBuilderAppDirs(PLATFORM_APPS, { silent: true });
260
+ forceCleanSummary = { forceSummary, cleanedApps };
261
+ } else {
262
+ forceCleanSummary = { forceSummary, cleanedApps: [] };
263
+ }
264
+ }
265
+ // Reuse the same guided UX as `aifabrix up-platform` (keycloak → miso → auth → dataplane → footer).
266
+ await infraGuided.runGuidedUpPlatform(
267
+ { base: true },
268
+ upMiso.handleUpMiso,
269
+ upDataplane.handleUpDataplane,
270
+ handleLogin,
271
+ forceCleanSummary
272
+ );
273
+ }
274
+
275
+ /**
276
+ * Remove `~/.aifabrix/secrets.local.yaml` if present. Never touches the
277
+ * shared `aifabrix-secrets` file. Idempotent.
278
+ *
279
+ * @returns {boolean} True when a file was removed
280
+ */
281
+ function removeUserLocalSecrets() {
282
+ const target = pathsUtil.getPrimaryUserSecretsLocalPath();
283
+ if (!fs.existsSync(target)) return false;
284
+ fs.rmSync(target, { force: true });
285
+ logger.log(chalk.gray(` ${successGlyph()} Removed ${target}`));
286
+ return true;
287
+ }
288
+
289
+ /**
290
+ * Pull infra-compose images (postgres, redis, optional pgadmin/redis-commander/traefik).
291
+ * Best-effort: warns and continues when the compose file is missing.
292
+ *
293
+ * @async
294
+ * @returns {Promise<void>}
295
+ */
296
+ async function pullInfraImages() {
297
+ const devId = await config.getDeveloperId();
298
+ const infraDir = path.join(pathsUtil.getAifabrixSystemDir(), infraHelpers.getInfraDirName(devId));
299
+ const composePath = path.join(infraDir, 'compose.yaml');
300
+ if (!fs.existsSync(composePath)) {
301
+ logger.log(chalk.yellow(`No infra compose file at ${composePath}; skipping image pull.`));
302
+ return;
303
+ }
304
+ const project = infraHelpers.getInfraProjectName(devId);
305
+ const composeCmd = await dockerUtils.getComposeCommand();
306
+ logger.log('');
307
+ logger.log(sectionTitle('Pull images'));
308
+ const spin = startSpinner('Pulling infrastructure images...');
309
+ await dockerExec.execWithDockerEnv(`${composeCmd} -f "${composePath}" -p ${project} pull`, { cwd: infraDir });
310
+ stopSpinnerSuccess(spin, 'Infrastructure images pulled');
311
+ }
312
+
313
+ /**
314
+ * Pull a single platform app image based on `builder/<appName>/application.yaml`.
315
+ * Tolerates missing config / unresolvable images by warning and continuing.
316
+ *
317
+ * @async
318
+ * @param {string} appName
319
+ * @returns {Promise<void>}
320
+ */
321
+ async function pullPlatformAppImage(appName) {
322
+ let imageRef = '';
323
+ try {
324
+ const { loadConfigFile } = require('../utils/config-format');
325
+ const builderPath = pathsUtil.getBuilderPath(appName);
326
+ const configPath = pathsUtil.resolveApplicationConfigPath(builderPath);
327
+ const cfg = loadConfigFile(configPath) || {};
328
+ const deploy = cfg.deploy || {};
329
+ if (typeof deploy.image === 'string' && deploy.image.trim()) {
330
+ imageRef = deploy.image.trim();
331
+ }
332
+ } catch {
333
+ imageRef = '';
334
+ }
335
+ if (!imageRef) {
336
+ logger.log(formatDatasourceListRow('skipped', appName, 'no image ref'));
337
+ return;
338
+ }
339
+ try {
340
+ const spin = startSpinner(`Pulling ${appName} image...`);
341
+ await dockerExec.execWithDockerEnv(`docker pull ${imageRef}`);
342
+ stopSpinnerSuccess(spin, `Pulled ${appName} image`);
343
+ } catch (err) {
344
+ logger.log(chalk.yellow(`Could not pull ${imageRef}: ${err.message}`));
345
+ }
346
+ }
347
+
348
+ /** Pull every platform app image. */
349
+ async function pullPlatformImages() {
350
+ for (const appName of PLATFORM_APPS) {
351
+ await pullPlatformAppImage(appName);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Fresh install: wizard collects admin email + password, then AI tool keys,
357
+ * then up-infra + up-platform.
358
+ *
359
+ * @async
360
+ * @param {{ adminEmail: string, adminPassword: string }} adminCreds
361
+ * @returns {Promise<void>}
362
+ */
363
+ async function runFreshInstall(adminCreds) {
364
+ logger.log(infoLine('Setup: fresh install'));
365
+ const infraSpin = startSpinner('Starting infrastructure...');
366
+ await startInfraFromConfig({
367
+ adminEmail: adminCreds.adminEmail,
368
+ adminPassword: adminCreds.adminPassword
369
+ });
370
+ stopSpinnerSuccess(infraSpin, 'Infrastructure ready');
371
+ // Bring up infra first, then collect optional platform-level secrets (AI tool)
372
+ // so the flow feels like a product installer: infra → platform configuration → platform services.
373
+ await setupPrompts.promptAiTool({ silentIfConfigured: true });
374
+ // Fresh-install should reset dev defaults (env + controller URL) the same way
375
+ // as other setup recovery paths, otherwise the platform can come up pointed at
376
+ // stale configuration.
377
+ await runUpPlatform({ force: true });
378
+ }
379
+
380
+ /**
381
+ * Mode 1 — Re-install: stop infra + remove all volumes, wipe local secrets,
382
+ * then up-infra + up-platform --force.
383
+ *
384
+ * @async
385
+ * @returns {Promise<void>}
386
+ */
387
+ async function runReinstall() {
388
+ logger.log(infoLine('Setup: re-install (volumes will be removed)'));
389
+ const downSpin = startSpinner('Stopping infrastructure (down-infra -v)...');
390
+ await withMutedLogger(async() => {
391
+ await infra.stopInfraWithVolumes();
392
+ });
393
+ stopSpinnerSuccess(downSpin, 'Infrastructure stopped and volumes removed');
394
+ removeUserLocalSecrets();
395
+ await setupPrompts.promptAiTool({ silentIfConfigured: true });
396
+ const infraSpin = startSpinner('Starting infrastructure...');
397
+ await startInfraFromConfig({});
398
+ stopSpinnerSuccess(infraSpin, 'Infrastructure ready');
399
+ await runUpPlatform({ force: true });
400
+ }
401
+
402
+ /**
403
+ * Mode 2 — Wipe data: drop DBs and DB users, wipe local secrets, then
404
+ * up-infra + up-platform --force. Postgres volume + admin password preserved.
405
+ *
406
+ * @async
407
+ * @returns {Promise<void>}
408
+ */
409
+ async function runWipeData() {
410
+ logger.log(infoLine('Setup: wipe data (databases and DB users)'));
411
+ await postgresWipe.wipePostgresData();
412
+ removeUserLocalSecrets();
413
+ await setupPrompts.promptAiTool({ silentIfConfigured: true });
414
+ const infraSpin = startSpinner('Starting infrastructure...');
415
+ await startInfraFromConfig({});
416
+ stopSpinnerSuccess(infraSpin, 'Infrastructure ready');
417
+ await runUpPlatform({ force: true });
418
+ }
419
+
420
+ /**
421
+ * Mode 3 — Clean install files: wipe local secrets, then up-infra +
422
+ * up-platform --force (which removes builder/<keycloak|miso-controller|dataplane>
423
+ * and re-fetches from templates).
424
+ *
425
+ * @async
426
+ * @returns {Promise<void>}
427
+ */
428
+ async function runCleanInstallFiles() {
429
+ logger.log(infoLine('Setup: clean installation files and re-install platform services'));
430
+ removeUserLocalSecrets();
431
+ await setupPrompts.promptAiTool({ silentIfConfigured: true });
432
+ const infraSpin = startSpinner('Starting infrastructure...');
433
+ await startInfraFromConfig({});
434
+ stopSpinnerSuccess(infraSpin, 'Infrastructure ready');
435
+ await runUpPlatform({ force: true });
436
+ }
437
+
438
+ /**
439
+ * Mode 4 — Update images: docker pull infra + platform images, then up-infra +
440
+ * up-platform (no force; secrets and data preserved).
441
+ *
442
+ * @async
443
+ * @returns {Promise<void>}
444
+ */
445
+ async function runUpdateImages() {
446
+ logger.log(infoLine('Setup: update docker images'));
447
+ await pullInfraImages();
448
+ await pullPlatformImages();
449
+ await setupPrompts.promptAiTool({ silentIfConfigured: true });
450
+ const infraSpin = startSpinner('Starting infrastructure...');
451
+ await startInfraFromConfig({});
452
+ stopSpinnerSuccess(infraSpin, 'Infrastructure ready');
453
+ await runUpPlatform({ force: false });
454
+ }
455
+
456
+ module.exports = {
457
+ PLATFORM_APPS,
458
+ startInfraFromConfig,
459
+ runUpPlatform,
460
+ removeUserLocalSecrets,
461
+ pullInfraImages,
462
+ pullPlatformImages,
463
+ runFreshInstall,
464
+ runReinstall,
465
+ runWipeData,
466
+ runCleanInstallFiles,
467
+ runUpdateImages
468
+ };