@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/api/wizard.api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Wizard API functions
|
|
2
|
+
* @fileoverview Wizard API functions for external system creation
|
|
3
3
|
* @author AI Fabrix Team
|
|
4
4
|
* @version 2.0.0
|
|
5
5
|
*/
|
|
@@ -8,59 +8,147 @@ const { ApiClient } = require('./index');
|
|
|
8
8
|
const { uploadFile } = require('../utils/file-upload');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* POST /api/v1/wizard/
|
|
11
|
+
* Create wizard session
|
|
12
|
+
* POST /api/v1/wizard/sessions
|
|
13
13
|
* @async
|
|
14
|
-
* @function
|
|
14
|
+
* @function createWizardSession
|
|
15
15
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
16
16
|
* @param {Object} authConfig - Authentication configuration
|
|
17
17
|
* @param {string} mode - Wizard mode ('create-system' | 'add-datasource')
|
|
18
|
-
* @
|
|
18
|
+
* @param {string} [systemIdOrKey] - Existing system ID or key (required when mode='add-datasource')
|
|
19
|
+
* @returns {Promise<Object>} Session creation response with sessionId
|
|
19
20
|
* @throws {Error} If request fails
|
|
20
21
|
*/
|
|
21
|
-
async function
|
|
22
|
+
async function createWizardSession(dataplaneUrl, authConfig, mode, systemIdOrKey = null) {
|
|
22
23
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const body = { mode };
|
|
25
|
+
if (systemIdOrKey) {
|
|
26
|
+
body.systemIdOrKey = systemIdOrKey;
|
|
27
|
+
}
|
|
28
|
+
return await client.post('/api/v1/wizard/sessions', {
|
|
29
|
+
body
|
|
25
30
|
});
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
34
|
+
* Get wizard session
|
|
35
|
+
* GET /api/v1/wizard/sessions/{sessionId}
|
|
31
36
|
* @async
|
|
32
|
-
* @function
|
|
37
|
+
* @function getWizardSession
|
|
33
38
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
39
|
+
* @param {string} sessionId - Session ID
|
|
34
40
|
* @param {Object} authConfig - Authentication configuration
|
|
35
|
-
* @
|
|
36
|
-
* @param {string} [sourceData] - Source data (file path, URL, etc.)
|
|
37
|
-
* @returns {Promise<Object>} Source selection response
|
|
41
|
+
* @returns {Promise<Object>} Session state response
|
|
38
42
|
* @throws {Error} If request fails
|
|
39
43
|
*/
|
|
40
|
-
async function
|
|
44
|
+
async function getWizardSession(dataplaneUrl, sessionId, authConfig) {
|
|
41
45
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
42
|
-
return await client.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
return await client.get(`/api/v1/wizard/sessions/${sessionId}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Update wizard session
|
|
51
|
+
* PUT /api/v1/wizard/sessions/{sessionId}
|
|
52
|
+
* @async
|
|
53
|
+
* @function updateWizardSession
|
|
54
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
55
|
+
* @param {string} sessionId - Session ID
|
|
56
|
+
* @param {Object} authConfig - Authentication configuration
|
|
57
|
+
* @param {Object} updateData - Session update data
|
|
58
|
+
* @param {number} [updateData.currentStep] - Current wizard step (0-6)
|
|
59
|
+
* @param {string} [updateData.credentialIdOrKey] - Selected credential ID or key
|
|
60
|
+
* @param {Object} [updateData.openapiSpec] - Parsed OpenAPI specification
|
|
61
|
+
* @param {string} [updateData.mcpServerUrl] - MCP server URL
|
|
62
|
+
* @param {Array} [updateData.detectedTypes] - Detected API types
|
|
63
|
+
* @param {string} [updateData.selectedType] - Selected API type
|
|
64
|
+
* @param {string} [updateData.intent] - User intent
|
|
65
|
+
* @param {string} [updateData.fieldOnboardingLevel] - Field onboarding level
|
|
66
|
+
* @param {boolean} [updateData.enableOpenAPIGeneration] - Enable OpenAPI generation
|
|
67
|
+
* @param {Object} [updateData.systemConfig] - Generated system configuration
|
|
68
|
+
* @param {Object} [updateData.datasourceConfig] - Generated datasource configuration
|
|
69
|
+
* @param {Object} [updateData.validationResults] - Validation results
|
|
70
|
+
* @returns {Promise<Object>} Updated session state response
|
|
71
|
+
* @throws {Error} If request fails
|
|
72
|
+
*/
|
|
73
|
+
async function updateWizardSession(dataplaneUrl, sessionId, authConfig, updateData) {
|
|
74
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
75
|
+
return await client.put(`/api/v1/wizard/sessions/${sessionId}`, {
|
|
76
|
+
body: updateData
|
|
47
77
|
});
|
|
48
78
|
}
|
|
49
79
|
|
|
50
80
|
/**
|
|
51
|
-
*
|
|
81
|
+
* Delete wizard session
|
|
82
|
+
* DELETE /api/v1/wizard/sessions/{sessionId}
|
|
83
|
+
* @async
|
|
84
|
+
* @function deleteWizardSession
|
|
85
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
86
|
+
* @param {string} sessionId - Session ID
|
|
87
|
+
* @param {Object} authConfig - Authentication configuration
|
|
88
|
+
* @returns {Promise<Object>} Delete response
|
|
89
|
+
* @throws {Error} If request fails
|
|
90
|
+
*/
|
|
91
|
+
async function deleteWizardSession(dataplaneUrl, sessionId, authConfig) {
|
|
92
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
93
|
+
return await client.delete(`/api/v1/wizard/sessions/${sessionId}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get wizard session progress
|
|
98
|
+
* GET /api/v1/wizard/sessions/{sessionId}/progress
|
|
99
|
+
* @async
|
|
100
|
+
* @function getWizardProgress
|
|
101
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
102
|
+
* @param {string} sessionId - Session ID
|
|
103
|
+
* @param {Object} authConfig - Authentication configuration
|
|
104
|
+
* @returns {Promise<Object>} Session progress response
|
|
105
|
+
* @throws {Error} If request fails
|
|
106
|
+
*/
|
|
107
|
+
async function getWizardProgress(dataplaneUrl, sessionId, authConfig) {
|
|
108
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
109
|
+
return await client.get(`/api/v1/wizard/sessions/${sessionId}/progress`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parse OpenAPI file or URL
|
|
52
114
|
* POST /api/v1/wizard/parse-openapi
|
|
53
115
|
* @async
|
|
54
116
|
* @function parseOpenApi
|
|
55
117
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
56
118
|
* @param {Object} authConfig - Authentication configuration
|
|
57
|
-
* @param {string}
|
|
119
|
+
* @param {string} openApiFilePathOrUrl - Path to OpenAPI file or URL
|
|
120
|
+
* @param {boolean} [isUrl=false] - Whether the input is a URL
|
|
58
121
|
* @returns {Promise<Object>} Parsed OpenAPI response
|
|
59
122
|
* @throws {Error} If request fails
|
|
60
123
|
*/
|
|
61
|
-
async function parseOpenApi(dataplaneUrl, authConfig,
|
|
124
|
+
async function parseOpenApi(dataplaneUrl, authConfig, openApiFilePathOrUrl, isUrl = false) {
|
|
125
|
+
if (isUrl) {
|
|
126
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
127
|
+
return await client.post(`/api/v1/wizard/parse-openapi?url=${encodeURIComponent(openApiFilePathOrUrl)}`);
|
|
128
|
+
}
|
|
62
129
|
const url = `${dataplaneUrl.replace(/\/$/, '')}/api/v1/wizard/parse-openapi`;
|
|
63
|
-
return await uploadFile(url,
|
|
130
|
+
return await uploadFile(url, openApiFilePathOrUrl, 'file', authConfig);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Credential selection for wizard
|
|
135
|
+
* POST /api/v1/wizard/credential-selection
|
|
136
|
+
* @async
|
|
137
|
+
* @function credentialSelection
|
|
138
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
139
|
+
* @param {Object} authConfig - Authentication configuration
|
|
140
|
+
* @param {Object} selectionData - Credential selection data
|
|
141
|
+
* @param {string} selectionData.action - Action type ('create' | 'select' | 'skip')
|
|
142
|
+
* @param {Object} [selectionData.credentialConfig] - Credential config (required when action='create')
|
|
143
|
+
* @param {string} [selectionData.credentialIdOrKey] - Credential ID or key (required when action='select')
|
|
144
|
+
* @returns {Promise<Object>} Credential selection response
|
|
145
|
+
* @throws {Error} If request fails
|
|
146
|
+
*/
|
|
147
|
+
async function credentialSelection(dataplaneUrl, authConfig, selectionData) {
|
|
148
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
149
|
+
return await client.post('/api/v1/wizard/credential-selection', {
|
|
150
|
+
body: selectionData
|
|
151
|
+
});
|
|
64
152
|
}
|
|
65
153
|
|
|
66
154
|
/**
|
|
@@ -70,14 +158,14 @@ async function parseOpenApi(dataplaneUrl, authConfig, openApiFilePath) {
|
|
|
70
158
|
* @function detectType
|
|
71
159
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
72
160
|
* @param {Object} authConfig - Authentication configuration
|
|
73
|
-
* @param {Object}
|
|
161
|
+
* @param {Object} openapiSpec - OpenAPI specification object
|
|
74
162
|
* @returns {Promise<Object>} Type detection response
|
|
75
163
|
* @throws {Error} If request fails
|
|
76
164
|
*/
|
|
77
|
-
async function detectType(dataplaneUrl, authConfig,
|
|
165
|
+
async function detectType(dataplaneUrl, authConfig, openapiSpec) {
|
|
78
166
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
79
167
|
return await client.post('/api/v1/wizard/detect-type', {
|
|
80
|
-
body: {
|
|
168
|
+
body: { openapiSpec }
|
|
81
169
|
});
|
|
82
170
|
}
|
|
83
171
|
|
|
@@ -89,11 +177,18 @@ async function detectType(dataplaneUrl, authConfig, openApiSpec) {
|
|
|
89
177
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
90
178
|
* @param {Object} authConfig - Authentication configuration
|
|
91
179
|
* @param {Object} config - Generation configuration
|
|
92
|
-
* @param {
|
|
93
|
-
* @param {string} config.
|
|
94
|
-
* @param {
|
|
95
|
-
* @param {string}
|
|
96
|
-
* @param {
|
|
180
|
+
* @param {Object} config.openapiSpec - OpenAPI specification object (required)
|
|
181
|
+
* @param {string} config.detectedType - Detected API type (required, e.g., 'record-based')
|
|
182
|
+
* @param {string} config.intent - User intent (required, any descriptive text)
|
|
183
|
+
* @param {string} config.mode - Wizard mode (required, 'create-system' | 'add-datasource')
|
|
184
|
+
* @param {string} [config.systemIdOrKey] - Existing system ID/key (required for add-datasource)
|
|
185
|
+
* @param {string} [config.credentialIdOrKey] - Credential ID or key
|
|
186
|
+
* @param {string} [config.fieldOnboardingLevel] - Field onboarding level ('full' | 'standard' | 'minimal')
|
|
187
|
+
* @param {boolean} [config.enableOpenAPIGeneration] - Enable OpenAPI operation generation
|
|
188
|
+
* @param {Object} [config.userPreferences] - User preferences
|
|
189
|
+
* @param {boolean} [config.userPreferences.enableMCP] - Enable MCP
|
|
190
|
+
* @param {boolean} [config.userPreferences.enableABAC] - Enable ABAC
|
|
191
|
+
* @param {boolean} [config.userPreferences.enableRBAC] - Enable RBAC
|
|
97
192
|
* @returns {Promise<Object>} Generated configuration response
|
|
98
193
|
* @throws {Error} If request fails
|
|
99
194
|
*/
|
|
@@ -104,6 +199,24 @@ async function generateConfig(dataplaneUrl, authConfig, config) {
|
|
|
104
199
|
});
|
|
105
200
|
}
|
|
106
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Generate configuration via AI (streaming)
|
|
204
|
+
* POST /api/v1/wizard/generate-config-stream
|
|
205
|
+
* @async
|
|
206
|
+
* @function generateConfigStream
|
|
207
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
208
|
+
* @param {Object} authConfig - Authentication configuration
|
|
209
|
+
* @param {Object} config - Generation configuration payload
|
|
210
|
+
* @returns {Promise<Object>} Streaming generation response
|
|
211
|
+
* @throws {Error} If request fails
|
|
212
|
+
*/
|
|
213
|
+
async function generateConfigStream(dataplaneUrl, authConfig, config) {
|
|
214
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
215
|
+
return await client.post('/api/v1/wizard/generate-config-stream', {
|
|
216
|
+
body: config
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
107
220
|
/**
|
|
108
221
|
* Validate wizard configuration
|
|
109
222
|
* POST /api/v1/wizard/validate
|
|
@@ -112,20 +225,69 @@ async function generateConfig(dataplaneUrl, authConfig, config) {
|
|
|
112
225
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
113
226
|
* @param {Object} authConfig - Authentication configuration
|
|
114
227
|
* @param {Object} systemConfig - System configuration to validate
|
|
115
|
-
* @param {Object[]}
|
|
228
|
+
* @param {Object|Object[]} datasourceConfig - Datasource configuration(s) to validate
|
|
116
229
|
* @returns {Promise<Object>} Validation response
|
|
117
230
|
* @throws {Error} If request fails
|
|
118
231
|
*/
|
|
119
|
-
async function validateWizardConfig(dataplaneUrl, authConfig, systemConfig,
|
|
232
|
+
async function validateWizardConfig(dataplaneUrl, authConfig, systemConfig, datasourceConfig) {
|
|
120
233
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
121
234
|
return await client.post('/api/v1/wizard/validate', {
|
|
122
235
|
body: {
|
|
123
236
|
systemConfig,
|
|
124
|
-
|
|
237
|
+
datasourceConfig
|
|
125
238
|
}
|
|
126
239
|
});
|
|
127
240
|
}
|
|
128
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Validate all completed wizard steps
|
|
244
|
+
* GET /api/v1/wizard/sessions/{sessionId}/validate
|
|
245
|
+
* @async
|
|
246
|
+
* @function validateAllSteps
|
|
247
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
248
|
+
* @param {string} sessionId - Session ID
|
|
249
|
+
* @param {Object} authConfig - Authentication configuration
|
|
250
|
+
* @returns {Promise<Object>} Validation response for all steps
|
|
251
|
+
* @throws {Error} If request fails
|
|
252
|
+
*/
|
|
253
|
+
async function validateAllSteps(dataplaneUrl, sessionId, authConfig) {
|
|
254
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
255
|
+
return await client.get(`/api/v1/wizard/sessions/${sessionId}/validate`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Validate specific wizard step
|
|
260
|
+
* POST /api/v1/wizard/sessions/{sessionId}/validate-step
|
|
261
|
+
* @async
|
|
262
|
+
* @function validateStep
|
|
263
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
264
|
+
* @param {string} sessionId - Session ID
|
|
265
|
+
* @param {Object} authConfig - Authentication configuration
|
|
266
|
+
* @param {number} stepNumber - Step number to validate (1-7)
|
|
267
|
+
* @returns {Promise<Object>} Validation response for the step
|
|
268
|
+
* @throws {Error} If request fails
|
|
269
|
+
*/
|
|
270
|
+
async function validateStep(dataplaneUrl, sessionId, authConfig, stepNumber) {
|
|
271
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
272
|
+
return await client.post(`/api/v1/wizard/sessions/${sessionId}/validate-step?step=${stepNumber}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get configuration preview with summaries
|
|
277
|
+
* GET /api/v1/wizard/preview/{sessionId}
|
|
278
|
+
* @async
|
|
279
|
+
* @function getPreview
|
|
280
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
281
|
+
* @param {string} sessionId - Session ID
|
|
282
|
+
* @param {Object} authConfig - Authentication configuration
|
|
283
|
+
* @returns {Promise<Object>} Preview response with system and datasource summaries
|
|
284
|
+
* @throws {Error} If request fails
|
|
285
|
+
*/
|
|
286
|
+
async function getPreview(dataplaneUrl, sessionId, authConfig) {
|
|
287
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
288
|
+
return await client.get(`/api/v1/wizard/preview/${sessionId}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
129
291
|
/**
|
|
130
292
|
* Test MCP server connection
|
|
131
293
|
* POST /api/v1/wizard/test-mcp-connection
|
|
@@ -165,13 +327,20 @@ async function getDeploymentDocs(dataplaneUrl, authConfig, systemKey) {
|
|
|
165
327
|
}
|
|
166
328
|
|
|
167
329
|
module.exports = {
|
|
168
|
-
|
|
169
|
-
|
|
330
|
+
createWizardSession,
|
|
331
|
+
getWizardSession,
|
|
332
|
+
updateWizardSession,
|
|
333
|
+
deleteWizardSession,
|
|
334
|
+
getWizardProgress,
|
|
170
335
|
parseOpenApi,
|
|
336
|
+
credentialSelection,
|
|
171
337
|
detectType,
|
|
172
338
|
generateConfig,
|
|
339
|
+
generateConfigStream,
|
|
173
340
|
validateWizardConfig,
|
|
341
|
+
validateAllSteps,
|
|
342
|
+
validateStep,
|
|
343
|
+
getPreview,
|
|
174
344
|
testMcpConnection,
|
|
175
345
|
getDeploymentDocs
|
|
176
346
|
};
|
|
177
|
-
|
package/lib/app/deploy.js
CHANGED
|
@@ -18,6 +18,8 @@ const logger = require('../utils/logger');
|
|
|
18
18
|
const config = require('../core/config');
|
|
19
19
|
const { getDeploymentAuth } = require('../utils/token-manager');
|
|
20
20
|
const { detectAppType } = require('../utils/paths');
|
|
21
|
+
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
22
|
+
const { checkApplicationExists } = require('../utils/app-existence');
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Validate application name format
|
|
@@ -178,15 +180,26 @@ async function loadVariablesFile(variablesPath) {
|
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
/**
|
|
181
|
-
* Extracts deployment configuration from
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* @
|
|
183
|
+
* Extracts deployment configuration from config.yaml
|
|
184
|
+
* Resolves controller URL using fallback chain: config.controller → logged-in user → developer ID default
|
|
185
|
+
* Resolves environment using fallback chain: config.environment → default 'dev'
|
|
186
|
+
* @async
|
|
187
|
+
* @param {Object} options - CLI options (for poll settings only)
|
|
188
|
+
* @param {Object} _variables - Variables from variables.yaml (unused, kept for compatibility)
|
|
189
|
+
* @returns {Promise<Object>} Extracted configuration with resolved controller URL
|
|
185
190
|
*/
|
|
186
|
-
function extractDeploymentConfig(options,
|
|
191
|
+
async function extractDeploymentConfig(options, _variables) {
|
|
192
|
+
const { resolveEnvironment } = require('../core/config');
|
|
193
|
+
|
|
194
|
+
// Resolve controller URL from config.yaml (no flags, no options)
|
|
195
|
+
const controllerUrl = await resolveControllerUrl();
|
|
196
|
+
|
|
197
|
+
// Resolve environment from config.yaml (no flags, no options)
|
|
198
|
+
const envKey = await resolveEnvironment();
|
|
199
|
+
|
|
187
200
|
return {
|
|
188
|
-
controllerUrl
|
|
189
|
-
envKey
|
|
201
|
+
controllerUrl,
|
|
202
|
+
envKey,
|
|
190
203
|
poll: options.poll !== false,
|
|
191
204
|
pollInterval: options.pollInterval || 5000,
|
|
192
205
|
pollMaxAttempts: options.pollMaxAttempts || 60
|
|
@@ -200,7 +213,7 @@ function extractDeploymentConfig(options, variables) {
|
|
|
200
213
|
*/
|
|
201
214
|
function validateDeploymentConfig(deploymentConfig) {
|
|
202
215
|
if (!deploymentConfig.controllerUrl) {
|
|
203
|
-
throw new Error('Controller URL is required.
|
|
216
|
+
throw new Error('Controller URL is required. Run "aifabrix login" to set the controller URL in config.yaml');
|
|
204
217
|
}
|
|
205
218
|
if (!deploymentConfig.auth) {
|
|
206
219
|
throw new Error('Authentication is required. Run "aifabrix login" first or ensure credentials are in secrets.local.yaml');
|
|
@@ -208,19 +221,15 @@ function validateDeploymentConfig(deploymentConfig) {
|
|
|
208
221
|
}
|
|
209
222
|
|
|
210
223
|
/**
|
|
211
|
-
* Configure deployment environment settings
|
|
224
|
+
* Configure deployment environment settings from config.yaml
|
|
212
225
|
* @async
|
|
213
|
-
* @param {Object}
|
|
226
|
+
* @param {Object} _options - CLI options (unused, kept for compatibility)
|
|
214
227
|
* @param {Object} deploymentConfig - Deployment configuration to update
|
|
215
228
|
* @returns {Promise<void>}
|
|
216
229
|
*/
|
|
217
|
-
async function configureDeploymentEnvironment(
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
await config.setCurrentEnvironment(options.environment);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Get current environment from root-level config
|
|
230
|
+
async function configureDeploymentEnvironment(_options, deploymentConfig) {
|
|
231
|
+
// Get current environment from root-level config (already resolved in extractDeploymentConfig)
|
|
232
|
+
// This function is kept for compatibility but no longer updates environment from options
|
|
224
233
|
const currentEnvironment = await config.getCurrentEnvironment();
|
|
225
234
|
deploymentConfig.envKey = deploymentConfig.envKey || currentEnvironment;
|
|
226
235
|
}
|
|
@@ -234,9 +243,9 @@ async function configureDeploymentEnvironment(options, deploymentConfig) {
|
|
|
234
243
|
* @throws {Error} If authentication fails
|
|
235
244
|
*/
|
|
236
245
|
async function refreshDeploymentToken(appName, deploymentConfig) {
|
|
237
|
-
// Get controller URL
|
|
246
|
+
// Get controller URL (should already be resolved by extractDeploymentConfig)
|
|
238
247
|
if (!deploymentConfig.controllerUrl) {
|
|
239
|
-
throw new Error('Controller URL is required.
|
|
248
|
+
throw new Error('Controller URL is required. Run "aifabrix login" to set the controller URL in config.yaml');
|
|
240
249
|
}
|
|
241
250
|
|
|
242
251
|
// Get deployment authentication (device token → client token → credentials)
|
|
@@ -275,7 +284,7 @@ async function loadDeploymentConfig(appName, options) {
|
|
|
275
284
|
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
276
285
|
const variables = await loadVariablesFile(variablesPath);
|
|
277
286
|
|
|
278
|
-
const deploymentConfig = extractDeploymentConfig(options, variables);
|
|
287
|
+
const deploymentConfig = await extractDeploymentConfig(options, variables);
|
|
279
288
|
|
|
280
289
|
await configureDeploymentEnvironment(options, deploymentConfig);
|
|
281
290
|
await refreshDeploymentToken(appName, deploymentConfig);
|
|
@@ -360,6 +369,80 @@ function displayDeploymentResults(result) {
|
|
|
360
369
|
}
|
|
361
370
|
}
|
|
362
371
|
|
|
372
|
+
/**
|
|
373
|
+
* Check if app is external and handle external deployment
|
|
374
|
+
* @async
|
|
375
|
+
* @function handleExternalDeployment
|
|
376
|
+
* @param {string} appName - Application name
|
|
377
|
+
* @param {Object} options - Deployment options
|
|
378
|
+
* @returns {Promise<Object|null>} Deployment result if external, null otherwise
|
|
379
|
+
*/
|
|
380
|
+
async function handleExternalDeployment(appName, options) {
|
|
381
|
+
const { isExternal } = await detectAppType(appName);
|
|
382
|
+
if (isExternal) {
|
|
383
|
+
const externalDeploy = require('../external-system/deploy');
|
|
384
|
+
await externalDeploy.deployExternalSystem(appName, options);
|
|
385
|
+
return { success: true, type: 'external' };
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Handle deployment errors
|
|
392
|
+
* @async
|
|
393
|
+
* @function handleDeploymentError
|
|
394
|
+
* @param {Error} error - Error that occurred
|
|
395
|
+
* @param {string} appName - Application name
|
|
396
|
+
* @param {string} controllerUrl - Controller URL (from config, or null if not yet resolved)
|
|
397
|
+
* @param {boolean} usedExternalDeploy - Whether external deployment was used
|
|
398
|
+
*/
|
|
399
|
+
async function handleDeploymentError(error, appName, controllerUrl, usedExternalDeploy) {
|
|
400
|
+
if (usedExternalDeploy) {
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
const alreadyLogged = error._logged === true;
|
|
404
|
+
const url = controllerUrl || 'unknown';
|
|
405
|
+
const deployer = require('../deployment/deployer');
|
|
406
|
+
await deployer.handleDeploymentErrors(error, appName, url, alreadyLogged);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Execute standard application deployment flow
|
|
411
|
+
* @async
|
|
412
|
+
* @function executeStandardDeployment
|
|
413
|
+
* @param {string} appName - Application name
|
|
414
|
+
* @param {Object} options - Deployment options
|
|
415
|
+
* @returns {Promise<Object>} Deployment result
|
|
416
|
+
*/
|
|
417
|
+
async function executeStandardDeployment(appName, options) {
|
|
418
|
+
const config = await loadDeploymentConfig(appName, options);
|
|
419
|
+
const controllerUrl = config.controllerUrl || 'unknown';
|
|
420
|
+
|
|
421
|
+
// Check if application exists before deployment
|
|
422
|
+
const appExists = await checkApplicationExists(appName, controllerUrl, config.envKey, config.auth);
|
|
423
|
+
|
|
424
|
+
const { manifest, manifestPath } = await generateAndValidateManifest(appName);
|
|
425
|
+
displayDeploymentInfo(manifest, manifestPath);
|
|
426
|
+
|
|
427
|
+
try {
|
|
428
|
+
const result = await executeDeployment(manifest, config);
|
|
429
|
+
displayDeploymentResults(result);
|
|
430
|
+
return { result, controllerUrl, appExists };
|
|
431
|
+
} catch (error) {
|
|
432
|
+
// Enhance error if app exists and credentials are invalid
|
|
433
|
+
if (appExists && error.status === 401 && !error.message.includes('rotate-secret')) {
|
|
434
|
+
const enhancedError = new Error(
|
|
435
|
+
`${error.message}\n\n💡 The application '${appName}' exists in environment '${config.envKey}'. ` +
|
|
436
|
+
`To fix invalid credentials, rotate the application secret:\n aifabrix app rotate-secret ${appName}`
|
|
437
|
+
);
|
|
438
|
+
enhancedError.status = 401;
|
|
439
|
+
enhancedError.formatted = error.formatted || enhancedError.message;
|
|
440
|
+
throw enhancedError;
|
|
441
|
+
}
|
|
442
|
+
throw error;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
363
446
|
/**
|
|
364
447
|
* Deploys application to Miso Controller
|
|
365
448
|
* Orchestrates manifest generation, key creation, and deployment
|
|
@@ -368,59 +451,38 @@ function displayDeploymentResults(result) {
|
|
|
368
451
|
* @function deployApp
|
|
369
452
|
* @param {string} appName - Name of the application to deploy
|
|
370
453
|
* @param {Object} options - Deployment options
|
|
371
|
-
* @param {string} options.controller - Controller URL (required)
|
|
372
|
-
* @param {string} [options.environment] - Target environment (miso/dev/tst/pro)
|
|
373
454
|
* @param {boolean} [options.poll] - Poll for deployment status
|
|
374
455
|
* @param {number} [options.pollInterval] - Polling interval in milliseconds
|
|
456
|
+
* @param {number} [options.pollMaxAttempts] - Max polling attempts
|
|
375
457
|
* @returns {Promise<Object>} Deployment result
|
|
376
458
|
* @throws {Error} If deployment fails
|
|
377
459
|
*
|
|
460
|
+
* Controller and environment come from config.yaml (set via aifabrix login or aifabrix auth config).
|
|
461
|
+
*
|
|
378
462
|
* @example
|
|
379
|
-
* await deployApp('myapp', {
|
|
463
|
+
* await deployApp('myapp', { poll: true });
|
|
380
464
|
*/
|
|
381
465
|
async function deployApp(appName, options = {}) {
|
|
382
466
|
let controllerUrl = null;
|
|
383
|
-
let
|
|
467
|
+
let usedExternalDeploy = false;
|
|
384
468
|
|
|
385
469
|
try {
|
|
386
|
-
// 1. Input validation
|
|
387
470
|
if (!appName || typeof appName !== 'string' || appName.trim().length === 0) {
|
|
388
471
|
throw new Error('App name is required');
|
|
389
472
|
}
|
|
390
|
-
|
|
391
473
|
validateAppName(appName);
|
|
392
474
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
config = await loadDeploymentConfig(appName, options);
|
|
399
|
-
controllerUrl = config.controllerUrl || options.controller || 'unknown';
|
|
400
|
-
|
|
401
|
-
// 3. Generate and validate manifest
|
|
402
|
-
const { manifest, manifestPath } = await generateAndValidateManifest(appName);
|
|
403
|
-
|
|
404
|
-
// 4. Display deployment info
|
|
405
|
-
displayDeploymentInfo(manifest, manifestPath);
|
|
406
|
-
|
|
407
|
-
// 5. Execute deployment
|
|
408
|
-
const result = await executeDeployment(manifest, config);
|
|
409
|
-
|
|
410
|
-
// 6. Display results
|
|
411
|
-
displayDeploymentResults(result);
|
|
475
|
+
const externalResult = await handleExternalDeployment(appName, options);
|
|
476
|
+
if (externalResult) {
|
|
477
|
+
return externalResult;
|
|
478
|
+
}
|
|
479
|
+
usedExternalDeploy = false;
|
|
412
480
|
|
|
481
|
+
const { result, controllerUrl: url } = await executeStandardDeployment(appName, options);
|
|
482
|
+
controllerUrl = url;
|
|
413
483
|
return result;
|
|
414
|
-
|
|
415
484
|
} catch (error) {
|
|
416
|
-
|
|
417
|
-
// Check if error was already logged (from deployer.js)
|
|
418
|
-
const alreadyLogged = error._logged === true;
|
|
419
|
-
const url = controllerUrl || options.controller || 'unknown';
|
|
420
|
-
|
|
421
|
-
const deployer = require('../deployment/deployer');
|
|
422
|
-
// handleDeploymentErrors will log, format, and throw the error
|
|
423
|
-
await deployer.handleDeploymentErrors(error, appName, url, alreadyLogged);
|
|
485
|
+
await handleDeploymentError(error, appName, controllerUrl, usedExternalDeploy);
|
|
424
486
|
}
|
|
425
487
|
}
|
|
426
488
|
|
package/lib/app/display.js
CHANGED
|
@@ -25,9 +25,9 @@ function displayExternalSystemSuccess(appName, config, location) {
|
|
|
25
25
|
logger.log(chalk.blue(`System Key: ${config.systemKey || appName}`));
|
|
26
26
|
logger.log(chalk.green('\nNext steps:'));
|
|
27
27
|
logger.log(chalk.white('1. Edit external system JSON files in ' + location));
|
|
28
|
-
logger.log(chalk.white('2. Run: aifabrix
|
|
29
|
-
logger.log(chalk.white('3. Run: aifabrix
|
|
30
|
-
logger.log(chalk.white('4. Run: aifabrix deploy ' + appName
|
|
28
|
+
logger.log(chalk.white('2. Run: aifabrix validate ' + appName + ' --type external'));
|
|
29
|
+
logger.log(chalk.white('3. Run: aifabrix login'));
|
|
30
|
+
logger.log(chalk.white('4. Run: aifabrix deploy ' + appName));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -50,8 +50,9 @@ function displayWebappSuccess(appName, config, envConversionMessage) {
|
|
|
50
50
|
|
|
51
51
|
logger.log(chalk.green('\nNext steps:'));
|
|
52
52
|
logger.log(chalk.white('1. Copy env.template to .env and fill in your values'));
|
|
53
|
-
logger.log(chalk.white('2. Run: aifabrix
|
|
54
|
-
logger.log(chalk.white('3. Run: aifabrix
|
|
53
|
+
logger.log(chalk.white('2. Run: aifabrix up'));
|
|
54
|
+
logger.log(chalk.white('3. Run: aifabrix build ' + appName));
|
|
55
|
+
logger.log(chalk.white('4. Run: aifabrix run ' + appName));
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
/**
|
package/lib/app/dockerfile.js
CHANGED
|
@@ -15,6 +15,7 @@ const yaml = require('js-yaml');
|
|
|
15
15
|
const build = require('../build');
|
|
16
16
|
const { validateAppName } = require('./push');
|
|
17
17
|
const logger = require('../utils/logger');
|
|
18
|
+
const { getContainerPort } = require('../utils/port-resolver');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Checks if Dockerfile exists and validates overwrite permission
|
|
@@ -52,7 +53,7 @@ async function loadAppConfig(configPath, options) {
|
|
|
52
53
|
const variables = yaml.load(yamlContent);
|
|
53
54
|
return {
|
|
54
55
|
language: options.language || variables.build?.language || 'typescript',
|
|
55
|
-
port: variables
|
|
56
|
+
port: getContainerPort(variables, 3000),
|
|
56
57
|
...variables
|
|
57
58
|
};
|
|
58
59
|
} catch {
|