@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
@@ -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
  *
@@ -7,30 +12,36 @@ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
7
12
  * @version 2.0.0
8
13
  */
9
14
 
10
- const chalk = require('chalk');
11
15
  const infra = require('../infrastructure');
12
16
  const appLib = require('../app');
13
- const validator = require('../validation/validator');
14
17
  const config = require('../core/config');
15
18
  const logger = require('../utils/logger');
16
- const { handleCommandError, isAuthenticationError } = require('../utils/cli-utils');
17
- const { resolveControllerUrl } = require('../utils/controller-url');
18
- const { handleLogin } = require('../commands/login');
19
+ const { handleCommandError } = require('../utils/cli-utils');
19
20
  const { handleUpMiso } = require('../commands/up-miso');
20
- const { handleUpDataplane } = require('../commands/up-dataplane');
21
- const { applyUpPlatformForceConfig, cleanBuilderAppDirs } = require('../commands/up-common');
21
+ const { cleanBuilderAppDirs } = require('../commands/up-common');
22
+ const { runGuidedUpInfra, runGuidedUpMiso, runGuidedDownInfra } = require('./infra-guided');
22
23
  const {
23
24
  loadInfraStatusSummary,
24
25
  formatInfraStatusTitleLine,
25
26
  logInfraStatusConfigurationSummary,
26
27
  logPaddedFieldRow
27
28
  } = require('../utils/infra-status-display');
29
+ const { runDoctorCheck } = require('./doctor-check');
30
+ const {
31
+ getRestartableInfraServiceNames,
32
+ buildRestartInfraHelpLines
33
+ } = require('../constants/infra-compose-service-names');
34
+
35
+ const {
36
+ computeEffectiveInfraOptionalFlags,
37
+ persistMissingInfraOptionalServiceFlags
38
+ } = require('../utils/infra-optional-service-flags');
28
39
 
