@aifabrix/builder 2.44.6 → 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 (86) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/jest.projects.js +56 -0
  3. package/lib/app/helpers.js +3 -3
  4. package/lib/app/index.js +3 -3
  5. package/lib/app/register.js +7 -6
  6. package/lib/app/restart-display.js +52 -21
  7. package/lib/app/rotate-secret.js +7 -6
  8. package/lib/app/run-helpers.js +15 -8
  9. package/lib/app/run.js +57 -9
  10. package/lib/app/show-display.js +7 -0
  11. package/lib/app/show.js +87 -5
  12. package/lib/build/index.js +9 -5
  13. package/lib/cli/infra-guided.js +42 -27
  14. package/lib/cli/installation-log-command.js +73 -0
  15. package/lib/cli/setup-app.js +11 -1
  16. package/lib/cli/setup-auth.js +94 -49
  17. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  18. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  19. package/lib/cli/setup-infra.js +60 -119
  20. package/lib/cli/setup-platform.js +1 -1
  21. package/lib/cli/setup-utility-resolve.js +132 -0
  22. package/lib/cli/setup-utility.js +65 -51
  23. package/lib/commands/app-logs.js +81 -33
  24. package/lib/commands/auth-config.js +116 -18
  25. package/lib/commands/setup-modes.js +19 -6
  26. package/lib/commands/setup-prompts.js +41 -8
  27. package/lib/commands/setup.js +114 -9
  28. package/lib/commands/teardown.js +54 -5
  29. package/lib/commands/up-common.js +48 -14
  30. package/lib/commands/up-dataplane.js +21 -18
  31. package/lib/commands/up-miso.js +12 -8
  32. package/lib/commands/upload.js +5 -3
  33. package/lib/core/audit-logger.js +1 -34
  34. package/lib/core/config-admin-email.js +56 -0
  35. package/lib/core/config-normalize.js +60 -0
  36. package/lib/core/config-registered-controller-urls.js +54 -0
  37. package/lib/core/config.js +33 -50
  38. package/lib/core/secrets-ensure-infra.js +1 -1
  39. package/lib/core/secrets-env-content.js +86 -90
  40. package/lib/core/secrets-env-declarative-expand.js +170 -0
  41. package/lib/core/secrets-env-write.js +2 -0
  42. package/lib/core/secrets-load.js +106 -102
  43. package/lib/external-system/deploy.js +5 -1
  44. package/lib/internal/node-fs.js +2 -0
  45. package/lib/schema/application-schema.json +4 -0
  46. package/lib/schema/infra.parameter.yaml +10 -0
  47. package/lib/utils/app-config-resolver.js +24 -1
  48. package/lib/utils/applications-config-defaults.js +206 -0
  49. package/lib/utils/auth-config-validator.js +2 -12
  50. package/lib/utils/bash-secret-env.js +1 -1
  51. package/lib/utils/compose-generate-docker-compose.js +111 -6
  52. package/lib/utils/compose-generator.js +17 -8
  53. package/lib/utils/controller-url.js +50 -7
  54. package/lib/utils/env-copy.js +99 -14
  55. package/lib/utils/env-template.js +5 -1
  56. package/lib/utils/health-check-url.js +18 -15
  57. package/lib/utils/health-check.js +7 -5
  58. package/lib/utils/infra-optional-service-flags.js +69 -0
  59. package/lib/utils/installation-log-core.js +282 -0
  60. package/lib/utils/installation-log-record.js +237 -0
  61. package/lib/utils/installation-log.js +123 -0
  62. package/lib/utils/log-redaction.js +105 -0
  63. package/lib/utils/manifest-location.js +164 -0
  64. package/lib/utils/manifest-source-emit.js +162 -0
  65. package/lib/utils/paths.js +238 -89
  66. package/lib/utils/remote-secrets-loader.js +7 -1
  67. package/lib/utils/run-cli-flags.js +29 -0
  68. package/lib/utils/secrets-canonical.js +10 -3
  69. package/lib/utils/secrets-path.js +3 -4
  70. package/lib/utils/secrets-utils.js +20 -10
  71. package/lib/utils/system-builder-root.js +10 -2
  72. package/lib/utils/url-declarative-public-base.js +80 -12
  73. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  74. package/lib/utils/url-declarative-resolve-build.js +24 -393
  75. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  76. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  77. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  78. package/lib/utils/url-declarative-resolve.js +47 -7
  79. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  80. package/lib/utils/urls-local-registry-scan.js +103 -0
  81. package/lib/utils/urls-local-registry.js +161 -90
  82. package/package.json +3 -1
  83. package/templates/applications/dataplane/application.yaml +4 -0
  84. package/templates/applications/miso-controller/application.yaml +2 -0
  85. package/templates/applications/miso-controller/env.template +27 -29
  86. package/.npmrc.token +0 -1
