@aifabrix/builder 2.44.5 → 2.44.6

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 (207) hide show
  1. package/.cursor/rules/cli-layout.mdc +1 -1
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/.npmrc.token +1 -1
  4. package/README.md +15 -23
  5. package/integration/hubspot-test/README.md +2 -0
  6. package/integration/hubspot-test/test.js +5 -3
  7. package/jest.projects.js +48 -2
  8. package/lib/api/controller-health.api.js +49 -0
  9. package/lib/api/dimension-values.api.js +82 -0
  10. package/lib/api/dimensions.api.js +114 -0
  11. package/lib/api/external-systems.api.js +1 -0
  12. package/lib/api/integration-clients.api.js +168 -0
  13. package/lib/api/types/dimension-values.types.js +28 -0
  14. package/lib/api/types/dimensions.types.js +31 -0
  15. package/lib/api/types/integration-clients.types.js +45 -0
  16. package/lib/api/validation-runner.js +46 -25
  17. package/lib/app/deploy-config.js +11 -1
  18. package/lib/app/deploy-status-display.js +3 -3
  19. package/lib/app/deploy.js +36 -14
  20. package/lib/app/display.js +15 -11
  21. package/lib/app/push.js +46 -23
  22. package/lib/app/register.js +1 -1
  23. package/lib/app/restart-display.js +95 -0
  24. package/lib/app/rotate-secret.js +1 -1
  25. package/lib/app/run-container-start.js +12 -6
  26. package/lib/app/run-env-compose.js +30 -1
  27. package/lib/app/run-helpers.js +44 -12
  28. package/lib/app/run-reload-sync.js +148 -0
  29. package/lib/app/run-resolve-image.js +51 -1
  30. package/lib/app/run.js +99 -73
  31. package/lib/build/index.js +75 -45
  32. package/lib/cli/doctor-check.js +117 -0
  33. package/lib/cli/index.js +8 -2
  34. package/lib/cli/infra-guided.js +445 -0
  35. package/lib/cli/setup-app.js +20 -2
  36. package/lib/cli/setup-auth.js +26 -0
  37. package/lib/cli/setup-dev-path-commands.js +50 -3
  38. package/lib/cli/setup-infra.js +134 -61
  39. package/lib/cli/setup-integration-client.js +182 -0
  40. package/lib/cli/setup-parameters.js +21 -2
  41. package/lib/cli/setup-platform.js +102 -0
  42. package/lib/cli/setup-secrets.js +18 -6
  43. package/lib/cli/setup-utility.js +78 -33
  44. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  45. package/lib/commands/datasource-capability-output.js +29 -0
  46. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  47. package/lib/commands/datasource-capability.js +411 -0
  48. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  49. package/lib/commands/datasource.js +53 -13
  50. package/lib/commands/dev-down.js +3 -3
  51. package/lib/commands/dev-infra-gate.js +32 -0
  52. package/lib/commands/dev-init.js +13 -7
  53. package/lib/commands/dimension-value.js +179 -0
  54. package/lib/commands/dimension.js +330 -0
  55. package/lib/commands/integration-client.js +430 -0
  56. package/lib/commands/login-device.js +65 -30
  57. package/lib/commands/login.js +21 -10
  58. package/lib/commands/parameters-validate.js +78 -13
  59. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  60. package/lib/commands/repair-datasource-keys.js +10 -5
  61. package/lib/commands/repair-datasource.js +19 -7
  62. package/lib/commands/repair-env-template.js +4 -1
  63. package/lib/commands/repair-openapi-sync.js +172 -0
  64. package/lib/commands/repair-persist.js +102 -0
  65. package/lib/commands/repair-rbac-extract.js +27 -0
  66. package/lib/commands/repair-rbac-migrate.js +186 -0
  67. package/lib/commands/repair-rbac.js +214 -31
  68. package/lib/commands/repair-system-alignment.js +246 -0
  69. package/lib/commands/repair-system-permissions.js +168 -0
  70. package/lib/commands/repair.js +120 -338
  71. package/lib/commands/secure.js +1 -1
  72. package/lib/commands/setup-modes.js +455 -0
  73. package/lib/commands/setup-prompts.js +388 -0
  74. package/lib/commands/setup.js +149 -0
  75. package/lib/commands/teardown.js +228 -0
  76. package/lib/commands/up-common.js +79 -19
  77. package/lib/commands/up-dataplane.js +33 -11
  78. package/lib/commands/up-miso.js +7 -11
  79. package/lib/commands/upload.js +109 -23
  80. package/lib/commands/wizard-core-helpers.js +14 -11
  81. package/lib/commands/wizard-core.js +6 -5
  82. package/lib/commands/wizard-dataplane.js +2 -2
  83. package/lib/commands/wizard-entity-selection.js +4 -3
  84. package/lib/commands/wizard-headless.js +2 -1
  85. package/lib/commands/wizard.js +2 -1
  86. package/lib/constants/infra-compose-service-names.js +40 -0
  87. package/lib/core/env-reader.js +16 -3
  88. package/lib/core/secrets-admin-env.js +101 -0
  89. package/lib/core/secrets-ensure-infra.js +34 -1
  90. package/lib/core/secrets-ensure.js +88 -66
  91. package/lib/core/secrets-env-content.js +432 -0
  92. package/lib/core/secrets-env-write.js +27 -1
  93. package/lib/core/secrets-load.js +248 -0
  94. package/lib/core/secrets-names.js +32 -0
  95. package/lib/core/secrets.js +17 -757
  96. package/lib/datasource/capability/basic-exposure.js +76 -0
  97. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  98. package/lib/datasource/capability/capability-key.js +34 -0
  99. package/lib/datasource/capability/capability-resolve.js +172 -0
  100. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  101. package/lib/datasource/capability/copy-operations.js +348 -0
  102. package/lib/datasource/capability/copy-test-payload.js +139 -0
  103. package/lib/datasource/capability/create-operations.js +235 -0
  104. package/lib/datasource/capability/dimension-operations.js +151 -0
  105. package/lib/datasource/capability/dimension-validate.js +219 -0
  106. package/lib/datasource/capability/json-pointer.js +31 -0
  107. package/lib/datasource/capability/reference-rewrite.js +51 -0
  108. package/lib/datasource/capability/relate-operations.js +325 -0
  109. package/lib/datasource/capability/relate-validate.js +219 -0
  110. package/lib/datasource/capability/remove-operations.js +275 -0
  111. package/lib/datasource/capability/run-capability-copy.js +152 -0
  112. package/lib/datasource/capability/run-capability-diff.js +135 -0
  113. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  114. package/lib/datasource/capability/run-capability-edit.js +377 -0
  115. package/lib/datasource/capability/run-capability-relate.js +193 -0
  116. package/lib/datasource/capability/run-capability-remove.js +105 -0
  117. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  118. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  119. package/lib/datasource/list.js +136 -23
  120. package/lib/datasource/log-viewer.js +2 -4
  121. package/lib/datasource/unified-validation-run.js +51 -16
  122. package/lib/datasource/validate.js +53 -1
  123. package/lib/deployment/deploy-poll-ui.js +60 -0
  124. package/lib/deployment/deployer-status.js +29 -3
  125. package/lib/deployment/deployer.js +48 -30
  126. package/lib/deployment/environment.js +7 -2
  127. package/lib/deployment/poll-interval.js +72 -0
  128. package/lib/deployment/push.js +11 -9
  129. package/lib/external-system/deploy.js +4 -1
  130. package/lib/external-system/download.js +61 -32
  131. package/lib/external-system/sync-deploy-manifest.js +33 -0
  132. package/lib/infrastructure/index.js +49 -19
  133. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  134. package/lib/parameters/infra-kv-discovery.js +29 -4
  135. package/lib/parameters/infra-parameter-catalog.js +6 -3
  136. package/lib/parameters/infra-parameter-validate.js +67 -19
  137. package/lib/resolvers/datasource-resolver.js +53 -0
  138. package/lib/resolvers/dimension-file.js +52 -0
  139. package/lib/resolvers/manifest-resolver.js +133 -0
  140. package/lib/schema/external-datasource.schema.json +183 -53
  141. package/lib/schema/external-system.schema.json +23 -10
  142. package/lib/schema/infra.parameter.yaml +26 -11
  143. package/lib/schema/wizard-config.schema.json +1 -1
  144. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  145. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  146. package/lib/utils/app-run-containers.js +2 -2
  147. package/lib/utils/bash-secret-env.js +59 -0
  148. package/lib/utils/cli-secrets-error-format.js +78 -0
  149. package/lib/utils/cli-test-layout-chalk.js +31 -9
  150. package/lib/utils/cli-utils.js +4 -36
  151. package/lib/utils/datasource-test-run-display.js +8 -0
  152. package/lib/utils/dev-hosts-helper.js +3 -2
  153. package/lib/utils/dev-init-ssh-merge.js +2 -1
  154. package/lib/utils/docker-build.js +17 -9
  155. package/lib/utils/docker-reload-mount.js +127 -0
  156. package/lib/utils/external-readme.js +71 -2
  157. package/lib/utils/external-system-local-test-tty.js +3 -2
  158. package/lib/utils/external-system-readiness-core.js +45 -12
  159. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  160. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  161. package/lib/utils/external-system-readiness-display.js +10 -1
  162. package/lib/utils/file-upload.js +40 -3
  163. package/lib/utils/health-check-db-init.js +107 -0
  164. package/lib/utils/health-check-public-warn.js +69 -0
  165. package/lib/utils/health-check-url.js +19 -4
  166. package/lib/utils/health-check.js +135 -105
  167. package/lib/utils/help-builder.js +5 -1
  168. package/lib/utils/image-name.js +34 -7
  169. package/lib/utils/integration-file-backup.js +74 -0
  170. package/lib/utils/mutagen-install.js +30 -3
  171. package/lib/utils/paths.js +108 -25
  172. package/lib/utils/postgres-wipe.js +212 -0
  173. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  174. package/lib/utils/remote-dev-auth.js +21 -5
  175. package/lib/utils/remote-docker-env.js +9 -1
  176. package/lib/utils/remote-secrets-loader.js +42 -3
  177. package/lib/utils/resolve-docker-image-ref.js +9 -3
  178. package/lib/utils/secrets-ancestor-paths.js +47 -0
  179. package/lib/utils/secrets-helpers.js +17 -10
  180. package/lib/utils/secrets-kv-refs.js +42 -0
  181. package/lib/utils/secrets-kv-scope.js +19 -2
  182. package/lib/utils/secrets-materialize-local.js +134 -0
  183. package/lib/utils/secrets-path.js +24 -10
  184. package/lib/utils/secrets-utils.js +2 -2
  185. package/lib/utils/system-builder-root.js +34 -0
  186. package/lib/utils/url-declarative-resolve-build.js +6 -1
  187. package/lib/utils/url-declarative-runtime-base-path.js +32 -0
  188. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  189. package/lib/utils/urls-local-registry.js +23 -12
  190. package/lib/utils/validation-poll-ui.js +81 -0
  191. package/lib/utils/validation-run-poll.js +29 -5
  192. package/lib/utils/with-muted-logger.js +53 -0
  193. package/package.json +1 -1
  194. package/templates/applications/dataplane/application.yaml +1 -1
  195. package/templates/applications/dataplane/rbac.yaml +10 -10
  196. package/templates/applications/keycloak/env.template +8 -6
  197. package/templates/applications/miso-controller/application.yaml +7 -0
  198. package/templates/applications/miso-controller/env.template +1 -1
  199. package/templates/applications/miso-controller/rbac.yaml +9 -9
  200. package/templates/external-system/README.md.hbs +83 -123
  201. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  202. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  203. package/.nyc_output/processinfo/index.json +0 -1
  204. package/lib/api/service-users.api.js +0 -150
  205. package/lib/api/types/service-users.types.js +0 -65
  206. package/lib/cli/setup-service-user.js +0 -187
  207. package/lib/commands/service-user.js +0 -429