29
40
  const UP_INFRA_HELP_AFTER = `
30
41
  Typical sequence:
31
42
  $ aifabrix up-infra
32
43
  $ aifabrix up-platform
33
- Or: aifabrix up-miso, then aifabrix up-dataplane (login required for dataplane)
44
+ Or: aifabrix up-infra, aifabrix up-miso, then aifabrix up-dataplane (login required for dataplane)
34
45
 
35
46
  Full bootstrap example (Traefik, TLS flag, pgAdmin, catalog overrides — matches shipped defaults in infra.parameter.yaml):
36
47
  $ aifabrix up-infra --traefik --tls --adminPassword admin123 --adminEmail admin@aifabrix.dev --userPassword user123 --pgAdmin
@@ -59,29 +70,14 @@ async function persistTlsEnabledFlag(cfg, value) {
59
70
  await config.saveConfig(cfg);
60
71
  const tlsStr = value ? 'true' : 'false';
61
72
  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
- );
72
- }
73
-
74
- /**
75
- * Resolves effective boolean from option vs config.
76
- * @param {*} optValue - options.traefik | options.pgAdmin | options.redisAdmin
77
- * @param {*} cfgValue - cfg.traefik | cfg.pgadmin | cfg.redisCommander
78
- * @param {boolean} defaultWhenUndef - Default when config value is undefined
79
- * @returns {boolean}
80
- */
81
- function resolveFlag(optValue, cfgValue, defaultWhenUndef = true) {
82
- if (optValue === true) return true;
83
- if (optValue === false) return false;
84
- return cfgValue !== false && (cfgValue === true || defaultWhenUndef);
73
+ logger.log(formatSuccessLine(
74
+ `TLS mode ${value ? 'enabled' : 'disabled'} and saved to config (` +
75
+ '${TLS_ENABLED}=' +
76
+ tlsStr +
77
+ ', ${HTTP_ENABLED}=' +
78
+ httpStr +
79
+ ' when generating deployment JSON)'
80
+ ));
85
81
  }
86
82
 
87
83
  /**
@@ -140,16 +136,18 @@ async function runUpInfraCommand(options) {
140
136
  await maybePersistTlsFromUpInfra(options, cfg);
141
137
  const adminPass = options.adminPassword || options.adminPwd;
142
138
  const tlsEnabledEffective = cfg.tlsEnabled === true;
139
+ const effective = computeEffectiveInfraOptionalFlags(cfg, options);
143
140
  await infra.startInfra(developerId, {
144
- traefik: resolveFlag(options.traefik, cfg.traefik, false),
145
- pgadmin: resolveFlag(options.pgAdmin, cfg.pgadmin, true),
146
- redisCommander: resolveFlag(options.redisAdmin, cfg.redisCommander, true),
141
+ traefik: effective.traefik,
142
+ pgadmin: effective.pgadmin,
143
+ redisCommander: effective.redisCommander,
147
144
  adminPassword: adminPass,
148
145
  adminPwd: adminPass,
149
146
  adminEmail: options.adminEmail,
150
147
  userPassword: options.userPassword,
151
148
  tlsEnabled: tlsEnabledEffective
152
149
  });
150
+ await persistMissingInfraOptionalServiceFlags(cfg, effective);
153
151
  }
154
152
 
155
153
  function setupUpInfraCommand(program) {
@@ -157,6 +155,7 @@ function setupUpInfraCommand(program) {
157
155
  .description('Start Postgres, Redis; optional pgAdmin, Redis Commander, Traefik')
158
156
  .addHelpText('after', UP_INFRA_HELP_AFTER)
159
157
  .option('-d, --developer <id>', 'Set developer ID and start infrastructure')
158
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
160
159
  .option('--adminPassword <password>', 'Override {{adminPassword}} defaults (Postgres, pgAdmin, Redis Commander, catalog literals)')
161
160
  .option('--adminEmail <email>', 'Override {{adminEmail}} default (e.g. pgAdmin login email)')
162
161
  .option('--userPassword <password>', 'Override {{userPassword}} default (e.g. Keycloak default user password)')
@@ -169,9 +168,28 @@ function setupUpInfraCommand(program) {
169
168
  .option('--tls', 'Enable TLS mode; save tlsEnabled (${TLS_ENABLED}=true, ${HTTP_ENABLED}=false in application.yaml)')
170
169
  .option('--no-tls', 'Disable TLS mode (${TLS_ENABLED}=false, ${HTTP_ENABLED}=true)')
171
170
  .action(async(options) => {
171
+ const startedAt = new Date();
172
+ const { recordInfraInstallationCommand } = require('./installation-log-command');
172
173
  try {
173
- await runUpInfraCommand(options);
174
+ if (!options.verbose) {
175
+ await runGuidedUpInfra(options, runUpInfraCommand);
176
+ } else {
177
+ await runUpInfraCommand(options);
178
+ }
179
+ await recordInfraInstallationCommand({
180
+ command: 'up-infra',
181
+ options,
182
+ startedAt,
183
+ outcome: 'success'
184
+ });
174
185
  } catch (error) {
186
+ await recordInfraInstallationCommand({
187
+ command: 'up-infra',
188
+ options,
189
+ startedAt,
190
+ outcome: 'failure',
191
+ error
192
+ });
175
193
  handleCommandError(error, 'up-infra');
176
194
  process.exit(1);
177
195
  }
@@ -184,34 +202,15 @@ function setupUpPlatformCommand(program) {
184
202
  .option('-r, --registry <url>', 'Override registry for all apps (e.g. myacr.azurecr.io)')
185
203
  .option('--registry-mode <mode>', 'Override registry mode (acr|external)')
186
204
  .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]))
205
+ .option('--base', 'Resolve platform images from manifest only (default: true)', true)
206
+ .option('--verbose', 'Show full orchestration output (default is guided installer)')
187
207
  .option(
188
208
  '-f, --force',
189
209
  '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
210
  )
191
- .action(async(options) => {
192
- try {
193
- if (options.force) {
194
- await applyUpPlatformForceConfig();
195
- await cleanBuilderAppDirs(['keycloak', 'miso-controller', 'dataplane']);
196
- }
197
- await handleUpMiso(options);
198
- await handleUpDataplane(options);
199
- } 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);
206
- return;
207
- } catch (loginOrRetryError) {
208
- handleCommandError(loginOrRetryError, 'up-platform');
209
- process.exit(1);
210
- }
211
- }
212
- handleCommandError(error, 'up-platform');
213
- process.exit(1);
214
- }
211
+ .action((options) => {
212
+ const { handleUpPlatformCliAction } = require('./setup-infra-up-platform-action');
213
+ return handleUpPlatformCliAction(options);
215
214
  });
216
215
  }
217
216
 
@@ -221,14 +220,41 @@ function setupUpMisoCommand(program) {
221
220
  .option('-r, --registry <url>', 'Override registry for all apps (e.g. myacr.azurecr.io)')
222
221
  .option('--registry-mode <mode>', 'Override registry mode (acr|external)')
223
222
  .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]))
223
+ .option('--base', 'Resolve platform images from manifest only (default: true)', true)
224
224
  .option('-f, --force', 'Clean builder/keycloak and builder/miso-controller and re-fetch from templates')
225
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
225
226
  .action(async(options) => {
227
+ const startedAt = new Date();
228
+ const { recordInfraInstallationCommand } = require('./installation-log-command');
229
+ let cleanedAppKeys = [];
226
230
  try {
227
231
  if (options.force) {
228
- await cleanBuilderAppDirs(['keycloak', 'miso-controller']);
232
+ const c = await cleanBuilderAppDirs(['keycloak', 'miso-controller']);
233
+ cleanedAppKeys = Array.isArray(c) ? c : [];
234
+ }
235
+ if (!options.verbose) {
236
+ await runGuidedUpMiso(options, handleUpMiso);
237
+ } else {
238
+ await handleUpMiso(options);
229
239
  }
230
- await handleUpMiso(options);
240
+ await recordInfraInstallationCommand({
241
+ command: 'up-miso',
242
+ options,
243
+ startedAt,
244
+ outcome: 'success',
245
+ platformAppList: ['keycloak', 'miso-controller'],
246
+ cleanup: cleanedAppKeys.length > 0 ? { cleanedAppKeys } : undefined
247
+ });
231
248
  } catch (error) {
249
+ await recordInfraInstallationCommand({
250
+ command: 'up-miso',
251
+ options,
252
+ startedAt,
253
+ outcome: 'failure',
254
+ error,
255
+ platformAppList: ['keycloak', 'miso-controller'],
256
+ cleanup: cleanedAppKeys.length > 0 ? { cleanedAppKeys } : undefined
257
+ });
232
258
  handleCommandError(error, 'up-miso');
233
259
  process.exit(1);
234
260
  }
@@ -237,33 +263,16 @@ function setupUpMisoCommand(program) {
237
263
 
238
264
  function setupUpDataplaneCommand(program) {
239
265
  program.command('up-dataplane')
240
- .description('Register, deploy, run dataplane locally (dev env; login required)')
266
+ .description('Register, deploy, run dataplane locally (dev env; needs up-infra; login required)')
241
267
  .option('-r, --registry <url>', 'Override registry for dataplane image')
242
268
  .option('--registry-mode <mode>', 'Override registry mode (acr|external)')
243
269
  .option('-i, --image <ref>', 'Override dataplane image reference (e.g. myreg/dataplane:latest)')
270
+ .option('--base', 'Resolve dataplane image from manifest only when running locally (default: true)', true)
244
271
  .option('-f, --force', 'Clean builder/dataplane and re-fetch from templates')
245
- .action(async(options) => {
246
- try {
247
- if (options.force) {
248
- await cleanBuilderAppDirs(['dataplane']);
249
- }
250
- await handleUpDataplane(options);
251
- } catch (error) {
252
- if (isAuthenticationError(error)) {
253
- const controllerUrl = error.controllerUrl || await resolveControllerUrl();
254
- logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
255
- try {
256
- await handleLogin({ method: 'device', controller: controllerUrl });
257
- await handleUpDataplane(options);
258
- return;
259
- } catch (loginOrRetryError) {
260
- handleCommandError(loginOrRetryError, 'up-dataplane');
261
- process.exit(1);
262
- }
263
- }
264
- handleCommandError(error, 'up-dataplane');
265
- process.exit(1);
266
- }
272
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
273
+ .action((options) => {
274
+ const { handleUpDataplaneCliAction } = require('./setup-infra-up-dataplane-action');
275
+ return handleUpDataplaneCliAction(options);
267
276
  });
268
277
  }
269
278
 
@@ -271,8 +280,14 @@ function setupDownInfraCommand(program) {
271
280
  program.command('down-infra [service|app]')
272
281
  .description('Stop all infra, or stop one app; use -v to remove volumes')
273
282
  .option('-v, --volumes', 'Remove volumes (deletes all data)')
283
+ .option('--verbose', 'Show full orchestration output (default is guided summary)')
274
284
  .action(async(appName, options) => {
275
285
  try {
286
+ if (!options.verbose) {
287
+ await runGuidedDownInfra(appName, options, infra, appLib);
288
+ return;
289
+ }
290
+
276
291
  if (typeof appName === 'string' && appName.trim().length > 0) {
277
292
  await appLib.downApp(appName, { volumes: !!options.volumes });
278
293
  } else {
@@ -291,33 +306,7 @@ function setupDoctorCommand(program) {
291
306
  .description('Check Docker, ports, secrets, and infra health')
292
307
  .action(async() => {
293
308
  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('');
309
+ await runDoctorCheck();
321
310
  } catch (error) {
322
311
  handleCommandError(error, 'doctor');
323
312
  process.exit(1);
@@ -365,19 +354,44 @@ function setupStatusCommand(program) {
365
354
  });
366
355
  }
367
356
 
368
- const INFRA_SERVICES = ['postgres', 'redis', 'pgadmin', 'redis-commander', 'traefik'];
357
+ const RESTART_INFRA_SERVICE_NAMES = getRestartableInfraServiceNames();
358
+
359
+ const RESTART_COMMAND_HELP_AFTER = `
360
+ Infra — use these exact Docker Compose service names:
361
+ ${buildRestartInfraHelpLines()}
362
+
363
+ Optional services (pgadmin, redis-commander, traefik) only exist if you enabled them when running up-infra.
364
+
365
+ Builder apps (examples):
366
+ keycloak, miso-controller, dataplane
367
+
368
+ Examples:
369
+ $ aifabrix restart postgres
370
+ $ aifabrix restart traefik
371
+ $ aifabrix restart miso-controller
372
+ `;
369
373
 
370
374
  function setupRestartCommand(program) {
371
375
  program.command('restart <service|app>')
372
- .description('Restart infra service (postgres, redis, …) or a builder/<app> container')
376
+ .description(
377
+ 'Restart one infra compose service (postgres, redis, pgadmin, redis-commander, traefik) or a builder app container'
378
+ )
379
+ .addHelpText('after', RESTART_COMMAND_HELP_AFTER)
373
380
  .action(async(service) => {
374
381
  try {
375
- if (INFRA_SERVICES.includes(service)) {
376
- await infra.restartService(service);
377
- logger.log(`✔ ${service} service restarted successfully`);
382
+ if (RESTART_INFRA_SERVICE_NAMES.includes(service)) {
383
+ logger.log('');
384
+ logger.log(sectionTitle('Restart'));
385
+ logger.log(headerKeyValue('Infra service:', service));
386
+ logger.log(formatProgress(`Restarting ${service}…`));
387
+ await infra.restartService(service, { suppressProgressLog: true });
388
+ logger.log(formatSuccessLine(`${service} restarted successfully`));
378
389
  } else {
390
+ logger.log('');
391
+ logger.log(sectionTitle('Restart'));
392
+ logger.log(headerKeyValue('Application:', service));
379
393
  await appLib.restartApp(service);
380
- logger.log(`✔ ${service} restarted successfully`);
394
+ logger.log(formatSuccessLine(`${service} restarted successfully`));
381
395
  }
382
396
  } catch (error) {
383
397
  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');