@aifabrix/builder 2.32.2 → 2.33.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.
- package/.cursor/rules/project-rules.mdc +8 -0
- package/README.md +36 -8
- package/bin/aifabrix.js +6 -8
- package/integration/hubspot/README.md +8 -7
- package/integration/hubspot/companies.json +2048 -0
- package/integration/hubspot/create-hubspot.js +665 -0
- package/integration/hubspot/{hubspot-deploy-company.json → hubspot-datasource-company.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-contact.json → hubspot-datasource-contact.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-deal.json → hubspot-datasource-deal.json} +1 -1
- package/integration/hubspot/hubspot-deploy.json +832 -81
- package/integration/hubspot/hubspot-system.json +99 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-credential-real.yaml +20 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-env-vars.yaml +9 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-add-datasource.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-app-name.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-create.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-select.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-known-platform.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-app.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-mode.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-file.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-url.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-source.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-array-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +5 -0
- package/integration/hubspot/test-dataplane-down-helpers.js +246 -0
- package/integration/hubspot/test-dataplane-down-tests.js +419 -0
- package/integration/hubspot/test-dataplane-down.js +157 -0
- package/integration/hubspot/test.js +1517 -0
- package/integration/hubspot/variables.yaml +4 -4
- package/integration/hubspot/wizard-hubspot-e2e.yaml +16 -0
- package/integration/hubspot/wizard-hubspot-platform.yaml +8 -0
- package/lib/api/applications.api.js +1 -0
- package/lib/api/index.js +10 -5
- package/lib/api/types/wizard.types.js +176 -38
- package/lib/api/wizard.api.js +207 -38
- package/lib/app/deploy.js +116 -54
- package/lib/app/display.js +6 -5
- package/lib/app/dockerfile.js +2 -1
- package/lib/app/list.js +78 -37
- package/lib/app/prompts.js +9 -5
- package/lib/app/readme.js +41 -112
- package/lib/app/register.js +44 -9
- package/lib/app/rotate-secret.js +50 -32
- package/lib/cli.js +243 -65
- package/lib/commands/app.js +4 -9
- package/lib/commands/auth-config.js +125 -0
- package/lib/commands/auth-status.js +261 -0
- package/lib/commands/datasource.js +3 -6
- package/lib/commands/login-credentials.js +4 -4
- package/lib/commands/login-device.js +43 -29
- package/lib/commands/login.js +22 -13
- package/lib/commands/wizard-config-normalizer.js +92 -0
- package/lib/commands/wizard-core.js +515 -0
- package/lib/commands/wizard-dataplane.js +122 -0
- package/lib/commands/wizard-headless.js +115 -0
- package/lib/commands/wizard.js +129 -357
- package/lib/core/config.js +46 -0
- package/lib/core/secrets.js +3 -22
- package/lib/core/templates-env.js +1 -1
- package/lib/datasource/deploy.js +34 -23
- package/lib/datasource/list.js +8 -6
- package/lib/deployment/deployer.js +25 -0
- package/lib/deployment/environment.js +10 -13
- package/lib/external-system/delete.js +151 -0
- package/lib/external-system/deploy.js +54 -378
- package/lib/external-system/download-helpers.js +45 -65
- package/lib/external-system/download.js +34 -13
- package/lib/external-system/generator.js +11 -7
- package/lib/external-system/test-auth.js +5 -3
- package/lib/generator/builders.js +3 -1
- package/lib/generator/external-controller-manifest.js +157 -0
- package/lib/generator/external-schema-utils.js +236 -0
- package/lib/generator/external.js +55 -3
- package/lib/generator/index.js +22 -10
- package/lib/generator/wizard-prompts.js +33 -10
- package/lib/generator/wizard.js +69 -86
- package/lib/infrastructure/compose.js +100 -0
- package/lib/infrastructure/helpers.js +139 -0
- package/lib/infrastructure/index.js +52 -311
- package/lib/infrastructure/services.js +168 -0
- package/lib/schema/application-schema.json +24 -5
- package/lib/schema/external-datasource.schema.json +303 -17
- package/lib/schema/external-system.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +234 -0
- package/lib/utils/api.js +37 -42
- package/lib/utils/app-existence.js +42 -0
- package/lib/utils/app-register-config.js +7 -2
- package/lib/utils/app-register-display.js +2 -1
- package/lib/utils/auth-config-validator.js +92 -0
- package/lib/utils/cli-utils.js +3 -1
- package/lib/utils/command-header.js +43 -0
- package/lib/utils/compose-generator.js +113 -70
- package/lib/utils/controller-url.js +115 -0
- package/lib/utils/dataplane-health.js +115 -0
- package/lib/utils/dataplane-resolver.js +29 -0
- package/lib/utils/dev-config.js +6 -2
- package/lib/utils/env-copy.js +2 -1
- package/lib/utils/env-map.js +2 -1
- package/lib/utils/env-ports.js +2 -1
- package/lib/utils/env-template.js +1 -1
- package/lib/utils/error-formatter.js +149 -28
- package/lib/utils/external-readme.js +125 -0
- package/lib/utils/help-builder.js +190 -0
- package/lib/utils/infra-status.js +13 -3
- package/lib/utils/paths.js +17 -2
- package/lib/utils/port-resolver.js +111 -0
- package/lib/utils/secrets-helpers.js +3 -15
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/token-manager.js +69 -4
- package/lib/utils/variable-transformer.js +7 -2
- package/lib/validation/external-manifest-validator.js +202 -0
- package/lib/validation/validate-display.js +406 -0
- package/lib/validation/validate.js +159 -123
- package/lib/validation/validator.js +38 -4
- package/lib/validation/wizard-config-validator.js +267 -0
- package/package.json +4 -2
- package/templates/applications/README.md.hbs +19 -17
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +7 -7
- package/templates/external-system/README.md.hbs +99 -0
- package/templates/external-system/external-system.json.hbs +1 -1
- package/templates/infra/compose.yaml.hbs +35 -0
- package/templates/python/docker-compose.hbs +26 -0
- package/templates/typescript/docker-compose.hbs +26 -0
package/lib/commands/wizard.js
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Wizard command handler - interactive external system creation
|
|
2
|
+
* @fileoverview Wizard command handler - interactive and headless external system creation
|
|
3
3
|
* @author AI Fabrix Team
|
|
4
4
|
* @version 2.0.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
|
-
const ora = require('ora');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs').promises;
|
|
11
8
|
const logger = require('../utils/logger');
|
|
12
|
-
const config = require('../core/config');
|
|
13
|
-
const { getDeploymentAuth } = require('../utils/token-manager');
|
|
14
|
-
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
15
|
-
const {
|
|
16
|
-
selectMode,
|
|
17
|
-
selectSource,
|
|
18
|
-
parseOpenApi,
|
|
19
|
-
detectType,
|
|
20
|
-
generateConfig,
|
|
21
|
-
validateWizardConfig,
|
|
22
|
-
getDeploymentDocs
|
|
23
|
-
} = require('../api/wizard.api');
|
|
24
9
|
const {
|
|
25
10
|
promptForMode,
|
|
11
|
+
promptForSystemIdOrKey,
|
|
26
12
|
promptForSourceType,
|
|
27
13
|
promptForOpenApiFile,
|
|
28
14
|
promptForOpenApiUrl,
|
|
@@ -33,282 +19,148 @@ const {
|
|
|
33
19
|
promptForConfigReview,
|
|
34
20
|
promptForAppName
|
|
35
21
|
} = require('../generator/wizard-prompts');
|
|
36
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
validateAndCheckAppDirectory,
|
|
24
|
+
handleOpenApiParsing,
|
|
25
|
+
handleCredentialSelection,
|
|
26
|
+
handleTypeDetection,
|
|
27
|
+
handleConfigurationGeneration,
|
|
28
|
+
validateWizardConfiguration,
|
|
29
|
+
handleFileSaving,
|
|
30
|
+
setupDataplaneAndAuth
|
|
31
|
+
} = require('./wizard-core');
|
|
32
|
+
const { handleWizardHeadless } = require('./wizard-headless');
|
|
33
|
+
const { createWizardSession, updateWizardSession } = require('../api/wizard.api');
|
|
37
34
|
|
|
38
35
|
/**
|
|
39
|
-
*
|
|
40
|
-
* @
|
|
41
|
-
* @
|
|
42
|
-
* @
|
|
43
|
-
* @
|
|
44
|
-
* @throws {Error} If validation fails
|
|
36
|
+
* Extract session ID from response data
|
|
37
|
+
* @function extractSessionId
|
|
38
|
+
* @param {Object} responseData - Response data from API
|
|
39
|
+
* @returns {string} Session ID
|
|
40
|
+
* @throws {Error} If session ID not found or invalid
|
|
45
41
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
function extractSessionId(responseData) {
|
|
43
|
+
let sessionId = responseData?.data?.sessionId || responseData?.sessionId ||
|
|
44
|
+
responseData?.data?.session_id || responseData?.session_id;
|
|
45
|
+
if (sessionId && typeof sessionId === 'object') {
|
|
46
|
+
sessionId = sessionId.id || sessionId.sessionId || sessionId.session_id;
|
|
50
47
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const appPath = path.join(process.cwd(), 'integration', appName);
|
|
54
|
-
try {
|
|
55
|
-
await fs.access(appPath);
|
|
56
|
-
const { overwrite } = await require('inquirer').prompt([
|
|
57
|
-
{
|
|
58
|
-
type: 'confirm',
|
|
59
|
-
name: 'overwrite',
|
|
60
|
-
message: `Directory ${appPath} already exists. Overwrite?`,
|
|
61
|
-
default: false
|
|
62
|
-
}
|
|
63
|
-
]);
|
|
64
|
-
if (!overwrite) {
|
|
65
|
-
logger.log(chalk.yellow('Wizard cancelled.'));
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
} catch (error) {
|
|
69
|
-
// Directory doesn't exist, continue
|
|
70
|
-
if (error.code !== 'ENOENT') {
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
48
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
49
|
+
throw new Error(`Session ID not found: ${JSON.stringify(responseData, null, 2)}`);
|
|
73
50
|
}
|
|
74
|
-
return
|
|
51
|
+
return sessionId;
|
|
75
52
|
}
|
|
76
53
|
|
|
77
54
|
/**
|
|
78
|
-
* Handle mode selection step
|
|
55
|
+
* Handle interactive mode selection step
|
|
79
56
|
* @async
|
|
80
|
-
* @function
|
|
57
|
+
* @function handleInteractiveModeSelection
|
|
81
58
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
82
59
|
* @param {Object} authConfig - Authentication configuration
|
|
83
|
-
* @returns {Promise<
|
|
84
|
-
* @throws {Error} If mode selection fails
|
|
60
|
+
* @returns {Promise<Object>} Object with mode and sessionId
|
|
85
61
|
*/
|
|
86
|
-
async function
|
|
87
|
-
logger.log(chalk.blue('\n
|
|
62
|
+
async function handleInteractiveModeSelection(dataplaneUrl, authConfig) {
|
|
63
|
+
logger.log(chalk.blue('\n\uD83D\uDCCB Step 1: Mode Selection'));
|
|
88
64
|
const mode = await promptForMode();
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
65
|
+
let systemIdOrKey = null;
|
|
66
|
+
if (mode === 'add-datasource') {
|
|
67
|
+
systemIdOrKey = await promptForSystemIdOrKey();
|
|
92
68
|
}
|
|
93
|
-
|
|
69
|
+
const sessionResponse = await createWizardSession(dataplaneUrl, authConfig, mode, systemIdOrKey);
|
|
70
|
+
if (!sessionResponse.success || !sessionResponse.data) {
|
|
71
|
+
const errorMsg = sessionResponse.formattedError || sessionResponse.error ||
|
|
72
|
+
sessionResponse.errorData?.detail || sessionResponse.message ||
|
|
73
|
+
(sessionResponse.status ? `HTTP ${sessionResponse.status}` : 'Unknown error');
|
|
74
|
+
throw new Error(`Failed to create wizard session: ${errorMsg}`);
|
|
75
|
+
}
|
|
76
|
+
const sessionId = extractSessionId(sessionResponse.data);
|
|
77
|
+
return { mode, sessionId, systemIdOrKey };
|
|
94
78
|
}
|
|
95
79
|
|
|
96
80
|
/**
|
|
97
|
-
* Handle source selection step
|
|
81
|
+
* Handle interactive source selection step
|
|
98
82
|
* @async
|
|
99
|
-
* @function
|
|
83
|
+
* @function handleInteractiveSourceSelection
|
|
100
84
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
85
|
+
* @param {string} sessionId - Wizard session ID
|
|
101
86
|
* @param {Object} authConfig - Authentication configuration
|
|
102
87
|
* @returns {Promise<Object>} Object with sourceType and sourceData
|
|
103
|
-
* @throws {Error} If source selection fails
|
|
104
88
|
*/
|
|
105
|
-
async function
|
|
106
|
-
logger.log(chalk.blue('\n
|
|
89
|
+
async function handleInteractiveSourceSelection(dataplaneUrl, sessionId, authConfig) {
|
|
90
|
+
logger.log(chalk.blue('\n\uD83D\uDCCB Step 2: Source Selection'));
|
|
107
91
|
const sourceType = await promptForSourceType();
|
|
108
92
|
let sourceData = null;
|
|
93
|
+
const updateData = { currentStep: 1 };
|
|
109
94
|
|
|
110
95
|
if (sourceType === 'openapi-file') {
|
|
111
|
-
|
|
112
|
-
sourceData = filePath;
|
|
96
|
+
sourceData = await promptForOpenApiFile();
|
|
113
97
|
} else if (sourceType === 'openapi-url') {
|
|
114
|
-
|
|
115
|
-
|
|
98
|
+
sourceData = await promptForOpenApiUrl();
|
|
99
|
+
updateData.openapiSpec = null;
|
|
116
100
|
} else if (sourceType === 'mcp-server') {
|
|
117
101
|
const mcpDetails = await promptForMcpServer();
|
|
118
102
|
sourceData = JSON.stringify(mcpDetails);
|
|
103
|
+
updateData.mcpServerUrl = mcpDetails.url || null;
|
|
119
104
|
} else if (sourceType === 'known-platform') {
|
|
120
|
-
|
|
121
|
-
sourceData = platform;
|
|
105
|
+
sourceData = await promptForKnownPlatform();
|
|
122
106
|
}
|
|
123
107
|
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
126
|
-
throw new Error(`Source selection failed: ${
|
|
108
|
+
const updateResponse = await updateWizardSession(dataplaneUrl, sessionId, authConfig, updateData);
|
|
109
|
+
if (!updateResponse.success) {
|
|
110
|
+
throw new Error(`Source selection failed: ${updateResponse.error || updateResponse.formattedError}`);
|
|
127
111
|
}
|
|
128
112
|
|
|
129
113
|
return { sourceType, sourceData };
|
|
130
114
|
}
|
|
131
115
|
|
|
132
116
|
/**
|
|
133
|
-
*
|
|
134
|
-
* @async
|
|
135
|
-
* @function parseOpenApiFile
|
|
136
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
137
|
-
* @param {Object} authConfig - Authentication configuration
|
|
138
|
-
* @param {string} sourceData - Source data (file path)
|
|
139
|
-
* @returns {Promise<Object>} OpenAPI spec
|
|
140
|
-
* @throws {Error} If parsing fails
|
|
141
|
-
*/
|
|
142
|
-
async function parseOpenApiFile(dataplaneUrl, authConfig, sourceData) {
|
|
143
|
-
logger.log(chalk.blue('\n📋 Step 3: Parsing OpenAPI File'));
|
|
144
|
-
const spinner = ora('Parsing OpenAPI file...').start();
|
|
145
|
-
try {
|
|
146
|
-
const parseResponse = await parseOpenApi(dataplaneUrl, authConfig, sourceData);
|
|
147
|
-
spinner.stop();
|
|
148
|
-
if (!parseResponse.success) {
|
|
149
|
-
throw new Error(`OpenAPI parsing failed: ${parseResponse.error || parseResponse.formattedError}`);
|
|
150
|
-
}
|
|
151
|
-
logger.log(chalk.green('✓ OpenAPI file parsed successfully'));
|
|
152
|
-
return parseResponse.data?.spec;
|
|
153
|
-
} catch (error) {
|
|
154
|
-
spinner.stop();
|
|
155
|
-
throw error;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Handle OpenAPI parsing step
|
|
161
|
-
* @async
|
|
162
|
-
* @function handleOpenApiParsing
|
|
163
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
164
|
-
* @param {Object} authConfig - Authentication configuration
|
|
165
|
-
* @param {string} sourceType - Source type
|
|
166
|
-
* @param {string} sourceData - Source data
|
|
167
|
-
* @returns {Promise<Object|null>} OpenAPI spec or null
|
|
168
|
-
* @throws {Error} If parsing fails
|
|
169
|
-
*/
|
|
170
|
-
async function handleOpenApiParsing(dataplaneUrl, authConfig, sourceType, sourceData) {
|
|
171
|
-
if (sourceType === 'openapi-file') {
|
|
172
|
-
return await parseOpenApiFile(dataplaneUrl, authConfig, sourceData);
|
|
173
|
-
}
|
|
174
|
-
if (sourceType === 'openapi-url') {
|
|
175
|
-
logger.log(chalk.blue('\n📋 Step 3: Parsing OpenAPI URL'));
|
|
176
|
-
logger.log(chalk.green('✓ OpenAPI URL processed'));
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Handle type detection step
|
|
184
|
-
* @async
|
|
185
|
-
* @function handleTypeDetection
|
|
186
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
187
|
-
* @param {Object} authConfig - Authentication configuration
|
|
188
|
-
* @param {Object} openApiSpec - OpenAPI specification
|
|
189
|
-
* @returns {Promise<Object|null>} Detected type or null
|
|
190
|
-
*/
|
|
191
|
-
async function handleTypeDetection(dataplaneUrl, authConfig, openApiSpec) {
|
|
192
|
-
if (!openApiSpec) {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
logger.log(chalk.blue('\n📋 Step 4: Detecting API Type'));
|
|
197
|
-
const spinner = ora('Detecting API type...').start();
|
|
198
|
-
try {
|
|
199
|
-
const detectResponse = await detectType(dataplaneUrl, authConfig, openApiSpec);
|
|
200
|
-
spinner.stop();
|
|
201
|
-
if (detectResponse.success && detectResponse.data) {
|
|
202
|
-
const detectedType = detectResponse.data;
|
|
203
|
-
logger.log(chalk.green(`✓ API type detected: ${detectedType.apiType || 'unknown'}`));
|
|
204
|
-
if (detectedType.category) {
|
|
205
|
-
logger.log(chalk.gray(` Category: ${detectedType.category}`));
|
|
206
|
-
}
|
|
207
|
-
return detectedType;
|
|
208
|
-
}
|
|
209
|
-
} catch (error) {
|
|
210
|
-
spinner.stop();
|
|
211
|
-
logger.log(chalk.yellow(`⚠ Type detection failed: ${error.message}`));
|
|
212
|
-
}
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Handle configuration generation step
|
|
117
|
+
* Handle interactive configuration generation step
|
|
218
118
|
* @async
|
|
219
|
-
* @function
|
|
220
|
-
* @param {
|
|
221
|
-
* @param {
|
|
222
|
-
* @param {
|
|
223
|
-
* @param {string}
|
|
224
|
-
* @param {Object}
|
|
225
|
-
* @
|
|
226
|
-
* @
|
|
119
|
+
* @function handleInteractiveConfigGeneration
|
|
120
|
+
* @param {Object} options - Configuration options
|
|
121
|
+
* @param {string} options.dataplaneUrl - Dataplane URL
|
|
122
|
+
* @param {Object} options.authConfig - Authentication configuration
|
|
123
|
+
* @param {string} options.mode - Selected mode
|
|
124
|
+
* @param {Object} options.openapiSpec - OpenAPI specification
|
|
125
|
+
* @param {Object} options.detectedType - Detected type info
|
|
126
|
+
* @param {string} [options.credentialIdOrKey] - Credential ID or key (optional)
|
|
127
|
+
* @param {string} [options.systemIdOrKey] - System ID or key (optional)
|
|
128
|
+
* @returns {Promise<Object>} Generated configuration
|
|
227
129
|
*/
|
|
228
|
-
async function
|
|
229
|
-
logger.log(chalk.blue('\n
|
|
130
|
+
async function handleInteractiveConfigGeneration(options) {
|
|
131
|
+
logger.log(chalk.blue('\n\uD83D\uDCCB Step 5: User Preferences'));
|
|
230
132
|
const userIntent = await promptForUserIntent();
|
|
231
133
|
const preferences = await promptForUserPreferences();
|
|
232
134
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const systemConfig = generateResponse.data?.systemConfig;
|
|
249
|
-
const datasourceConfigs = generateResponse.data?.datasourceConfigs || [];
|
|
250
|
-
const systemKey = generateResponse.data?.systemKey;
|
|
251
|
-
|
|
252
|
-
if (!systemConfig) {
|
|
253
|
-
throw new Error('System configuration not found in generation response');
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
logger.log(chalk.green('✓ Configuration generated successfully'));
|
|
257
|
-
return { systemConfig, datasourceConfigs, systemKey };
|
|
258
|
-
} catch (error) {
|
|
259
|
-
spinner.stop();
|
|
260
|
-
throw error;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Validate wizard configuration
|
|
266
|
-
* @async
|
|
267
|
-
* @function validateWizardConfiguration
|
|
268
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
269
|
-
* @param {Object} authConfig - Authentication configuration
|
|
270
|
-
* @param {Object} systemConfig - System configuration
|
|
271
|
-
* @param {Object[]} datasourceConfigs - Datasource configurations
|
|
272
|
-
* @throws {Error} If validation fails
|
|
273
|
-
*/
|
|
274
|
-
async function validateWizardConfiguration(dataplaneUrl, authConfig, systemConfig, datasourceConfigs) {
|
|
275
|
-
const validateSpinner = ora('Validating configuration...').start();
|
|
276
|
-
try {
|
|
277
|
-
const validateResponse = await validateWizardConfig(dataplaneUrl, authConfig, systemConfig, datasourceConfigs);
|
|
278
|
-
validateSpinner.stop();
|
|
279
|
-
if (!validateResponse.success || !validateResponse.data?.valid) {
|
|
280
|
-
const errors = validateResponse.data?.errors || [];
|
|
281
|
-
const errorMsg = errors.length > 0
|
|
282
|
-
? errors.map(e => e.message || e).join(', ')
|
|
283
|
-
: validateResponse.error || validateResponse.formattedError || 'Validation failed';
|
|
284
|
-
throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
285
|
-
}
|
|
286
|
-
logger.log(chalk.green('✓ Configuration validated successfully'));
|
|
287
|
-
if (validateResponse.data?.warnings && validateResponse.data.warnings.length > 0) {
|
|
288
|
-
logger.log(chalk.yellow('\n⚠ Warnings:'));
|
|
289
|
-
validateResponse.data.warnings.forEach(warning => {
|
|
290
|
-
logger.log(chalk.yellow(` - ${warning.message || warning}`));
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
} catch (error) {
|
|
294
|
-
validateSpinner.stop();
|
|
295
|
-
throw error;
|
|
296
|
-
}
|
|
135
|
+
const configPrefs = {
|
|
136
|
+
intent: userIntent,
|
|
137
|
+
enableMCP: preferences.mcp,
|
|
138
|
+
enableABAC: preferences.abac,
|
|
139
|
+
enableRBAC: preferences.rbac
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return await handleConfigurationGeneration(options.dataplaneUrl, options.authConfig, {
|
|
143
|
+
mode: options.mode,
|
|
144
|
+
openapiSpec: options.openapiSpec,
|
|
145
|
+
detectedType: options.detectedType,
|
|
146
|
+
configPrefs,
|
|
147
|
+
credentialIdOrKey: options.credentialIdOrKey,
|
|
148
|
+
systemIdOrKey: options.systemIdOrKey
|
|
149
|
+
});
|
|
297
150
|
}
|
|
298
151
|
|
|
299
152
|
/**
|
|
300
|
-
* Handle configuration review and validation step
|
|
153
|
+
* Handle configuration review and validation step (interactive mode only)
|
|
301
154
|
* @async
|
|
302
155
|
* @function handleConfigurationReview
|
|
303
156
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
304
157
|
* @param {Object} authConfig - Authentication configuration
|
|
305
158
|
* @param {Object} systemConfig - System configuration
|
|
306
159
|
* @param {Object[]} datasourceConfigs - Datasource configurations
|
|
307
|
-
* @returns {Promise<Object>} Final configurations
|
|
308
|
-
* @throws {Error} If validation fails
|
|
160
|
+
* @returns {Promise<Object>} Final configurations
|
|
309
161
|
*/
|
|
310
162
|
async function handleConfigurationReview(dataplaneUrl, authConfig, systemConfig, datasourceConfigs) {
|
|
311
|
-
logger.log(chalk.blue('\n
|
|
163
|
+
logger.log(chalk.blue('\n\uD83D\uDCCB Step 6-7: Review & Validate'));
|
|
312
164
|
const reviewResult = await promptForConfigReview(systemConfig, datasourceConfigs);
|
|
313
165
|
|
|
314
166
|
if (reviewResult.action === 'cancel') {
|
|
@@ -316,97 +168,55 @@ async function handleConfigurationReview(dataplaneUrl, authConfig, systemConfig,
|
|
|
316
168
|
return null;
|
|
317
169
|
}
|
|
318
170
|
|
|
319
|
-
// Use edited configs if user edited them
|
|
320
171
|
const finalSystemConfig = reviewResult.systemConfig || systemConfig;
|
|
321
172
|
const finalDatasourceConfigs = reviewResult.datasourceConfigs || datasourceConfigs;
|
|
322
173
|
|
|
323
|
-
// Validate configuration
|
|
324
174
|
await validateWizardConfiguration(dataplaneUrl, authConfig, finalSystemConfig, finalDatasourceConfigs);
|
|
325
175
|
|
|
326
176
|
return { systemConfig: finalSystemConfig, datasourceConfigs: finalDatasourceConfigs };
|
|
327
177
|
}
|
|
328
178
|
|
|
329
179
|
/**
|
|
330
|
-
*
|
|
331
|
-
* @async
|
|
332
|
-
* @function handleFileSaving
|
|
333
|
-
* @param {string} appName - Application name
|
|
334
|
-
* @param {Object} systemConfig - System configuration
|
|
335
|
-
* @param {Object[]} datasourceConfigs - Datasource configurations
|
|
336
|
-
* @param {string} systemKey - System key
|
|
337
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
338
|
-
* @param {Object} authConfig - Authentication configuration
|
|
339
|
-
* @returns {Promise<Object>} Generated files information
|
|
340
|
-
* @throws {Error} If file saving fails
|
|
341
|
-
*/
|
|
342
|
-
async function handleFileSaving(appName, systemConfig, datasourceConfigs, systemKey, dataplaneUrl, authConfig) {
|
|
343
|
-
logger.log(chalk.blue('\n📋 Step 8: Saving Files'));
|
|
344
|
-
const saveSpinner = ora('Saving files...').start();
|
|
345
|
-
try {
|
|
346
|
-
let aiGeneratedReadme = null;
|
|
347
|
-
if (systemKey && dataplaneUrl && authConfig) {
|
|
348
|
-
try {
|
|
349
|
-
const docsResponse = await getDeploymentDocs(dataplaneUrl, authConfig, systemKey);
|
|
350
|
-
if (docsResponse.success && docsResponse.data?.content) {
|
|
351
|
-
aiGeneratedReadme = docsResponse.data.content;
|
|
352
|
-
logger.log(chalk.gray(' ✓ Fetched AI-generated README.md from dataplane'));
|
|
353
|
-
}
|
|
354
|
-
} catch (error) {
|
|
355
|
-
logger.log(chalk.gray(` ⚠ Could not fetch AI-generated README: ${error.message}`));
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
const generatedFiles = await generateWizardFiles(appName, systemConfig, datasourceConfigs, systemKey, { aiGeneratedReadme });
|
|
359
|
-
saveSpinner.stop();
|
|
360
|
-
logger.log(chalk.green('\n✓ Wizard completed successfully!'));
|
|
361
|
-
logger.log(chalk.green(`\nFiles created in: ${generatedFiles.appPath}`));
|
|
362
|
-
logger.log(chalk.blue('\nNext steps:'));
|
|
363
|
-
[` 1. Review the generated files in integration/${appName}/`, ' 2. Update env.template with your authentication details', ` 3. Deploy using: ./deploy.sh or .\\deploy.ps1 (or aifabrix deploy ${appName})`].forEach(step => logger.log(chalk.gray(step)));
|
|
364
|
-
return generatedFiles;
|
|
365
|
-
} catch (error) {
|
|
366
|
-
saveSpinner.stop();
|
|
367
|
-
throw error;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Execute wizard flow steps
|
|
180
|
+
* Execute wizard flow steps (interactive mode)
|
|
373
181
|
* @async
|
|
374
182
|
* @function executeWizardFlow
|
|
375
183
|
* @param {string} appName - Application name
|
|
376
184
|
* @param {string} dataplaneUrl - Dataplane URL
|
|
377
185
|
* @param {Object} authConfig - Authentication configuration
|
|
378
186
|
* @returns {Promise<void>} Resolves when wizard flow completes
|
|
379
|
-
* @throws {Error} If wizard flow fails
|
|
380
187
|
*/
|
|
381
188
|
async function executeWizardFlow(appName, dataplaneUrl, authConfig) {
|
|
382
189
|
// Step 1: Mode Selection
|
|
383
|
-
const mode = await
|
|
190
|
+
const { mode, sessionId, systemIdOrKey } = await handleInteractiveModeSelection(dataplaneUrl, authConfig);
|
|
384
191
|
|
|
385
192
|
// Step 2: Source Selection
|
|
386
|
-
const { sourceType, sourceData } = await
|
|
193
|
+
const { sourceType, sourceData } = await handleInteractiveSourceSelection(dataplaneUrl, sessionId, authConfig);
|
|
387
194
|
|
|
388
|
-
//
|
|
389
|
-
const
|
|
195
|
+
// Parse OpenAPI (part of step 2)
|
|
196
|
+
const openapiSpec = await handleOpenApiParsing(dataplaneUrl, authConfig, sourceType, sourceData);
|
|
390
197
|
|
|
391
|
-
// Step
|
|
392
|
-
await
|
|
198
|
+
// Step 3: Credential Selection (optional)
|
|
199
|
+
const credentialIdOrKey = await handleCredentialSelection(dataplaneUrl, authConfig);
|
|
393
200
|
|
|
394
|
-
// Step
|
|
395
|
-
const
|
|
201
|
+
// Step 4: Detect Type
|
|
202
|
+
const detectedType = await handleTypeDetection(dataplaneUrl, authConfig, openapiSpec);
|
|
203
|
+
|
|
204
|
+
// Step 5: Generate Configuration
|
|
205
|
+
const { systemConfig, datasourceConfigs, systemKey } = await handleInteractiveConfigGeneration({
|
|
396
206
|
dataplaneUrl,
|
|
397
207
|
authConfig,
|
|
398
208
|
mode,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
209
|
+
openapiSpec,
|
|
210
|
+
detectedType,
|
|
211
|
+
credentialIdOrKey,
|
|
212
|
+
systemIdOrKey
|
|
213
|
+
});
|
|
402
214
|
|
|
403
|
-
// Step 7: Review & Validate
|
|
215
|
+
// Step 6-7: Review & Validate
|
|
404
216
|
const finalConfigs = await handleConfigurationReview(dataplaneUrl, authConfig, systemConfig, datasourceConfigs);
|
|
405
|
-
if (!finalConfigs)
|
|
406
|
-
return; // User cancelled
|
|
407
|
-
}
|
|
217
|
+
if (!finalConfigs) return;
|
|
408
218
|
|
|
409
|
-
// Step
|
|
219
|
+
// Step 7: Save Files
|
|
410
220
|
await handleFileSaving(
|
|
411
221
|
appName,
|
|
412
222
|
finalConfigs.systemConfig,
|
|
@@ -426,73 +236,35 @@ async function executeWizardFlow(appName, dataplaneUrl, authConfig) {
|
|
|
426
236
|
* @param {string} [options.controller] - Controller URL
|
|
427
237
|
* @param {string} [options.environment] - Environment key
|
|
428
238
|
* @param {string} [options.dataplane] - Dataplane URL (overrides controller lookup)
|
|
239
|
+
* @param {string} [options.config] - Path to wizard.yaml config file (headless mode)
|
|
429
240
|
* @returns {Promise<void>} Resolves when wizard completes
|
|
430
241
|
* @throws {Error} If wizard fails
|
|
431
242
|
*/
|
|
432
243
|
async function handleWizard(options = {}) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
// Get or prompt for app name
|
|
437
|
-
let appName = options.app;
|
|
438
|
-
if (!appName) {
|
|
439
|
-
appName = await promptForAppName();
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Validate app name and check directory
|
|
443
|
-
const shouldContinue = await validateAndCheckAppDirectory(appName);
|
|
444
|
-
if (!shouldContinue) {
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Get dataplane URL and authentication
|
|
449
|
-
const { dataplaneUrl, authConfig } = await setupDataplaneAndAuth(options, appName);
|
|
450
|
-
|
|
451
|
-
// Execute wizard flow
|
|
452
|
-
await executeWizardFlow(appName, dataplaneUrl, authConfig);
|
|
453
|
-
} catch (error) {
|
|
454
|
-
logger.error(chalk.red(`\n❌ Wizard failed: ${error.message}`));
|
|
455
|
-
throw error;
|
|
244
|
+
// Check if headless mode (config file provided)
|
|
245
|
+
if (options.config) {
|
|
246
|
+
return await handleWizardHeadless(options);
|
|
456
247
|
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Setup dataplane URL and authentication
|
|
461
|
-
* @async
|
|
462
|
-
* @function setupDataplaneAndAuth
|
|
463
|
-
* @param {Object} options - Command options
|
|
464
|
-
* @param {string} appName - Application name
|
|
465
|
-
* @returns {Promise<Object>} Object with dataplaneUrl and authConfig
|
|
466
|
-
* @throws {Error} If setup fails
|
|
467
|
-
*/
|
|
468
|
-
async function setupDataplaneAndAuth(options, appName) {
|
|
469
|
-
const configData = await config.getConfig();
|
|
470
|
-
const environment = options.environment || 'dev';
|
|
471
|
-
const controllerUrl = options.controller || configData.deployment?.controllerUrl || 'http://localhost:3000';
|
|
472
|
-
|
|
473
|
-
// Get dataplane URL (either from option or from controller)
|
|
474
|
-
let dataplaneUrl = options.dataplane;
|
|
475
|
-
if (!dataplaneUrl) {
|
|
476
|
-
// Get authentication first
|
|
477
|
-
const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
|
|
478
|
-
if (!authConfig.token && !authConfig.clientId) {
|
|
479
|
-
throw new Error('Authentication required. Run "aifabrix login" or "aifabrix app register" first.');
|
|
480
|
-
}
|
|
481
248
|
|
|
482
|
-
|
|
483
|
-
logger.log(chalk.blue('🌐 Getting dataplane URL from controller...'));
|
|
484
|
-
dataplaneUrl = await getDataplaneUrl(controllerUrl, appName, environment, authConfig);
|
|
485
|
-
logger.log(chalk.green(`✓ Dataplane URL: ${dataplaneUrl}`));
|
|
249
|
+
logger.log(chalk.blue('\n\uD83E\uDDD9 AI Fabrix External System Wizard\n'));
|
|
486
250
|
|
|
487
|
-
|
|
251
|
+
// Get or prompt for app name
|
|
252
|
+
let appName = options.app;
|
|
253
|
+
if (!appName) {
|
|
254
|
+
appName = await promptForAppName();
|
|
488
255
|
}
|
|
489
256
|
|
|
490
|
-
//
|
|
491
|
-
const
|
|
492
|
-
if (!
|
|
493
|
-
|
|
257
|
+
// Validate app name and check directory
|
|
258
|
+
const shouldContinue = await validateAndCheckAppDirectory(appName, true);
|
|
259
|
+
if (!shouldContinue) {
|
|
260
|
+
return;
|
|
494
261
|
}
|
|
495
262
|
|
|
496
|
-
|
|
263
|
+
// Get dataplane URL and authentication
|
|
264
|
+
const { dataplaneUrl, authConfig } = await setupDataplaneAndAuth(options, appName);
|
|
265
|
+
|
|
266
|
+
// Execute wizard flow
|
|
267
|
+
await executeWizardFlow(appName, dataplaneUrl, authConfig);
|
|
497
268
|
}
|
|
498
|
-
|
|
269
|
+
|
|
270
|
+
module.exports = { handleWizard, handleWizardHeadless };
|