@@ -0,0 +1,111 @@
1
+ /**
2
+ * `aifabrix up-dataplane` action body (keeps setup-infra.js under size limits).
3
+ *
4
+ * @fileoverview up-dataplane CLI action
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('../utils/logger');
13
+ const { formatProgress } = require('../utils/cli-test-layout-chalk');
14
+ const { handleCommandError, isAuthenticationError } = require('../utils/cli-utils');
15
+ const { resolveControllerUrl } = require('../utils/controller-url');
16
+ const { handleLogin } = require('../commands/login');
17
+ const { handleUpDataplane } = require('../commands/up-dataplane');
18
+ const { cleanBuilderAppDirs } = require('../commands/up-common');
19
+ const { runGuidedUpDataplane } = require('./infra-guided');
20
+ const { recordInfraInstallationCommand } = require('./installation-log-command');
21
+
22
+ /**
23
+ * @param {Object} options
24
+ * @returns {Promise<string[]>}
25
+ */
26
+ async function runUpDataplaneWork(options) {
27
+ let cleanedAppKeys = [];
28
+ if (options.force) {
29
+ const c = await cleanBuilderAppDirs(['dataplane']);
30
+ cleanedAppKeys = Array.isArray(c) ? c : [];
31
+ }
32
+ if (!options.verbose) {
33
+ await runGuidedUpDataplane(options, handleUpDataplane);
34
+ } else {
35
+ await handleUpDataplane(options);
36
+ }
37
+ return cleanedAppKeys;
38
+ }
39
+
40
+ /**
41
+ * @param {Object} params
42
+ * @returns {Promise<void>}
43
+ */
44
+ async function logDataplaneOutcome(params) {
45
+ const { options, startedAt, outcome, error, cleanedAppKeys } = params;
46
+ await recordInfraInstallationCommand({
47
+ command: 'up-dataplane',
48
+ options,
49
+ startedAt,
50
+ outcome,
51
+ error,
52
+ platformAppList: ['dataplane'],
53
+ cleanup: cleanedAppKeys.length > 0 ? { cleanedAppKeys } : undefined
54
+ });
55
+ }
56
+
57
+ /**
58
+ * @param {Object} options
59
+ * @param {Date} startedAt
60
+ * @param {string[]} cleanedAppKeys
61
+ * @param {string} controllerUrl
62
+ * @returns {Promise<void>}
63
+ */
64
+ async function runDataplaneAuthRecovery(options, startedAt, cleanedAppKeys, controllerUrl) {
65
+ if (!options.verbose) {
66
+ logger.log(formatProgress('Authenticating...'));
67
+ } else {
68
+ logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
69
+ }
70
+ await handleLogin({ method: 'device', controller: controllerUrl, compact: !options.verbose });
71
+ await handleUpDataplane(options);
72
+ await logDataplaneOutcome({ options, startedAt, outcome: 'success', cleanedAppKeys });
73
+ }
74
+
75
+ /**
76
+ * @param {Object} options
77
+ * @returns {Promise<void>}
78
+ */
79
+ async function handleUpDataplaneCliAction(options) {
80
+ const startedAt = new Date();
81
+ let cleanedAppKeys = [];
82
+ try {
83
+ cleanedAppKeys = await runUpDataplaneWork(options);
84
+ await logDataplaneOutcome({ options, startedAt, outcome: 'success', cleanedAppKeys });
85
+ } catch (error) {
86
+ if (isAuthenticationError(error)) {
87
+ const controllerUrl = error.controllerUrl || await resolveControllerUrl();
88
+ try {
89
+ await runDataplaneAuthRecovery(options, startedAt, cleanedAppKeys, controllerUrl);
90
+ return;
91
+ } catch (loginOrRetryError) {
92
+ await logDataplaneOutcome({
93
+ options,
94
+ startedAt,
95
+ outcome: 'failure',
96
+ error: loginOrRetryError,
97
+ cleanedAppKeys
98
+ });
99
+ handleCommandError(loginOrRetryError, 'up-dataplane');
100
+ process.exit(1);
101
+ }
102
+ }
103
+ await logDataplaneOutcome({ options, startedAt, outcome: 'failure', error, cleanedAppKeys });
104
+ handleCommandError(error, 'up-dataplane');
105
+ process.exit(1);
106
+ }
107
+ }
108
+
109
+ module.exports = {
110
+ handleUpDataplaneCliAction
111
+ };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * `aifabrix up-platform` action body (keeps setup-infra.js under size limits).
3
+ *
4
+ * @fileoverview up-platform CLI action
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('../utils/logger');
13
+ const { formatProgress } = require('../utils/cli-test-layout-chalk');
14
+ const { handleCommandError, isAuthenticationError } = require('../utils/cli-utils');
15
+ const { resolveControllerUrl } = require('../utils/controller-url');
16
+ const { handleLogin } = require('../commands/login');
17
+ const { handleUpMiso } = require('../commands/up-miso');
18
+ const { handleUpDataplane } = require('../commands/up-dataplane');
19
+ const {
20
+ applyUpPlatformForceConfig,
21
+ cleanBuilderAppDirs,
22
+ prepareUrlsLocalRegistryForUpPlatform
23
+ } = require('../commands/up-common');
24
+ const { runGuidedUpPlatform } = require('./infra-guided');
25
+ const { recordInfraInstallationCommand } = require('./installation-log-command');
26
+
27
+ const PLATFORM_APPS = ['keycloak', 'miso-controller', 'dataplane'];
28
+
29
+ /**
30
+ * @param {Object} options
31
+ * @returns {Promise<string[]>}
32
+ */
33
+ async function runUpPlatformWork(options) {
34
+ let cleanedAppKeys = [];
35
+ let platformForceCleanSummary = null;
36
+ if (options.force) {
37
+ const forceSummary = await applyUpPlatformForceConfig({ silent: !options.verbose });
38
+ const cleanedApps = await cleanBuilderAppDirs(PLATFORM_APPS, {
39
+ silent: !options.verbose
40
+ });
41
+ cleanedAppKeys = Array.isArray(cleanedApps) ? cleanedApps : [];
42
+ if (!options.verbose) {
43
+ platformForceCleanSummary = { forceSummary, cleanedApps: cleanedAppKeys };
44
+ }
45
+ }
46
+
47
+ if (!options.verbose) {
48
+ await runGuidedUpPlatform(
49
+ options,
50
+ handleUpMiso,
51
+ handleUpDataplane,
52
+ handleLogin,
53
+ platformForceCleanSummary
54
+ );
55
+ } else {
56
+ await prepareUrlsLocalRegistryForUpPlatform();
57
+ await handleUpMiso(options);
58
+ await handleUpDataplane(options);
59
+ }
60
+ return cleanedAppKeys;
61
+ }
62
+
63
+ /**
64
+ * @param {Object} params
65
+ * @returns {Promise<void>}
66
+ */
67
+ async function logUpPlatformOutcome(params) {
68
+ const { options, startedAt, outcome, error, cleanedAppKeys } = params;
69
+ await recordInfraInstallationCommand({
70
+ command: 'up-platform',
71
+ options,
72
+ startedAt,
73
+ outcome,
74
+ error,
75
+ platformAppList: PLATFORM_APPS,
76
+ cleanup: cleanedAppKeys.length > 0 ? { cleanedAppKeys } : undefined,
77
+ upPlatformForce: options.force === true
78
+ });
79
+ }
80
+
81
+ async function maybeHandleUpPlatformAuthRetry(error, options) {
82
+ if (!isAuthenticationError(error)) {
83
+ return false;
84
+ }
85
+ const controllerUrl = error.controllerUrl || await resolveControllerUrl();
86
+ if (!options.verbose) {
87
+ logger.log(formatProgress('Authenticating...'));
88
+ } else {
89
+ logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
90
+ }
91
+ await handleLogin({ method: 'device', controller: controllerUrl, compact: !options.verbose });
92
+ await handleUpDataplane(options);
93
+ return true;
94
+ }
95
+
96
+ /**
97
+ * @param {Object} options
98
+ * @returns {Promise<void>}
99
+ */
100
+ async function handleUpPlatformCliAction(options) {
101
+ const startedAt = new Date();
102
+ let cleanedAppKeys = [];
103
+ try {
104
+ cleanedAppKeys = await runUpPlatformWork(options);
105
+ await logUpPlatformOutcome({ options, startedAt, outcome: 'success', cleanedAppKeys });
106
+ } catch (error) {
107
+ try {
108
+ if (await maybeHandleUpPlatformAuthRetry(error, options)) {
109
+ await logUpPlatformOutcome({ options, startedAt, outcome: 'success', cleanedAppKeys });
110
+ return;
111
+ }
112
+ } catch (loginOrRetryError) {
113
+ await logUpPlatformOutcome({
114
+ options,
115
+ startedAt,
116
+ outcome: 'failure',
117
+ error: loginOrRetryError,
118
+ cleanedAppKeys
119
+ });
120
+ handleCommandError(loginOrRetryError, 'up-platform');
121
+ process.exit(1);
122
+ }
123
+ await logUpPlatformOutcome({ options, startedAt, outcome: 'failure', error, cleanedAppKeys });
124
+ handleCommandError(error, 'up-platform');
125
+ process.exit(1);
126
+ }
127
+ }
128
+
129
+ module.exports = {
130
+ handleUpPlatformCliAction
131
+ };
@@ -12,22 +12,14 @@ const {
12
12
  * @version 2.0.0
13
13
  */
14
14
 
15
- const chalk = require('chalk');
16
15
  const infra = require('../infrastructure');
17
16
  const appLib = require('../app');
18
17
  const config = require('../core/config');
19
18
  const logger = require('../utils/logger');
20
- const { handleCommandError, isAuthenticationError } = require('../utils/cli-utils');
21
- const { resolveControllerUrl } = require('../utils/controller-url');
22
- const { handleLogin } = require('../commands/login');
19
+ const { handleCommandError } = require('../utils/cli-utils');
23
20
  const { handleUpMiso } = require('../commands/up-miso');
24
- const { handleUpDataplane } = require('../commands/up-dataplane');
25
- const {
26
- applyUpPlatformForceConfig,
27
- cleanBuilderAppDirs,
28
- prepareUrlsLocalRegistryForUpPlatform
29
- } = require('../commands/up-common');
30
- const { runGuidedUpInfra, runGuidedUpMiso, runGuidedUpDataplane, runGuidedUpPlatform, runGuidedDownInfra } = require('./infra-guided');
21
+ const { cleanBuilderAppDirs } = require('../commands/up-common');
22
+ const { runGuidedUpInfra, runGuidedUpMiso, runGuidedDownInfra } = require('./infra-guided');
31
23
  const {
32
24
  loadInfraStatusSummary,
33
25
  formatInfraStatusTitleLine,
@@ -40,6 +32,11 @@ const {
40
32
  buildRestartInfraHelpLines
41
33
  } = require('../constants/infra-compose-service-names');
42
34
 
35
+ const {
36
+ computeEffectiveInfraOptionalFlags,
37
+ persistMissingInfraOptionalServiceFlags
38
+ } = require('../utils/infra-optional-service-flags');
39
+
43
40
  const UP_INFRA_HELP_AFTER = `
44
41
  Typical sequence:
45
42
  $ aifabrix up-infra
@@ -83,19 +80,6 @@ async function persistTlsEnabledFlag(cfg, value) {
83
80
  ));
84
81
  }
85
82
 
86
- /**
87
- * Resolves effective boolean from option vs config.
88
- * @param {*} optValue - options.traefik | options.pgAdmin | options.redisAdmin
89
- * @param {*} cfgValue - cfg.traefik | cfg.pgadmin | cfg.redisCommander
90
- * @param {boolean} defaultWhenUndef - Default when config value is undefined
91
- * @returns {boolean}
92
- */
93
- function resolveFlag(optValue, cfgValue, defaultWhenUndef = true) {
94
- if (optValue === true) return true;
95
- if (optValue === false) return false;
96
- return cfgValue !== false && (cfgValue === true || defaultWhenUndef);
97
- }
98
-
99
83
  /**
100
84
  * @param {Object} options - Commander options
101
85
  * @returns {Promise<number|null>} Developer ID or null
@@ -152,31 +136,18 @@ async function runUpInfraCommand(options) {
152
136
  await maybePersistTlsFromUpInfra(options, cfg);
153
137
  const adminPass = options.adminPassword || options.adminPwd;
154
138
  const tlsEnabledEffective = cfg.tlsEnabled === true;
139
+ const effective = computeEffectiveInfraOptionalFlags(cfg, options);
155
140
  await infra.startInfra(developerId, {
156
- traefik: resolveFlag(options.traefik, cfg.traefik, false),
157
- pgadmin: resolveFlag(options.pgAdmin, cfg.pgadmin, true),
158
- redisCommander: resolveFlag(options.redisAdmin, cfg.redisCommander, true),
141
+ traefik: effective.traefik,
142
+ pgadmin: effective.pgadmin,
143
+ redisCommander: effective.redisCommander,
159
144
  adminPassword: adminPass,
160
145
  adminPwd: adminPass,
161
146
  adminEmail: options.adminEmail,
162
147
  userPassword: options.userPassword,
163
148
  tlsEnabled: tlsEnabledEffective
164
149
  });
165
- }
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;
150
+ await persistMissingInfraOptionalServiceFlags(cfg, effective);
180
151
  }
181
152
 
182
153
  function setupUpInfraCommand(program) {
@@ -197,14 +168,28 @@ function setupUpInfraCommand(program) {
197
168
  .option('--tls', 'Enable TLS mode; save tlsEnabled (${TLS_ENABLED}=true, ${HTTP_ENABLED}=false in application.yaml)')
198
169
  .option('--no-tls', 'Disable TLS mode (${TLS_ENABLED}=false, ${HTTP_ENABLED}=true)')
199
170
  .action(async(options) => {
171
+ const startedAt = new Date();
172
+ const { recordInfraInstallationCommand } = require('./installation-log-command');
200
173
  try {
201
174
  if (!options.verbose) {
202
175
  await runGuidedUpInfra(options, runUpInfraCommand);
203
- return;
176
+ } else {
177
+ await runUpInfraCommand(options);
204
178
  }
205
-
206
- await runUpInfraCommand(options);
179
+ await recordInfraInstallationCommand({
180
+ command: 'up-infra',
181
+ options,
182
+ startedAt,
183
+ outcome: 'success'
184
+ });
207
185
  } catch (error) {
186
+ await recordInfraInstallationCommand({
187
+ command: 'up-infra',
188
+ options,
189
+ startedAt,
190
+ outcome: 'failure',
191
+ error
192
+ });
208
193
  handleCommandError(error, 'up-infra');
209
194
  process.exit(1);
210
195
  }
@@ -223,45 +208,9 @@ function setupUpPlatformCommand(program) {
223
208
  '-f, --force',
224
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'
225
210
  )
226
- .action(async(options) => {
227
- try {
228
- let platformForceCleanSummary = null;
229
- if (options.force) {
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;
248
- }
249
-
250
- await prepareUrlsLocalRegistryForUpPlatform();
251
- await handleUpMiso(options);
252
- await handleUpDataplane(options);
253
- } catch (error) {
254
- try {
255
- if (await maybeHandleUpPlatformAuthRetry(error, options)) {
256
- return;
257
- }
258
- } catch (loginOrRetryError) {
259
- handleCommandError(loginOrRetryError, 'up-platform');
260
- process.exit(1);
261
- }
262
- handleCommandError(error, 'up-platform');
263
- process.exit(1);
264
- }
211
+ .action((options) => {
212
+ const { handleUpPlatformCliAction } = require('./setup-infra-up-platform-action');
213
+ return handleUpPlatformCliAction(options);
265
214
  });
266
215
  }
267
216
 
@@ -275,17 +224,37 @@ function setupUpMisoCommand(program) {
275
224
  .option('-f, --force', 'Clean builder/keycloak and builder/miso-controller and re-fetch from templates')
276
225
  .option('--verbose', 'Show full orchestration output (default is guided summary)')
277
226
  .action(async(options) => {
227
+ const startedAt = new Date();
228
+ const { recordInfraInstallationCommand } = require('./installation-log-command');
229
+ let cleanedAppKeys = [];
278
230
  try {
279
231
  if (options.force) {
280
- await cleanBuilderAppDirs(['keycloak', 'miso-controller']);
232
+ const c = await cleanBuilderAppDirs(['keycloak', 'miso-controller']);
233
+ cleanedAppKeys = Array.isArray(c) ? c : [];
281
234
  }
282
235
  if (!options.verbose) {
283
236
  await runGuidedUpMiso(options, handleUpMiso);
284
- return;
237
+ } else {
238
+ await handleUpMiso(options);
285
239
  }
286
-
287
- 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
+ });
288
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
+ });
289
258
  handleCommandError(error, 'up-miso');
290
259
  process.exit(1);
291
260
  }
@@ -301,37 +270,9 @@ function setupUpDataplaneCommand(program) {
301
270
  .option('--base', 'Resolve dataplane image from manifest only when running locally (default: true)', true)
302
271
  .option('-f, --force', 'Clean builder/dataplane and re-fetch from templates')
303
272
  .option('--verbose', 'Show full orchestration output (default is guided summary)')
304
- .action(async(options) => {
305
- try {
306
- if (options.force) {
307
- await cleanBuilderAppDirs(['dataplane']);
308
- }
309
- if (!options.verbose) {
310
- await runGuidedUpDataplane(options, handleUpDataplane);
311
- return;
312
- }
313
-
314
- await handleUpDataplane(options);
315
- } catch (error) {
316
- if (isAuthenticationError(error)) {
317
- const controllerUrl = error.controllerUrl || await resolveControllerUrl();
318
- if (!options.verbose) {
319
- logger.log(formatProgress('Authenticating...'));
320
- } else {
321
- logger.log(chalk.blue('\nAuthentication required. Running aifabrix login...\n'));
322
- }
323
- try {
324
- await handleLogin({ method: 'device', controller: controllerUrl, compact: !options.verbose });
325
- await handleUpDataplane(options);
326
- return;
327
- } catch (loginOrRetryError) {
328
- handleCommandError(loginOrRetryError, 'up-dataplane');
329
- process.exit(1);
330
- }
331
- }
332
- handleCommandError(error, 'up-dataplane');
333
- process.exit(1);
334
- }
273
+ .action((options) => {
274
+ const { handleUpDataplaneCliAction } = require('./setup-infra-up-dataplane-action');
275
+ return handleUpDataplaneCliAction(options);
335
276
  });
336
277
  }
337
278
 
@@ -23,7 +23,7 @@ const { handleTeardown } = require('../commands/teardown');
23
23
 
24
24
  const SETUP_HELP_AFTER = `
25
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.
26
+ - With no infra running: prompts for admin email/password (used for Postgres, pgAdmin, Keycloak). The email is saved as adminEmail in config.yaml so the next setup can show it as the default (you can still change it). Prompts for an AI tool (OpenAI or Azure OpenAI) only when keys are not already set, then runs up-infra and up-platform.
27
27
  - With infra running: shows a mode menu:
28
28
  1) Re-install — stop infra and remove all volumes, then up-infra + up-platform --force.
29
29
  2) Wipe data — drop every database and DB user, then up-infra + up-platform --force.