@@ -1,4 +1,9 @@
1
- const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
1
+ const {
2
+ formatProgress,
3
+ formatSuccessLine,
4
+ sectionTitle,
5
+ headerKeyValue
6
+ } = require('../utils/cli-test-layout-chalk');
2
7
  /**
3
8
  * CLI infrastructure command setup (up-infra, up-platform, up-miso, up-dataplane, down-infra, doctor, status, restart).
4
9
  *
@@ -10,7 +15,6 @@ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
10
15
  const chalk = require('chalk');
11
16
  const infra = require('../infrastructure');
12
17
  const appLib = require('../app');
13
- const validator = require('../validation/validator');
14
18
  const config = require('../core/config');
15
19
  const logger = require('../utils/logger');
16
20
  const { handleCommandError, isAuthenticationError } = require('../utils/cli-utils');
@@ -18,19 +22,29 @@ const { resolveControllerUrl } = require('../utils/controller-url');
18
22
  const { handleLogin } = require('../commands/login');
19
23
  const { handleUpMiso } = require('../commands/up-miso');
20
24
  const { handleUpDataplane } = require('../commands/up-dataplane');
21
- const { applyUpPlatformForceConfig, cleanBuilderAppDirs } = require('../commands/up-common');
25
+ const {
26
+ applyUpPlatformForceConfig,
27
+ cleanBuilderAppDirs,
28
+ prepareUrlsLocalRegistryForUpPlatform
29
+ } = require('../commands/up-common');
30
+ const { runGuidedUpInfra, runGuidedUpMiso, runGuidedUpDataplane, runGuidedUpPlatform, runGuidedDownInfra } = require('./infra-guided');
22
31
  const {
23
32
  loadInfraStatusSummary,
24
33
  formatInfraStatusTitleLine,
25
34
  logInfraStatusConfigurationSummary,
26
35
  logPaddedFieldRow
27
36
  } = require('../utils/infra-status-display');
37
+ const { runDoctorCheck } = require('./doctor-check');
38
+ const {
39
+ getRestartableInfraServiceNames,
40
+ buildRestartInfraHelpLines
41
+ } = require('../constants/infra-compose-service-names');
28
42
 
29
43
  const UP_INFRA_HELP_AFTER = `
30
44
  Typical sequence:
31
45
  $ aifabrix up-infra
32
46
  $ aifabrix up-platform
33
- Or: aifabrix up-miso, then aifabrix up-dataplane (login required for dataplane)
47
+ Or: aifabrix up-infra, aifabrix up-miso, then aifabrix up-dataplane (login required for dataplane)
34
48
 
35
49
  Full bootstrap example (Traefik, TLS flag, pgAdmin, catalog overrides — matches shipped defaults in infra.parameter.yaml):
36
50
  $ aifabrix up-infra --traefik --tls --adminPassword admin123 --adminEmail admin@aifabrix.dev --userPassword user123 --pgAdmin
@@ -59,16 +73,14 @@ async function persistTlsEnabledFlag(cfg, value) {
59
73
  await config.saveConfig(cfg);
60
74
  const tlsStr = value ? 'true' : 'false';
61
75
  const httpStr = value ? 'false' : 'true';
62
- logger.log(
63
- chalk.green(
64
- `✔ TLS mode ${value ? 'enabled' : 'disabled'} and saved to config (` +
65
- '${TLS_ENABLED}=' +
66
- tlsStr +
67
- ', ${HTTP_ENABLED}=' +
68
- httpStr +
69
- ' when generating deployment JSON)'
70
- )
71
- );
76
+ logger.log(formatSuccessLine(
77
+ `TLS mode ${value ? 'enabled' : 'disabled'} and saved to config (` +
78
+ '${TLS_ENABLED}=' +
79
+ tlsStr +
80
+ ', ${HTTP_ENABLED}=' +
81
+ httpStr +
82
+ ' when generating deployment JSON)'
83
+ ));
72
84
  }
73
85
 
74
86
  /**
@@ -152,11 +164,27 @@ async function runUpInfraCommand(options) {
152
164
  });
153
165
  }
154
166
 
167
+ async function maybeHandleUpPlatformAuthRetry(error, options) {
168
+ if (!isAuthenticationError(error)) {
169
+ return false;
170
+ }
171
+ const controllerUrl = error.controllerUrl || await resolveControllerUrl();
172
+ if (!options.verbose) {
173
+ logger.log(formatProgress('Authenticating...'));
174
+ } else {
175
+ logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
176
+ }
177
+ await handleLogin({ method: 'device', controller: controllerUrl, compact: !options.verbose });
178
+ await handleUpDataplane(options);
179
+ return true;
180
+ }
181
+
155
182
  function setupUpInfraCommand(program) {
156
183
  program.command('up-infra')
157
184
  .description('Start Postgres, Redis; optional pgAdmin, Redis Commander, Traefik')
158
185
  .addHelpText('after', UP_INFRA_HELP_AFTER)
159
186
  .option('-d, --developer <id>', 'Set developer ID and start infrastructure')
187
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
160
188
  .option('--adminPassword <password>', 'Override {{adminPassword}} defaults (Postgres, pgAdmin, Redis Commander, catalog literals)')
161
189
  .option('--adminEmail <email>', 'Override {{adminEmail}} default (e.g. pgAdmin login email)')
162
190
  .option('--userPassword <password>', 'Override {{userPassword}} default (e.g. Keycloak default user password)')
@@ -170,6 +198,11 @@ function setupUpInfraCommand(program) {
170
198
  .option('--no-tls', 'Disable TLS mode (${TLS_ENABLED}=false, ${HTTP_ENABLED}=true)')
171
199
  .action(async(options) => {
172
200
  try {
201
+ if (!options.verbose) {
202
+ await runGuidedUpInfra(options, runUpInfraCommand);
203
+ return;
204
+ }
205
+
173
206
  await runUpInfraCommand(options);
174
207
  } catch (error) {
175
208
  handleCommandError(error, 'up-infra');
@@ -184,30 +217,47 @@ function setupUpPlatformCommand(program) {
184
217
  .option('-r, --registry <url>', 'Override registry for all apps (e.g. myacr.azurecr.io)')
185
218
  .option('--registry-mode <mode>', 'Override registry mode (acr|external)')
186
219
  .option('-i, --image <key>=<value>', 'Override image (e.g. keycloak=myreg/k:v1, miso-controller=myreg/m:v1, dataplane=myreg/d:v1); can be repeated', (v, prev) => (prev || []).concat([v]))
220
+ .option('--base', 'Resolve platform images from manifest only (default: true)', true)
221
+ .option('--verbose', 'Show full orchestration output (default is guided installer)')
187
222
  .option(
188
223
  '-f, --force',
189
224
  'Reset CLI auth (clear all device/client tokens, set environment to dev, set default controller URL from developer-id), clean builder/keycloak, builder/miso-controller, builder/dataplane, then re-fetch from templates'
190
225
  )
191
226
  .action(async(options) => {
192
227
  try {
228
+ let platformForceCleanSummary = null;
193
229
  if (options.force) {
194
- await applyUpPlatformForceConfig();
195
- await cleanBuilderAppDirs(['keycloak', 'miso-controller', 'dataplane']);
230
+ const forceSummary = await applyUpPlatformForceConfig({ silent: !options.verbose });
231
+ const cleanedApps = await cleanBuilderAppDirs(['keycloak', 'miso-controller', 'dataplane'], {
232
+ silent: !options.verbose
233
+ });
234
+ if (!options.verbose) {
235
+ platformForceCleanSummary = { forceSummary, cleanedApps };
236
+ }
237
+ }
238
+
239
+ if (!options.verbose) {
240
+ await runGuidedUpPlatform(
241
+ options,
242
+ handleUpMiso,
243
+ handleUpDataplane,
244
+ handleLogin,
245
+ platformForceCleanSummary
246
+ );
247
+ return;
196
248
  }
249
+
250
+ await prepareUrlsLocalRegistryForUpPlatform();
197
251
  await handleUpMiso(options);
198
252
  await handleUpDataplane(options);
199
253
  } catch (error) {
200
- if (isAuthenticationError(error)) {
201
- const controllerUrl = error.controllerUrl || await resolveControllerUrl();
202
- logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
203
- try {
204
- await handleLogin({ method: 'device', controller: controllerUrl });
205
- await handleUpDataplane(options);
254
+ try {
255
+ if (await maybeHandleUpPlatformAuthRetry(error, options)) {
206
256
  return;
207
- } catch (loginOrRetryError) {
208
- handleCommandError(loginOrRetryError, 'up-platform');
209
- process.exit(1);
210
257
  }
258
+ } catch (loginOrRetryError) {
259
+ handleCommandError(loginOrRetryError, 'up-platform');
260
+ process.exit(1);
211
261
  }
212
262
  handleCommandError(error, 'up-platform');
213
263
  process.exit(1);
@@ -221,12 +271,19 @@ function setupUpMisoCommand(program) {
221
271
  .option('-r, --registry <url>', 'Override registry for all apps (e.g. myacr.azurecr.io)')
222
272
  .option('--registry-mode <mode>', 'Override registry mode (acr|external)')
223
273
  .option('-i, --image <key>=<value>', 'Override image (e.g. keycloak=myreg/k:v1, miso-controller=myreg/m:v1); can be repeated', (v, prev) => (prev || []).concat([v]))
274
+ .option('--base', 'Resolve platform images from manifest only (default: true)', true)
224
275
  .option('-f, --force', 'Clean builder/keycloak and builder/miso-controller and re-fetch from templates')
276
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
225
277
  .action(async(options) => {
226
278
  try {
227
279
  if (options.force) {
228
280
  await cleanBuilderAppDirs(['keycloak', 'miso-controller']);
229
281
  }
282
+ if (!options.verbose) {
283
+ await runGuidedUpMiso(options, handleUpMiso);
284
+ return;
285
+ }
286
+
230
287
  await handleUpMiso(options);
231
288
  } catch (error) {
232
289
  handleCommandError(error, 'up-miso');
@@ -237,23 +294,34 @@ function setupUpMisoCommand(program) {
237
294
 
238
295
  function setupUpDataplaneCommand(program) {
239
296
  program.command('up-dataplane')
240
- .description('Register, deploy, run dataplane locally (dev env; login required)')
297
+ .description('Register, deploy, run dataplane locally (dev env; needs up-infra; login required)')
241
298
  .option('-r, --registry <url>', 'Override registry for dataplane image')
242
299
  .option('--registry-mode <mode>', 'Override registry mode (acr|external)')
243
300
  .option('-i, --image <ref>', 'Override dataplane image reference (e.g. myreg/dataplane:latest)')
301
+ .option('--base', 'Resolve dataplane image from manifest only when running locally (default: true)', true)
244
302
  .option('-f, --force', 'Clean builder/dataplane and re-fetch from templates')
303
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
245
304
  .action(async(options) => {
246
305
  try {
247
306
  if (options.force) {
248
307
  await cleanBuilderAppDirs(['dataplane']);
249
308
  }
309
+ if (!options.verbose) {
310
+ await runGuidedUpDataplane(options, handleUpDataplane);
311
+ return;
312
+ }
313
+
250
314
  await handleUpDataplane(options);
251
315
  } catch (error) {
252
316
  if (isAuthenticationError(error)) {
253
317
  const controllerUrl = error.controllerUrl || await resolveControllerUrl();
254
- logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
318
+ if (!options.verbose) {
319
+ logger.log(formatProgress('Authenticating...'));
320
+ } else {
321
+ logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
322
+ }
255
323
  try {
256
- await handleLogin({ method: 'device', controller: controllerUrl });
324
+ await handleLogin({ method: 'device', controller: controllerUrl, compact: !options.verbose });
257
325
  await handleUpDataplane(options);
258
326
  return;
259
327
  } catch (loginOrRetryError) {
@@ -271,8 +339,14 @@ function setupDownInfraCommand(program) {
271
339
  program.command('down-infra [service|app]')
272
340
  .description('Stop all infra, or stop one app; use -v to remove volumes')
273
341
  .option('-v, --volumes', 'Remove volumes (deletes all data)')
342
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
274
343
  .action(async(appName, options) => {
275
344
  try {
345
+ if (!options.verbose) {
346
+ await runGuidedDownInfra(appName, options, infra, appLib);
347
+ return;
348
+ }
349
+
276
350
  if (typeof appName === 'string' && appName.trim().length > 0) {
277
351
  await appLib.downApp(appName, { volumes: !!options.volumes });
278
352
  } else {
@@ -291,33 +365,7 @@ function setupDoctorCommand(program) {
291
365
  .description('Check Docker, ports, secrets, and infra health')
292
366
  .action(async() => {
293
367
  try {
294
- const result = await validator.checkEnvironment();
295
- logger.log('\n🔍 AI Fabrix Environment Check\n');
296
- logger.log(`Docker: ${result.docker === 'ok' ? '✔ Running' : '✖ Not available'}`);
297
- logger.log(`Ports: ${result.ports === 'ok' ? '✔ Available' : '⚠ Some ports in use'}`);
298
- logger.log(`Secrets: ${result.secrets === 'ok' ? '✔ Configured' : '✖ Missing'}`);
299
- if (result.recommendations.length > 0) {
300
- logger.log('\n📋 Recommendations:');
301
- result.recommendations.forEach(rec => logger.log(` • ${rec}`));
302
- }
303
- if (result.docker === 'ok') {
304
- try {
305
- const cfg = await config.getConfig();
306
- const health = await infra.checkInfraHealth(null, {
307
- pgadmin: cfg.pgadmin !== false,
308
- redisCommander: cfg.redisCommander !== false,
309
- traefik: !!cfg.traefik
310
- });
311
- logger.log('\n🏥 Infrastructure Health:');
312
- Object.entries(health).forEach(([service, status]) => {
313
- const icon = status === 'healthy' ? '✔' : status === 'unknown' ? '❓' : '✖';
314
- logger.log(` ${icon} ${service}: ${status}`);
315
- });
316
- } catch (error) {
317
- logger.log('\n🏥 Infrastructure: Not running');
318
- }
319
- }
320
- logger.log('');
368
+ await runDoctorCheck();
321
369
  } catch (error) {
322
370
  handleCommandError(error, 'doctor');
323
371
  process.exit(1);
@@ -365,19 +413,44 @@ function setupStatusCommand(program) {
365
413
  });
366
414
  }
367
415
 
368
- const INFRA_SERVICES = ['postgres', 'redis', 'pgadmin', 'redis-commander', 'traefik'];
416
+ const RESTART_INFRA_SERVICE_NAMES = getRestartableInfraServiceNames();
417
+
418
+ const RESTART_COMMAND_HELP_AFTER = `
419
+ Infra — use these exact Docker Compose service names:
420
+ ${buildRestartInfraHelpLines()}
421
+
422
+ Optional services (pgadmin, redis-commander, traefik) only exist if you enabled them when running up-infra.
423
+
424
+ Builder apps (examples):
425
+ keycloak, miso-controller, dataplane
426
+
427
+ Examples:
428
+ $ aifabrix restart postgres
429
+ $ aifabrix restart traefik
430
+ $ aifabrix restart miso-controller
431
+ `;
369
432
 
370
433
  function setupRestartCommand(program) {
371
434
  program.command('restart <service|app>')
372
- .description('Restart infra service (postgres, redis, …) or a builder/<app> container')
435
+ .description(
436
+ 'Restart one infra compose service (postgres, redis, pgadmin, redis-commander, traefik) or a builder app container'
437
+ )
438
+ .addHelpText('after', RESTART_COMMAND_HELP_AFTER)
373
439
  .action(async(service) => {
374
440
  try {
375
- if (INFRA_SERVICES.includes(service)) {
376
- await infra.restartService(service);
377
- logger.log(`✔ ${service} service restarted successfully`);
441
+ if (RESTART_INFRA_SERVICE_NAMES.includes(service)) {
442
+ logger.log('');
443
+ logger.log(sectionTitle('Restart'));
444
+ logger.log(headerKeyValue('Infra service:', service));
445
+ logger.log(formatProgress(`Restarting ${service}…`));
446
+ await infra.restartService(service, { suppressProgressLog: true });
447
+ logger.log(formatSuccessLine(`${service} restarted successfully`));
378
448
  } else {
449
+ logger.log('');
450
+ logger.log(sectionTitle('Restart'));
451
+ logger.log(headerKeyValue('Application:', service));
379
452
  await appLib.restartApp(service);
380
- logger.log(`✔ ${service} restarted successfully`);
453
+ logger.log(formatSuccessLine(`${service} restarted successfully`));
381
454
  }
382
455
  } catch (error) {
383
456
  handleCommandError(error, 'restart');
@@ -0,0 +1,182 @@
1
+ /**
2
+ * CLI integration-client command setup (Controller OAuth / API clients).
3
+ *
4
+ * @fileoverview Integration client CLI definitions
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ const { handleCommandError } = require('../utils/cli-utils');
10
+ const {
11
+ runIntegrationClientCreate,
12
+ runIntegrationClientList,
13
+ runIntegrationClientRotateSecret,
14
+ runIntegrationClientDelete,
15
+ runIntegrationClientUpdateGroups,
16
+ runIntegrationClientUpdateRedirectUris
17
+ } = require('../commands/integration-client');
18
+
19
+ const HELP_AFTER = `
20
+ Integration clients are machine identities for integrations, CI, Postman OAuth2, or API access.
21
+ Use: list (integration-client:read), create (integration-client:create), rotate-secret,
22
+ update-groups, update-redirect-uris (integration-client:update), delete (integration-client:delete).
23
+ The controller returns a one-time clientSecret on create and rotate-secret—save it immediately;
24
+ it cannot be retrieved again.
25
+
26
+ Examples:
27
+ $ aifabrix integration-client create --key postman --display-name "Postman client" \\
28
+ --redirect-uris https://oauth.pstmn.io/v1/callback --group-names AI-Fabrix-Platform-Admins
29
+ $ aifabrix integration-client list
30
+ $ aifabrix integration-client rotate-secret --id <uuid>
31
+ $ aifabrix integration-client delete --id <uuid>
32
+ $ aifabrix integration-client update-groups --id <uuid> --group-names Group1,Group2
33
+ $ aifabrix integration-client update-redirect-uris --id <uuid> --redirect-uris https://app.example.com/callback
34
+
35
+ Run "aifabrix login" first.`;
36
+
37
+ function parseOptionalInt(val) {
38
+ return (val !== undefined && val !== null) ? parseInt(val, 10) : undefined;
39
+ }
40
+
41
+ function addCreateCommand(integrationClient) {
42
+ integrationClient.command('create')
43
+ .description('Create an integration client and receive a one-time clientSecret (save it now; it will not be shown again)')
44
+ .option('--controller <url>', 'Controller base URL (default: from config)')
45
+ .option('-k, --key <key>', 'Stable key (lowercase alphanumeric and hyphens; required)')
46
+ .option('-n, --display-name <name>', 'Display name (required)')
47
+ .option('--keycloak-client-id <id>', 'Optional fixed Keycloak client id (when omitted, the server assigns one)')
48
+ .option('--redirect-uris <uris>', 'Comma-separated OAuth2 redirect URIs (required)')
49
+ .option('--group-names <names>', 'Comma-separated group names (optional; omit for OAuth-only clients)')
50
+ .option('-d, --description <description>', 'Optional description')
51
+ .action(async(options) => {
52
+ try {
53
+ await runIntegrationClientCreate({
54
+ controller: options.controller,
55
+ key: options.key,
56
+ displayName: options.displayName,
57
+ keycloakClientId: options.keycloakClientId,
58
+ redirectUris: options.redirectUris,
59
+ groupNames: options.groupNames,
60
+ description: options.description
61
+ });
62
+ } catch (error) {
63
+ handleCommandError(error, 'integration-client create');
64
+ process.exit(1);
65
+ }
66
+ });
67
+ }
68
+
69
+ function addListCommand(integrationClient) {
70
+ integrationClient.command('list')
71
+ .description('List integration clients (supports pagination and search)')
72
+ .option('--controller <url>', 'Controller base URL (default: from config)')
73
+ .option('--page <n>', 'Page number')
74
+ .option('--page-size <n>', 'Items per page')
75
+ .option('--search <term>', 'Search term')
76
+ .option('--sort <field>', 'Sort field/direction')
77
+ .option('--filter <expr>', 'Filter expression')
78
+ .action(async(options) => {
79
+ try {
80
+ await runIntegrationClientList({
81
+ controller: options.controller,
82
+ page: parseOptionalInt(options.page),
83
+ pageSize: parseOptionalInt(options.pageSize),
84
+ search: options.search,
85
+ sort: options.sort,
86
+ filter: options.filter
87
+ });
88
+ } catch (error) {
89
+ handleCommandError(error, 'integration-client list');
90
+ process.exit(1);
91
+ }
92
+ });
93
+ }
94
+
95
+ function addRotateSecretCommand(integrationClient) {
96
+ integrationClient.command('rotate-secret')
97
+ .description('Rotate (regenerate) secret for an integration client; new secret shown once only')
98
+ .option('--controller <url>', 'Controller base URL (default: from config)')
99
+ .option('--id <uuid>', 'Integration client ID (required)')
100
+ .action(async(options) => {
101
+ try {
102
+ await runIntegrationClientRotateSecret({ controller: options.controller, id: options.id });
103
+ } catch (error) {
104
+ handleCommandError(error, 'integration-client rotate-secret');
105
+ process.exit(1);
106
+ }
107
+ });
108
+ }
109
+
110
+ function addDeleteCommand(integrationClient) {
111
+ integrationClient.command('delete')
112
+ .description('Delete (deactivate) an integration client')
113
+ .option('--controller <url>', 'Controller base URL (default: from config)')
114
+ .option('--id <uuid>', 'Integration client ID (required)')
115
+ .action(async(options) => {
116
+ try {
117
+ await runIntegrationClientDelete({ controller: options.controller, id: options.id });
118
+ } catch (error) {
119
+ handleCommandError(error, 'integration-client delete');
120
+ process.exit(1);
121
+ }
122
+ });
123
+ }
124
+
125
+ function addUpdateGroupsCommand(integrationClient) {
126
+ integrationClient.command('update-groups')
127
+ .description('Update group assignments for an integration client')
128
+ .option('--controller <url>', 'Controller base URL (default: from config)')
129
+ .option('--id <uuid>', 'Integration client ID (required)')
130
+ .option('--group-names <names>', 'Comma-separated group names (required)')
131
+ .action(async(options) => {
132
+ try {
133
+ await runIntegrationClientUpdateGroups({
134
+ controller: options.controller,
135
+ id: options.id,
136
+ groupNames: options.groupNames
137
+ });
138
+ } catch (error) {
139
+ handleCommandError(error, 'integration-client update-groups');
140
+ process.exit(1);
141
+ }
142
+ });
143
+ }
144
+
145
+ function addUpdateRedirectUrisCommand(integrationClient) {
146
+ integrationClient.command('update-redirect-uris')
147
+ .description('Update redirect URIs for an integration client (min 1)')
148
+ .option('--controller <url>', 'Controller base URL (default: from config)')
149
+ .option('--id <uuid>', 'Integration client ID (required)')
150
+ .option('--redirect-uris <uris>', 'Comma-separated redirect URIs (required, min 1)')
151
+ .action(async(options) => {
152
+ try {
153
+ await runIntegrationClientUpdateRedirectUris({
154
+ controller: options.controller,
155
+ id: options.id,
156
+ redirectUris: options.redirectUris
157
+ });
158
+ } catch (error) {
159
+ handleCommandError(error, 'integration-client update-redirect-uris');
160
+ process.exit(1);
161
+ }
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Registers integration-client commands
167
+ * @param {Command} program - Commander program instance
168
+ */
169
+ function setupIntegrationClientCommands(program) {
170
+ const integrationClient = program
171
+ .command('integration-client')
172
+ .description('OAuth integration clients on Controller (integrations, CI, API access)')
173
+ .addHelpText('after', HELP_AFTER);
174
+ addCreateCommand(integrationClient);
175
+ addListCommand(integrationClient);
176
+ addRotateSecretCommand(integrationClient);
177
+ addDeleteCommand(integrationClient);
178
+ addUpdateGroupsCommand(integrationClient);
179
+ addUpdateRedirectUrisCommand(integrationClient);
180
+ }
181
+
182
+ module.exports = { setupIntegrationClientCommands };
@@ -17,11 +17,30 @@ function setupParametersCommands(program) {
17
17
 
18
18
  parameters
19
19
  .command('validate')
20
- .description('Check env.template kv:// references against lib/schema/infra.parameter.yaml')
20
+ .description(
21
+ 'Validate builder/*/env.template kv:// references against infra.parameter.yaml'
22
+ )
21
23
  .option('--catalog <path>', 'Override path to infra.parameter.yaml')
24
+ .option('--verbose', 'Print scanned files and scan summary')
25
+ .addHelpText(
26
+ 'after',
27
+ `
28
+ Examples:
29
+ aifabrix parameters validate
30
+ aifabrix parameters validate --verbose
31
+ aifabrix parameters validate --catalog ./lib/schema/infra.parameter.yaml
32
+
33
+ Notes:
34
+ - Scans builder/* apps only (integration/* is intentionally skipped).
35
+ - Extracts kv://KEY references from env.template and checks KEY coverage in the catalog.
36
+ `
37
+ )
22
38
  .action(async(opts) => {
23
39
  try {
24
- const result = await handleParametersValidate({ catalogPath: opts.catalog });
40
+ const result = await handleParametersValidate({
41
+ catalogPath: opts.catalog,
42
+ verbose: Boolean(opts.verbose)
43
+ });
25
44
  if (!result.valid) process.exit(1);
26
45
  } catch (error) {
27
46
  handleCommandError(error, 'parameters validate');
@@ -0,0 +1,102 @@
1
+ /**
2
+ * CLI registration for `aifabrix setup` and `aifabrix teardown`.
3
+ *
4
+ * `setup` is the one-shot platform installer. When no infrastructure is
5
+ * detected it runs a small wizard (admin email/password, AI tool keys) and
6
+ * then `up-infra` + `up-platform`. When infra is already up, it offers a
7
+ * mode menu (re-install, wipe data, clean install files, update images).
8
+ *
9
+ * `teardown` is the symmetrical inverse of `setup`: `down-infra -v` plus a
10
+ * full clean of the AI Fabrix system directory (everything except
11
+ * `config.yaml`).
12
+ *
13
+ * @fileoverview CLI command setup for setup/teardown
14
+ * @author AI Fabrix Team
15
+ * @version 2.0.0
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ const { handleCommandError } = require('../utils/cli-utils');
21
+ const { handleSetup } = require('../commands/setup');
22
+ const { handleTeardown } = require('../commands/teardown');
23
+
24
+ const SETUP_HELP_AFTER = `
25
+ What this command does:
26
+ - With no infra running: prompts for admin email/password (used for Postgres, pgAdmin, Keycloak), prompts for an AI tool (OpenAI or Azure OpenAI) only when keys are not already set, then runs up-infra and up-platform.
27
+ - With infra running: shows a mode menu:
28
+ 1) Re-install — stop infra and remove all volumes, then up-infra + up-platform --force.
29
+ 2) Wipe data — drop every database and DB user, then up-infra + up-platform --force.
30
+ 3) Clean installation files — remove user-local secrets, then up-infra + up-platform --force.
31
+ 4) Update images — pull the latest infra and platform images, then up-infra + up-platform.
32
+
33
+ AI tool keys are read from the user-local file (~/.aifabrix/secrets.local.yaml) merged with the shared aifabrix-secrets file. If either source already provides keys, the AI tool prompt is skipped.
34
+
35
+ Use 'aifabrix teardown' to fully remove the local installation.
36
+ `;
37
+
38
+ const TEARDOWN_HELP_AFTER = `
39
+ What this command does:
40
+ - Runs down-infra -v (stops all infra + apps, removes every Docker volume).
41
+ - Removes every file and subfolder inside ~/.aifabrix/ EXCEPT config.yaml.
42
+ This includes secrets.local.yaml, admin-secrets.env, auth/token files,
43
+ and infra-dev*/ directories.
44
+
45
+ This is the symmetrical inverse of 'aifabrix setup'. Use --yes / -y to skip the confirmation prompt in CI.
46
+ `;
47
+
48
+ /**
49
+ * Register the setup command on the Commander program.
50
+ * @param {import('commander').Command} program
51
+ */
52
+ function registerSetup(program) {
53
+ program
54
+ .command('setup')
55
+ .description('Install or refresh the full AI Fabrix platform (infra + miso + dataplane)')
56
+ .option('-d, --developer <id>', 'Pin developer ID before fresh install (fresh path only; ignored when infra is already up)')
57
+ .option('-y, --yes', 'Skip destructive confirmation prompts (re-install, wipe data, teardown)')
58
+ .addHelpText('after', SETUP_HELP_AFTER)
59
+ .action(async(options) => {
60
+ try {
61
+ await handleSetup(options || {});
62
+ } catch (error) {
63
+ handleCommandError(error, 'setup');
64
+ process.exit(1);
65
+ }
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Register the teardown command on the Commander program.
71
+ * @param {import('commander').Command} program
72
+ */
73
+ function registerTeardown(program) {
74
+ program
75
+ .command('teardown')
76
+ .description('Tear down local AI Fabrix infra and clean ~/.aifabrix/ except config.yaml')
77
+ .option('-y, --yes', 'Skip the confirmation prompt')
78
+ .addHelpText('after', TEARDOWN_HELP_AFTER)
79
+ .action(async(options) => {
80
+ try {
81
+ await handleTeardown(options || {});
82
+ } catch (error) {
83
+ handleCommandError(error, 'teardown');
84
+ process.exit(1);
85
+ }
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Register all setup-platform commands on the Commander program.
91
+ * @param {import('commander').Command} program
92
+ */
93
+ function setupPlatformCommands(program) {
94
+ registerSetup(program);
95
+ registerTeardown(program);
96
+ }
97
+
98
+ module.exports = {
99
+ setupPlatformCommands,
100
+ registerSetup,
101
+ registerTeardown
102
+ };