@aifabrix/builder 2.44.4 → 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.
- package/.cursor/rules/cli-layout.mdc +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +68 -17
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/types/wizard.types.js +2 -1
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.help.js +1 -1
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-app.test-commands.js +9 -5
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +138 -61
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility.js +97 -33
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +225 -19
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -354
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/test-e2e-external.js +4 -3
- package/lib/commands/up-common.js +97 -12
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +58 -15
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +72 -14
- package/lib/commands/wizard-headless.js +7 -3
- package/lib/commands/wizard-helpers.js +13 -1
- package/lib/commands/wizard.js +210 -61
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +4 -1
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/generator/wizard-prompts.js +7 -1
- package/lib/generator/wizard.js +34 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- package/lib/schema/wizard-config.schema.json +2 -2
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/bash-secret-env.js +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- package/lib/utils/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/external-readme.js +117 -4
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +73 -20
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +7 -7
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +89 -102
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
package/lib/commands/upload.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatWarningLine,
|
|
4
|
+
formatProgress,
|
|
5
|
+
sectionTitle,
|
|
6
|
+
headerKeyValue,
|
|
7
|
+
metadata
|
|
8
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
9
|
/**
|
|
3
|
-
* Upload external system to dataplane (
|
|
10
|
+
* Upload external system to dataplane (sync local `integration/<systemKey>/openapi/*.json` when present,
|
|
11
|
+
* then single pipeline upload: validate → publish → controller register).
|
|
4
12
|
*
|
|
5
13
|
* @fileoverview Upload command handler for aifabrix upload <systemKey>
|
|
6
14
|
* @author AI Fabrix Team
|
|
@@ -10,6 +18,8 @@ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
|
10
18
|
const path = require('path');
|
|
11
19
|
const chalk = require('chalk');
|
|
12
20
|
const logger = require('../utils/logger');
|
|
21
|
+
|
|
22
|
+
const SEP = chalk.gray('────────────────────────────────────────');
|
|
13
23
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
14
24
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
15
25
|
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
@@ -38,6 +48,7 @@ const {
|
|
|
38
48
|
} = require('../utils/external-system-readiness-display');
|
|
39
49
|
const { maybeSyncSystemCertificationFromDataplane } = require('../certification/sync-system-certification');
|
|
40
50
|
const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
|
|
51
|
+
const { maybeSyncOpenApiFilesForMcp } = require('./repair-openapi-sync');
|
|
41
52
|
|
|
42
53
|
/**
|
|
43
54
|
* Validates system-key format (same as download).
|
|
@@ -86,7 +97,7 @@ async function resolveDataplaneAndAuth(systemKey, opts = {}) {
|
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
if (!silent) {
|
|
89
|
-
logger.log(
|
|
100
|
+
logger.log(metadata('Resolving dataplane URL...'));
|
|
90
101
|
}
|
|
91
102
|
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent });
|
|
92
103
|
return { dataplaneUrl, authConfig, environment };
|
|
@@ -168,7 +179,9 @@ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey,
|
|
|
168
179
|
});
|
|
169
180
|
if (pushResult.pushed > 0) {
|
|
170
181
|
const keyList = pushResult.keys?.length ? ` (${pushResult.keys.join(', ')})` : '';
|
|
171
|
-
logger.log(
|
|
182
|
+
logger.log(
|
|
183
|
+
formatSuccessLine(`Pushed ${pushResult.pushed} credential secret(s) to dataplane${keyList}.`)
|
|
184
|
+
);
|
|
172
185
|
} else {
|
|
173
186
|
logger.log(chalk.yellow('Secret push skipped'));
|
|
174
187
|
}
|
|
@@ -223,7 +236,10 @@ const UPLOAD_PROBE_TEST_DATA = {};
|
|
|
223
236
|
* @param {number|undefined} probeTimeoutMs
|
|
224
237
|
*/
|
|
225
238
|
async function maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, probeTimeoutMs) {
|
|
226
|
-
logger.log(
|
|
239
|
+
logger.log('');
|
|
240
|
+
logger.log(sectionTitle('Runtime checks (--probe)'));
|
|
241
|
+
logger.log(SEP);
|
|
242
|
+
logger.log(formatProgress('Running runtime checks...'));
|
|
227
243
|
const timeoutMs = probeTimeoutMs === undefined || probeTimeoutMs === null ? 120000 : probeTimeoutMs;
|
|
228
244
|
try {
|
|
229
245
|
const pr = await testSystemViaPipeline(
|
|
@@ -246,10 +262,13 @@ async function maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, probeTim
|
|
|
246
262
|
|
|
247
263
|
/**
|
|
248
264
|
* Local validation, manifest, payload, and configuration resolution.
|
|
265
|
+
* Does not write *-deploy.json; run `aifabrix json <systemKey>` to regenerate the deployment manifest from sources.
|
|
266
|
+
*
|
|
249
267
|
* @param {string} systemKey
|
|
268
|
+
* @param {{ dryRun?: boolean }} [_opts] - Reserved for callers (e.g. dry-run); manifest is always built from current disk files
|
|
250
269
|
* @returns {Promise<{ manifest: Object, payload: Object }>}
|
|
251
270
|
*/
|
|
252
|
-
async function buildValidatedUploadManifestPayload(systemKey) {
|
|
271
|
+
async function buildValidatedUploadManifestPayload(systemKey, _opts = {}) {
|
|
253
272
|
const validationResult = await validateExternalSystemComplete(systemKey, { type: 'external' });
|
|
254
273
|
throwIfValidationFailed(validationResult);
|
|
255
274
|
logger.log(formatSuccessLine('Local validation passed'));
|
|
@@ -260,24 +279,52 @@ async function buildValidatedUploadManifestPayload(systemKey) {
|
|
|
260
279
|
}
|
|
261
280
|
|
|
262
281
|
/**
|
|
263
|
-
* Upload
|
|
282
|
+
* Upload local `integration/<systemKey>/openapi/*.json` specs when present so pipeline can resolve
|
|
283
|
+
* each datasource `openapi.documentKey` (same behavior as repair --api OpenAPI sync).
|
|
284
|
+
*
|
|
264
285
|
* @param {string} systemKey
|
|
265
|
-
* @param {Object} options
|
|
266
286
|
* @param {Object} manifest
|
|
267
|
-
* @
|
|
287
|
+
* @returns {Promise<void>}
|
|
268
288
|
*/
|
|
269
|
-
async function
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
289
|
+
async function logAndSyncLocalOpenApiForUpload(systemKey, manifest) {
|
|
290
|
+
const appPath = getIntegrationPath(systemKey);
|
|
291
|
+
const ei = manifest && manifest.externalIntegration;
|
|
292
|
+
const datasourceFiles = ei && Array.isArray(ei.dataSources) ? ei.dataSources : [];
|
|
293
|
+
const openapiSyncLines = await maybeSyncOpenApiFilesForMcp({
|
|
294
|
+
enabled: true,
|
|
295
|
+
dryRun: false,
|
|
296
|
+
appPath,
|
|
297
|
+
systemKey,
|
|
298
|
+
datasourceFiles
|
|
299
|
+
});
|
|
300
|
+
for (const line of openapiSyncLines) {
|
|
301
|
+
logger.log(line.startsWith('Skipped') ? formatWarningLine(line) : formatSuccessLine(line));
|
|
278
302
|
}
|
|
279
|
-
|
|
280
|
-
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @param {Object} ctx
|
|
307
|
+
* @param {string} ctx.systemKey
|
|
308
|
+
* @param {Object} ctx.options
|
|
309
|
+
* @param {Object} ctx.manifest
|
|
310
|
+
* @param {Object} ctx.payload
|
|
311
|
+
* @param {string} ctx.environment
|
|
312
|
+
* @param {string} ctx.dataplaneUrl
|
|
313
|
+
* @param {Object} ctx.authConfig
|
|
314
|
+
* @param {Object} ctx.rawRes
|
|
315
|
+
* @returns {Promise<void>}
|
|
316
|
+
*/
|
|
317
|
+
async function handlePublicationAndFollowups(ctx) {
|
|
318
|
+
const {
|
|
319
|
+
systemKey,
|
|
320
|
+
options,
|
|
321
|
+
manifest,
|
|
322
|
+
payload,
|
|
323
|
+
environment,
|
|
324
|
+
dataplaneUrl,
|
|
325
|
+
authConfig,
|
|
326
|
+
rawRes
|
|
327
|
+
} = ctx;
|
|
281
328
|
const publication = unwrapPublicationResult(rawRes);
|
|
282
329
|
if (!publication) {
|
|
283
330
|
throw new Error(
|
|
@@ -296,7 +343,6 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
|
|
|
296
343
|
if (options.probe) {
|
|
297
344
|
await maybeRunUploadProbe(dataplaneUrl, systemKey, authConfig, options.probeTimeout);
|
|
298
345
|
}
|
|
299
|
-
|
|
300
346
|
const dsKeys = (payload.dataSources || []).map((ds) => ds && ds.key).filter(Boolean);
|
|
301
347
|
await maybeSyncSystemCertificationFromDataplane({
|
|
302
348
|
label: 'upload',
|
|
@@ -308,6 +354,41 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
|
|
|
308
354
|
});
|
|
309
355
|
}
|
|
310
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Upload path after dry-run check.
|
|
359
|
+
* @param {string} systemKey
|
|
360
|
+
* @param {Object} options
|
|
361
|
+
* @param {Object} manifest
|
|
362
|
+
* @param {Object} payload
|
|
363
|
+
*/
|
|
364
|
+
async function runUploadPublishAndSummary(systemKey, options, manifest, payload) {
|
|
365
|
+
const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey);
|
|
366
|
+
requireBearerForDataplanePipeline(authConfig);
|
|
367
|
+
logger.log('');
|
|
368
|
+
logger.log(sectionTitle('Target'));
|
|
369
|
+
logger.log(SEP);
|
|
370
|
+
logger.log(headerKeyValue('Environment:', environment));
|
|
371
|
+
logger.log(headerKeyValue('Dataplane:', dataplaneUrl));
|
|
372
|
+
logDataplanePipelineWarning();
|
|
373
|
+
if (options.verbose) {
|
|
374
|
+
await maybeRunVerboseServerValidation(dataplaneUrl, authConfig, payload);
|
|
375
|
+
}
|
|
376
|
+
await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload);
|
|
377
|
+
await logAndSyncLocalOpenApiForUpload(systemKey, manifest);
|
|
378
|
+
|
|
379
|
+
const rawRes = await runUploadValidatePublish(dataplaneUrl, authConfig, payload);
|
|
380
|
+
await handlePublicationAndFollowups({
|
|
381
|
+
systemKey,
|
|
382
|
+
options,
|
|
383
|
+
manifest,
|
|
384
|
+
payload,
|
|
385
|
+
environment,
|
|
386
|
+
dataplaneUrl,
|
|
387
|
+
authConfig,
|
|
388
|
+
rawRes
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
311
392
|
/**
|
|
312
393
|
* Uploads external system: publishes to dataplane and registers with controller (draft).
|
|
313
394
|
* @param {string} systemKey - External system key (integration/<systemKey>/)
|
|
@@ -322,8 +403,13 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
|
|
|
322
403
|
*/
|
|
323
404
|
async function uploadExternalSystem(systemKey, options = {}) {
|
|
324
405
|
validateSystemKeyFormat(systemKey);
|
|
325
|
-
logger.log(
|
|
326
|
-
|
|
406
|
+
logger.log('');
|
|
407
|
+
logger.log(sectionTitle('Upload external system'));
|
|
408
|
+
logger.log(SEP);
|
|
409
|
+
logger.log(headerKeyValue('System:', systemKey));
|
|
410
|
+
const { manifest, payload } = await buildValidatedUploadManifestPayload(systemKey, {
|
|
411
|
+
dryRun: !!options.dryRun
|
|
412
|
+
});
|
|
327
413
|
if (options.dryRun) {
|
|
328
414
|
logger.log(chalk.yellow('Dry run: would upload payload (no API calls).'));
|
|
329
415
|
logger.log(
|
|
@@ -15,6 +15,7 @@ const { getIntegrationPath } = require('../utils/paths');
|
|
|
15
15
|
const { parseOpenApi, testMcpConnection, credentialSelection } = require('../api/wizard.api');
|
|
16
16
|
const { listCredentials } = require('../api/credentials.api');
|
|
17
17
|
const { listExternalSystems, getExternalSystem } = require('../api/external-systems.api');
|
|
18
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Parse OpenAPI file or URL
|
|
@@ -35,7 +36,7 @@ async function parseOpenApiSource(dataplaneUrl, authConfig, sourceType, sourceDa
|
|
|
35
36
|
if (!parseResponse.success) {
|
|
36
37
|
throw new Error(`OpenAPI parsing failed: ${parseResponse.error || parseResponse.formattedError}`);
|
|
37
38
|
}
|
|
38
|
-
logger.log(
|
|
39
|
+
logger.log(formatSuccessLine(`OpenAPI ${isUrl ? 'URL' : 'file'} parsed successfully`));
|
|
39
40
|
return parseResponse.data?.spec;
|
|
40
41
|
} catch (error) {
|
|
41
42
|
spinner.stop();
|
|
@@ -61,7 +62,7 @@ async function testMcpServerConnection(dataplaneUrl, authConfig, sourceData) {
|
|
|
61
62
|
if (!testResponse.success || !testResponse.data?.connected) {
|
|
62
63
|
throw new Error(`MCP connection failed: ${testResponse.data?.error || 'Unable to connect'}`);
|
|
63
64
|
}
|
|
64
|
-
logger.log(
|
|
65
|
+
logger.log(formatSuccessLine('MCP server connection successful'));
|
|
65
66
|
} catch (error) {
|
|
66
67
|
spinner.stop();
|
|
67
68
|
throw error;
|
|
@@ -145,7 +146,7 @@ async function runCredentialSelectionLoop(dataplaneUrl, authConfig, selectionDat
|
|
|
145
146
|
}
|
|
146
147
|
if (response.success) {
|
|
147
148
|
const actionText = selectionData.action === 'create' ? 'created' : 'selected';
|
|
148
|
-
logger.log(
|
|
149
|
+
logger.log(formatSuccessLine(`Credential ${actionText}`));
|
|
149
150
|
return response.data?.credentialIdOrKey || null;
|
|
150
151
|
}
|
|
151
152
|
const errorMsg = response.error || response.formattedError || response.message || 'Unknown error';
|
|
@@ -308,18 +309,19 @@ function throwConfigGenerationError(generateResponse, options = {}) {
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
/**
|
|
311
|
-
* Write debug log to integration/<systemKey>/debug.log
|
|
312
|
+
* Write debug log to integration/<systemKey>/logs/debug.log
|
|
312
313
|
* @async
|
|
313
314
|
* @param {string} appName - Application name
|
|
314
315
|
* @param {string} content - Debug log content
|
|
315
316
|
*/
|
|
316
317
|
async function writeDebugLog(appName, content) {
|
|
317
318
|
try {
|
|
318
|
-
const
|
|
319
|
+
const appDir = getIntegrationPath(appName);
|
|
320
|
+
const dir = path.join(appDir, 'logs');
|
|
319
321
|
await fs.mkdir(dir, { recursive: true });
|
|
320
322
|
const debugPath = path.join(dir, 'debug.log');
|
|
321
323
|
await fs.writeFile(debugPath, content, 'utf8');
|
|
322
|
-
logger.log(chalk.gray(` Debug log saved to integration/${appName}/debug.log`));
|
|
324
|
+
logger.log(chalk.gray(` Debug log saved to integration/${appName}/logs/debug.log`));
|
|
323
325
|
} catch (e) {
|
|
324
326
|
logger.warn(`Could not save debug.log: ${e.message}`);
|
|
325
327
|
}
|
|
@@ -336,13 +338,14 @@ async function writeDebugLog(appName, content) {
|
|
|
336
338
|
async function writeDebugManifest(appName, systemConfig, datasourceConfig) {
|
|
337
339
|
const saved = [];
|
|
338
340
|
try {
|
|
339
|
-
const
|
|
341
|
+
const appDir = getIntegrationPath(appName);
|
|
342
|
+
const dir = path.join(appDir, 'logs');
|
|
340
343
|
await fs.mkdir(dir, { recursive: true });
|
|
341
344
|
if (systemConfig && typeof systemConfig === 'object') {
|
|
342
345
|
const systemPath = path.join(dir, 'debug-system.yaml');
|
|
343
346
|
await fs.writeFile(systemPath, yaml.dump(systemConfig, { lineWidth: -1 }), 'utf8');
|
|
344
347
|
saved.push('debug-system.yaml');
|
|
345
|
-
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-system.yaml`));
|
|
348
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/logs/debug-system.yaml`));
|
|
346
349
|
}
|
|
347
350
|
if (datasourceConfig !== undefined && datasourceConfig !== null) {
|
|
348
351
|
const configs = Array.isArray(datasourceConfig) ? datasourceConfig : [datasourceConfig];
|
|
@@ -351,7 +354,7 @@ async function writeDebugManifest(appName, systemConfig, datasourceConfig) {
|
|
|
351
354
|
const toWrite = configs.length === 1 ? configs[0] : configs;
|
|
352
355
|
await fs.writeFile(datasourcePath, yaml.dump(toWrite, { lineWidth: -1 }), 'utf8');
|
|
353
356
|
saved.push('debug-datasource.yaml');
|
|
354
|
-
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-datasource.yaml`));
|
|
357
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/logs/debug-datasource.yaml`));
|
|
355
358
|
}
|
|
356
359
|
}
|
|
357
360
|
} catch (e) {
|
|
@@ -381,7 +384,7 @@ async function saveDebugManifestOnErrorAndThrow(generateResponse, opts) {
|
|
|
381
384
|
const savedManifest = await writeDebugManifest(appName, systemConfig, datasourceConfig);
|
|
382
385
|
if (debugLog || savedManifest.length > 0) {
|
|
383
386
|
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
384
|
-
debugManifestHint = `Debug manifest saved to integration/${appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${appName}`;
|
|
387
|
+
debugManifestHint = `Debug manifest saved to integration/${appName}/logs/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${appName}`;
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
throwConfigGenerationError(generateResponse, { debugManifestHint });
|
|
@@ -398,7 +401,7 @@ async function throwValidationFailureWithDebug(validateResponse, systemConfig, c
|
|
|
398
401
|
);
|
|
399
402
|
if (!debugLog && savedManifest.length === 0) throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
400
403
|
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
401
|
-
throw new Error(`Configuration validation failed: ${errorMsg}\n\nDebug manifest saved to integration/${options.appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${options.appName}`);
|
|
404
|
+
throw new Error(`Configuration validation failed: ${errorMsg}\n\nDebug manifest saved to integration/${options.appName}/logs/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${options.appName}`);
|
|
402
405
|
}
|
|
403
406
|
|
|
404
407
|
/**
|
|
@@ -13,6 +13,7 @@ const logger = require('../utils/logger');
|
|
|
13
13
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
14
14
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
15
15
|
const { normalizeWizardConfigs } = require('./wizard-config-normalizer');
|
|
16
|
+
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-layout-chalk');
|
|
16
17
|
const {
|
|
17
18
|
createWizardSession,
|
|
18
19
|
updateWizardSession,
|
|
@@ -143,7 +144,7 @@ async function handleModeSelection(dataplaneUrl, authConfig, configMode = null,
|
|
|
143
144
|
throw new Error(fullMsg);
|
|
144
145
|
}
|
|
145
146
|
const sessionId = extractSessionId(sessionResponse.data);
|
|
146
|
-
logger.log(
|
|
147
|
+
logger.log(formatSuccessLine(`Session created: ${sessionId}`));
|
|
147
148
|
return { mode, sessionId };
|
|
148
149
|
}
|
|
149
150
|
|
|
@@ -250,7 +251,7 @@ async function handleTypeDetection(dataplaneUrl, authConfig, openapiSpec) {
|
|
|
250
251
|
if (detectResponse.success && detectResponse.data) {
|
|
251
252
|
const detectedType = detectResponse.data;
|
|
252
253
|
const recommendedType = detectedType.recommendedType || detectedType.apiType || 'unknown';
|
|
253
|
-
logger.log(
|
|
254
|
+
logger.log(formatSuccessLine(`API type detected: ${recommendedType}`));
|
|
254
255
|
return detectedType;
|
|
255
256
|
}
|
|
256
257
|
} catch (error) {
|
|
@@ -275,7 +276,8 @@ async function handleTypeDetection(dataplaneUrl, authConfig, openapiSpec) {
|
|
|
275
276
|
* @param {string} [options.systemIdOrKey] - System ID or key (optional)
|
|
276
277
|
* @param {string} [options.sourceType] - Source type (use 'known-platform' to call platforms config endpoint)
|
|
277
278
|
* @param {string} [options.platformKey] - Platform key for known-platform (e.g. 'hubspot')
|
|
278
|
-
* @param {string} [options.appName] -
|
|
279
|
+
* @param {string} [options.appName] - Integration app key; for create-system, used as systemIdOrKey
|
|
280
|
+
* when not set so kv paths match the folder (avoids spec-title keys like "companies")
|
|
279
281
|
* @returns {Promise<Object>} Generated configuration
|
|
280
282
|
*/
|
|
281
283
|
async function callGenerateApi(dataplaneUrl, authConfig, options, prefs) {
|
|
@@ -291,13 +293,19 @@ async function callGenerateApi(dataplaneUrl, authConfig, options, prefs) {
|
|
|
291
293
|
});
|
|
292
294
|
return await getPlatformConfig(dataplaneUrl, authConfig, options.platformKey, platformPayload);
|
|
293
295
|
}
|
|
296
|
+
// Headless/interactive create-system uses appName as integration folder key; OpenAPI title alone
|
|
297
|
+
// can yield a wrong system key (e.g. title "Companies" → "companies"). Prefer appName when set.
|
|
298
|
+
let systemIdOrKeyForPayload = options.systemIdOrKey;
|
|
299
|
+
if (options.mode === 'create-system' && options.appName) {
|
|
300
|
+
systemIdOrKeyForPayload = systemIdOrKeyForPayload || options.appName;
|
|
301
|
+
}
|
|
294
302
|
const configPayload = buildConfigPayload({
|
|
295
303
|
openapiSpec: options.openapiSpec,
|
|
296
304
|
detectedType: options.detectedType,
|
|
297
305
|
mode: options.mode,
|
|
298
306
|
prefs,
|
|
299
307
|
credentialIdOrKey: options.credentialIdOrKey,
|
|
300
|
-
systemIdOrKey:
|
|
308
|
+
systemIdOrKey: systemIdOrKeyForPayload,
|
|
301
309
|
entityName: options.entityName,
|
|
302
310
|
systemDisplayName: options.systemDisplayName
|
|
303
311
|
});
|
|
@@ -322,7 +330,7 @@ async function handleConfigurationGeneration(dataplaneUrl, authConfig, options)
|
|
|
322
330
|
await writeDebugLog(options.appName, debugLog);
|
|
323
331
|
}
|
|
324
332
|
}
|
|
325
|
-
logger.log(
|
|
333
|
+
logger.log(formatSuccessLine('Configuration generated successfully'));
|
|
326
334
|
return { systemConfig: normalized.systemConfig, datasourceConfigs: normalized.datasourceConfigs, systemKey: result.systemKey };
|
|
327
335
|
} catch (error) {
|
|
328
336
|
spinner.stop();
|
|
@@ -361,7 +369,7 @@ async function validateWizardConfiguration(dataplaneUrl, authConfig, systemConfi
|
|
|
361
369
|
if (validateResponse.data?.warnings?.length > 0) warnings.push(...validateResponse.data.warnings);
|
|
362
370
|
}
|
|
363
371
|
spinner.stop();
|
|
364
|
-
logger.log(
|
|
372
|
+
logger.log(formatSuccessLine('Configuration validated successfully'));
|
|
365
373
|
if (warnings.length > 0) logger.log(chalk.yellow('\n\u26A0 Warnings:\n' + warnings.map(w => ` - ${w.message || w}`).join('\n')));
|
|
366
374
|
} catch (error) {
|
|
367
375
|
spinner.stop();
|
|
@@ -410,6 +418,39 @@ async function tryUpdateReadmeFromDeploymentDocs(appPath, appName, dataplaneUrl,
|
|
|
410
418
|
}
|
|
411
419
|
}
|
|
412
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Writes rbac.yaml / rbac.json from datasource resourceType + capabilities (same as `af repair --rbac`).
|
|
423
|
+
* @param {Object} generatedFiles - Result of generateWizardFiles (appPath, systemFilePath, datasourceFilePaths)
|
|
424
|
+
* @param {string} format - Project format: yaml | json
|
|
425
|
+
*/
|
|
426
|
+
function logWizardFileSaveFooter(appName, generatedFiles) {
|
|
427
|
+
logger.log(formatSuccessParagraph('Wizard completed successfully!'));
|
|
428
|
+
logger.log(chalk.green(`\nFiles created in: ${generatedFiles.appPath}`));
|
|
429
|
+
logger.log(chalk.blue('\nNext steps:'));
|
|
430
|
+
logger.log(chalk.gray(` 1. Review the generated files in integration/${appName}/`));
|
|
431
|
+
logger.log(chalk.gray(' 2. Update env.template with your authentication details'));
|
|
432
|
+
logger.log(chalk.gray(` 3. Deploy using: node deploy.js or aifabrix deploy ${appName}`));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function mergeRbacAfterWizardFilesWritten(generatedFiles, format) {
|
|
436
|
+
const { mergeRbacFromDatasources, extractRbacFromSystem } = require('./repair-rbac');
|
|
437
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
438
|
+
const systemParsedForRbac = loadConfigFile(generatedFiles.systemFilePath);
|
|
439
|
+
const datasourceFileNames = (generatedFiles.datasourceFilePaths || []).map((p) => path.basename(p));
|
|
440
|
+
const rbacChanges = [];
|
|
441
|
+
const rbacUpdated = mergeRbacFromDatasources(
|
|
442
|
+
generatedFiles.appPath,
|
|
443
|
+
systemParsedForRbac,
|
|
444
|
+
datasourceFileNames,
|
|
445
|
+
extractRbacFromSystem,
|
|
446
|
+
{ format: format === 'json' ? 'json' : 'yaml', dryRun: false, changes: rbacChanges }
|
|
447
|
+
);
|
|
448
|
+
if (rbacUpdated && rbacChanges.length) {
|
|
449
|
+
rbacChanges.forEach((c) => logger.log(chalk.gray(` RBAC: ${c}`)));
|
|
450
|
+
logger.log(chalk.green(' RBAC file updated from datasource capabilities (enableRBAC).'));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
413
454
|
/**
|
|
414
455
|
* Handle file saving step
|
|
415
456
|
* @async
|
|
@@ -418,17 +459,24 @@ async function tryUpdateReadmeFromDeploymentDocs(appPath, appName, dataplaneUrl,
|
|
|
418
459
|
* @param {Object} systemConfig - System configuration
|
|
419
460
|
* @param {Object[]} datasourceConfigs - Datasource configurations
|
|
420
461
|
* @param {string} systemKey - System key
|
|
421
|
-
* @param {string}
|
|
422
|
-
* @param {Object} authConfig - Authentication configuration
|
|
462
|
+
* @param {{ dataplaneUrl: string, authConfig: Object, enableRBAC?: boolean }} ctx - Dataplane auth + optional RBAC generation
|
|
423
463
|
* @returns {Promise<Object>} Generated files information
|
|
424
464
|
*/
|
|
425
|
-
async function handleFileSaving(appName, systemConfig, datasourceConfigs, systemKey,
|
|
465
|
+
async function handleFileSaving(appName, systemConfig, datasourceConfigs, systemKey, ctx) {
|
|
466
|
+
const { dataplaneUrl, authConfig, enableRBAC = false } = ctx || {};
|
|
426
467
|
logger.log(chalk.blue('\n\uD83D\uDCCB Step 7: Save Files'));
|
|
427
468
|
const spinner = ora('Saving files...').start();
|
|
428
469
|
try {
|
|
429
470
|
const config = require('../core/config');
|
|
430
471
|
const format = (await config.getFormat()) || 'yaml';
|
|
431
472
|
const generatedFiles = await generateWizardFiles(appName, systemConfig, datasourceConfigs, systemKey, { aiGeneratedReadme: null, format });
|
|
473
|
+
if (enableRBAC && generatedFiles.appPath && generatedFiles.systemFilePath) {
|
|
474
|
+
try {
|
|
475
|
+
mergeRbacAfterWizardFilesWritten(generatedFiles, format);
|
|
476
|
+
} catch (e) {
|
|
477
|
+
logger.log(chalk.yellow(` Could not generate RBAC file: ${e.message}`));
|
|
478
|
+
}
|
|
479
|
+
}
|
|
432
480
|
if (systemKey && dataplaneUrl && authConfig && generatedFiles.appPath) {
|
|
433
481
|
try {
|
|
434
482
|
await tryUpdateReadmeFromDeploymentDocs(generatedFiles.appPath, appName, dataplaneUrl, authConfig, systemKey);
|
|
@@ -437,12 +485,7 @@ async function handleFileSaving(appName, systemConfig, datasourceConfigs, system
|
|
|
437
485
|
}
|
|
438
486
|
}
|
|
439
487
|
spinner.stop();
|
|
440
|
-
|
|
441
|
-
logger.log(chalk.green(`\nFiles created in: ${generatedFiles.appPath}`));
|
|
442
|
-
logger.log(chalk.blue('\nNext steps:'));
|
|
443
|
-
logger.log(chalk.gray(` 1. Review the generated files in integration/${appName}/`));
|
|
444
|
-
logger.log(chalk.gray(' 2. Update env.template with your authentication details'));
|
|
445
|
-
logger.log(chalk.gray(` 3. Deploy using: node deploy.js or aifabrix deploy ${appName}`));
|
|
488
|
+
logWizardFileSaveFooter(appName, generatedFiles);
|
|
446
489
|
return generatedFiles;
|
|
447
490
|
} catch (error) {
|
|
448
491
|
spinner.stop();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const logger = require('../utils/logger');
|
|
9
|
-
const {
|
|
9
|
+
const { formatProgress, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
10
10
|
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
11
11
|
const { listEnvironmentApplications } = require('../api/environments.api');
|
|
12
12
|
|
|
@@ -102,7 +102,7 @@ async function tryFallbackDataplaneUrl(controllerUrl, environment, authConfig, s
|
|
|
102
102
|
async function discoverDataplaneUrl(controllerUrl, environment, authConfig, opts = {}) {
|
|
103
103
|
const silent = opts.silent === true;
|
|
104
104
|
if (!silent) {
|
|
105
|
-
logger.log(
|
|
105
|
+
logger.log(formatProgress('Getting dataplane URL from controller...'));
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
108
108
|
const dataplaneAppKey = await findDataplaneServiceAppKey(controllerUrl, environment, authConfig);
|
|
@@ -9,31 +9,89 @@ const logger = require('../utils/logger');
|
|
|
9
9
|
const { discoverEntities } = require('../api/wizard.api');
|
|
10
10
|
const { validateEntityNameForOpenApi } = require('../validation/wizard-datasource-validation');
|
|
11
11
|
const { promptForEntitySelection } = require('../generator/wizard-prompts');
|
|
12
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* If wizard.yaml entity name matches discover-entities list, use it; else warn.
|
|
16
|
+
* @param {string} trimmed - Trimmed entity name from prefill
|
|
17
|
+
* @param {Array<{name: string}>} entities - Discovered entities
|
|
18
|
+
* @returns {string|null} Resolved name or null to prompt
|
|
19
|
+
*/
|
|
20
|
+
function resolvePrefillEntityName(trimmed, entities) {
|
|
21
|
+
const prefillCheck = validateEntityNameForOpenApi(trimmed, entities);
|
|
22
|
+
if (prefillCheck.valid) {
|
|
23
|
+
logger.log(chalk.gray(
|
|
24
|
+
`Using entity from wizard.yaml (${trimmed}). Skipping entity prompts.`
|
|
25
|
+
));
|
|
26
|
+
logger.log(formatSuccessLine(`Selected entity: ${trimmed}`));
|
|
27
|
+
return trimmed;
|
|
28
|
+
}
|
|
29
|
+
logger.log(chalk.yellow(
|
|
30
|
+
`Warning: wizard.yaml source.entityName '${trimmed}' is not in the discover-entities list; choose manually.`
|
|
31
|
+
));
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Prompt for entity and validate against list.
|
|
37
|
+
* @param {Array<{name: string}>} entities - Discovered entities
|
|
38
|
+
* @returns {Promise<string>} Valid entity name
|
|
39
|
+
*/
|
|
40
|
+
async function promptForValidatedEntity(entities) {
|
|
41
|
+
const entityName = await promptForEntitySelection(entities);
|
|
42
|
+
const validation = validateEntityNameForOpenApi(entityName, entities);
|
|
43
|
+
if (!validation.valid) {
|
|
44
|
+
throw new Error(`Invalid entity '${entityName}'. Available: ${entities.map(e => e.name).join(', ')}`);
|
|
45
|
+
}
|
|
46
|
+
logger.log(formatSuccessLine(`Selected entity: ${entityName}`));
|
|
47
|
+
return entityName;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Discover entities and select one (single-entity shortcut, prefill, or prompt).
|
|
52
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
53
|
+
* @param {Object} authConfig - Authentication configuration
|
|
54
|
+
* @param {Object} openapiSpec - OpenAPI specification
|
|
55
|
+
* @param {string} [prefillEntityName] - From wizard.yaml `source.entityName` when valid
|
|
56
|
+
* @returns {Promise<string|null>} Selected entity name or null (skip)
|
|
57
|
+
*/
|
|
58
|
+
async function discoverAndSelectEntity(dataplaneUrl, authConfig, openapiSpec, prefillEntityName) {
|
|
59
|
+
const response = await discoverEntities(dataplaneUrl, authConfig, openapiSpec);
|
|
60
|
+
const entities = response?.data?.entities;
|
|
61
|
+
if (!Array.isArray(entities) || entities.length === 0) return null;
|
|
62
|
+
|
|
63
|
+
logger.log(chalk.blue('\n\uD83D\uDCCB Step 4.5: Select Entity'));
|
|
64
|
+
|
|
65
|
+
if (entities.length === 1) {
|
|
66
|
+
const only = entities[0].name;
|
|
67
|
+
logger.log(formatSuccessLine(`Only one entity discovered; using: ${only}`));
|
|
68
|
+
return only;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const trimmed =
|
|
72
|
+
typeof prefillEntityName === 'string' ? prefillEntityName.trim() : '';
|
|
73
|
+
if (trimmed) {
|
|
74
|
+
const resolved = resolvePrefillEntityName(trimmed, entities);
|
|
75
|
+
if (resolved) return resolved;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return promptForValidatedEntity(entities);
|
|
79
|
+
}
|
|
12
80
|
|
|
13
81
|
/**
|
|
14
82
|
* Handle entity selection step (OpenAPI multi-entity).
|
|
15
|
-
* Calls discover-entities;
|
|
83
|
+
* Calls discover-entities; prompts unless prefill or a single entity applies.
|
|
16
84
|
* @async
|
|
17
85
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
18
86
|
* @param {Object} authConfig - Authentication configuration
|
|
19
87
|
* @param {Object} openapiSpec - OpenAPI specification
|
|
88
|
+
* @param {string} [prefillEntityName] - From wizard.yaml `source.entityName` when valid
|
|
20
89
|
* @returns {Promise<string|null>} Selected entity name or null (skip)
|
|
21
90
|
*/
|
|
22
|
-
async function handleEntitySelection(dataplaneUrl, authConfig, openapiSpec) {
|
|
91
|
+
async function handleEntitySelection(dataplaneUrl, authConfig, openapiSpec, prefillEntityName) {
|
|
23
92
|
if (!openapiSpec || typeof openapiSpec !== 'object') return null;
|
|
24
93
|
try {
|
|
25
|
-
|
|
26
|
-
const entities = response?.data?.entities;
|
|
27
|
-
if (!Array.isArray(entities) || entities.length === 0) return null;
|
|
28
|
-
|
|
29
|
-
logger.log(chalk.blue('\n\uD83D\uDCCB Step 4.5: Select Entity'));
|
|
30
|
-
const entityName = await promptForEntitySelection(entities);
|
|
31
|
-
const validation = validateEntityNameForOpenApi(entityName, entities);
|
|
32
|
-
if (!validation.valid) {
|
|
33
|
-
throw new Error(`Invalid entity '${entityName}'. Available: ${entities.map(e => e.name).join(', ')}`);
|
|
34
|
-
}
|
|
35
|
-
logger.log(chalk.green(`\u2713 Selected entity: ${entityName}`));
|
|
36
|
-
return entityName;
|
|
94
|
+
return await discoverAndSelectEntity(dataplaneUrl, authConfig, openapiSpec, prefillEntityName);
|
|
37
95
|
} catch (error) {
|
|
38
96
|
logger.log(chalk.yellow(`Warning: Entity discovery failed, using default: ${error.message}`));
|
|
39
97
|
return null;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const logger = require('../utils/logger');
|
|
9
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
9
10
|
const {
|
|
10
11
|
validateWizardConfig: validateWizardConfigFile,
|
|
11
12
|
displayValidationResults
|
|
@@ -111,8 +112,11 @@ async function executeWizardFromConfig(wizardConfig, dataplaneUrl, authConfig, o
|
|
|
111
112
|
systemConfig,
|
|
112
113
|
datasourceConfigs,
|
|
113
114
|
systemKey || appName,
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
{
|
|
116
|
+
dataplaneUrl,
|
|
117
|
+
authConfig,
|
|
118
|
+
enableRBAC: Boolean(preferences?.enableRBAC)
|
|
119
|
+
}
|
|
116
120
|
);
|
|
117
121
|
}
|
|
118
122
|
|
|
@@ -138,7 +142,7 @@ async function handleWizardHeadless(options) {
|
|
|
138
142
|
displayValidationResults(validationResult);
|
|
139
143
|
throw new Error('Wizard configuration validation failed');
|
|
140
144
|
}
|
|
141
|
-
logger.log(
|
|
145
|
+
logger.log(formatSuccessLine('Configuration file validated'));
|
|
142
146
|
|
|
143
147
|
const wizardConfig = validationResult.config;
|
|
144
148
|
const appName = wizardConfig.appName;
|
|
@@ -47,6 +47,12 @@ function buildSourceForSave(source) {
|
|
|
47
47
|
out.token = source.token ? '(set)' : undefined;
|
|
48
48
|
}
|
|
49
49
|
if (source.type === 'known-platform' && source.platform) out.platform = source.platform;
|
|
50
|
+
if (
|
|
51
|
+
(source.type === 'openapi-file' || source.type === 'openapi-url') &&
|
|
52
|
+
source.entityName
|
|
53
|
+
) {
|
|
54
|
+
out.entityName = source.entityName;
|
|
55
|
+
}
|
|
50
56
|
return out;
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -75,7 +81,13 @@ function buildWizardStateForSave(opts) {
|
|
|
75
81
|
function formatSourceLine(source) {
|
|
76
82
|
if (!source) return null;
|
|
77
83
|
const s = source;
|
|
78
|
-
|
|
84
|
+
let line =
|
|
85
|
+
s.type +
|
|
86
|
+
(s.filePath ? ` (${s.filePath})` : s.url ? ` (${s.url})` : s.platform ? ` (${s.platform})` : '');
|
|
87
|
+
if (s.entityName && (s.type === 'openapi-file' || s.type === 'openapi-url')) {
|
|
88
|
+
line += ` [entity: ${s.entityName}]`;
|
|
89
|
+
}
|
|
90
|
+
return line;
|
|
79
91
|
}
|
|
80
92
|
|
|
81
93
|
/**
|