@@ -0,0 +1,132 @@
1
+ /**
2
+ * `aifabrix resolve <app>` command wiring (split from setup-utility for file/function size limits).
3
+ *
4
+ * @fileoverview Resolve CLI command
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const chalk = require('chalk');
12
+ const secrets = require('../core/secrets');
13
+ const logger = require('../utils/logger');
14
+ const { handleCommandError } = require('../utils/cli-utils');
15
+ const coreConfig = require('../core/config');
16
+ const { resolvePreferLocalEnvOutputPathFlag } = require('../utils/applications-config-defaults');
17
+ const { getResolveAppPath } = require('../utils/paths');
18
+
19
+ /**
20
+ * @param {string} appName
21
+ * @param {string} appPath
22
+ * @param {boolean} envOnly
23
+ * @param {string} envPath
24
+ * @param {{ skipValidation?: boolean }} options
25
+ * @param {{ getManifestSourcePayload: function }} manifestEmit
26
+ * @returns {Promise<void>}
27
+ */
28
+ async function runResolveJsonOutput(appName, appPath, envOnly, envPath, options, manifestEmit) {
29
+ /** @type {Record<string, unknown>} */
30
+ const payload = {
31
+ app: appName,
32
+ appPath,
33
+ envOnly,
34
+ envPath,
35
+ manifestSource: envOnly ? null : manifestEmit.getManifestSourcePayload(appName, appPath)
36
+ };
37
+ if (!envOnly && !options.skipValidation) {
38
+ const validate = require('../validation/validate');
39
+ payload.validation = await validate.validateAppOrFile(appName);
40
+ }
41
+ logger.log(JSON.stringify(payload, null, 2));
42
+ const v = payload.validation;
43
+ if (v && typeof v === 'object' && v.valid === false) {
44
+ process.exit(1);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * @param {string} appName
50
+ * @param {string} envPath
51
+ * @param {boolean} envOnly
52
+ * @param {{ skipValidation?: boolean }} options
53
+ * @returns {Promise<void>}
54
+ */
55
+ async function runResolveHumanOutput(appName, envPath, envOnly, options) {
56
+ logger.log(`✔ Generated .env file: ${envPath}`);
57
+ logger.log(chalk.gray(' Note: up-platform / up-miso / up-dataplane / register / build resolve secrets in memory only.'));
58
+ logger.log(chalk.gray(` Re-run "aifabrix resolve ${appName}" whenever you need an on-disk .env again.`));
59
+ if (envOnly) {
60
+ logger.log(chalk.gray(' (env-only mode: validation skipped; no application config file)'));
61
+ return;
62
+ }
63
+ if (options.skipValidation) {
64
+ return;
65
+ }
66
+ const validate = require('../validation/validate');
67
+ const result = await validate.validateAppOrFile(appName);
68
+ validate.displayValidationResults(result);
69
+ if (!result.valid) {
70
+ logger.log(chalk.yellow('\n⚠ Validation found errors. Fix them before deploying.'));
71
+ process.exit(1);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * @param {string} appName
77
+ * @param {{ force?: boolean, skipValidation?: boolean, json?: boolean }} options
78
+ * @returns {Promise<void>}
79
+ */
80
+ async function runResolveAppAction(appName, options) {
81
+ const jsonMode = options.json === true;
82
+ const { appPath, envOnly } = await getResolveAppPath(appName);
83
+ const manifestEmit = require('../utils/manifest-source-emit');
84
+ manifestEmit.emitManifestMetadataLineIfTTY(logger, {
85
+ appKey: appName,
86
+ appPath,
87
+ envOnly,
88
+ json: jsonMode
89
+ });
90
+ const userCfg = await coreConfig.getConfig();
91
+ const preferLocalEnvOutputPath = await resolvePreferLocalEnvOutputPathFlag(userCfg, appName);
92
+ const envPath = await secrets.generateEnvFile(
93
+ appName,
94
+ undefined,
95
+ 'docker',
96
+ options.force,
97
+ {
98
+ appPath,
99
+ envOnly,
100
+ skipOutputPath: false,
101
+ preserveFromPath: null,
102
+ preferLocalEnvOutputPath
103
+ }
104
+ );
105
+ if (jsonMode) {
106
+ await runResolveJsonOutput(appName, appPath, envOnly, envPath, options, manifestEmit);
107
+ return;
108
+ }
109
+ await runResolveHumanOutput(appName, envPath, envOnly, options);
110
+ }
111
+
112
+ /**
113
+ * @param {import('commander').Command} program
114
+ * @returns {void}
115
+ */
116
+ function setupResolveCommand(program) {
117
+ program.command('resolve <app>')
118
+ .description('Generate .env from template; optional validate after')
119
+ .option('-f, --force', 'Generate missing secret keys in secrets file')
120
+ .option('--skip-validation', 'Skip file validation after generating .env')
121
+ .option('--json', 'Print JSON summary (envPath, manifestSource, optional validation)')
122
+ .action(async(appName, options) => {
123
+ try {
124
+ await runResolveAppAction(appName, options);
125
+ } catch (error) {
126
+ handleCommandError(error, 'resolve');
127
+ process.exit(1);
128
+ }
129
+ });
130
+ }
131
+
132
+ module.exports = { setupResolveCommand };