@aifabrix/builder 2.33.5 → 2.36.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/README.md +5 -0
- package/integration/test-hubspot/wizard.yaml +8 -0
- package/lib/api/wizard.api.js +24 -1
- package/lib/app/run-helpers.js +7 -2
- package/lib/app/run.js +2 -2
- package/lib/app/show-display.js +184 -0
- package/lib/app/show.js +642 -0
- package/lib/cli.js +38 -9
- package/lib/commands/auth-status.js +58 -2
- package/lib/commands/up-miso.js +25 -16
- package/lib/commands/wizard-core-helpers.js +278 -0
- package/lib/commands/wizard-core.js +74 -161
- package/lib/commands/wizard-headless.js +2 -2
- package/lib/commands/wizard-helpers.js +143 -0
- package/lib/commands/wizard.js +282 -69
- package/lib/datasource/list.js +6 -3
- package/lib/generator/index.js +32 -0
- package/lib/generator/wizard-prompts.js +111 -44
- package/lib/infrastructure/services.js +6 -3
- package/lib/utils/app-register-auth.js +9 -2
- package/lib/utils/cli-utils.js +40 -1
- package/lib/utils/error-formatters/http-status-errors.js +8 -0
- package/lib/utils/error-formatters/permission-errors.js +44 -1
- package/lib/utils/infra-containers.js +19 -16
- package/lib/utils/infra-status.js +12 -3
- package/lib/validation/wizard-config-validator.js +35 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -121,6 +121,11 @@ Build --> Run[Run Locally]:::base
|
|
|
121
121
|
Run --> Deploy[Deploy to Azure]:::primary
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
- **Tests**: `npm test` (runs via wrapper; handles known Jest/Node exit issues).
|
|
127
|
+
- **Coverage**: `npm run test:coverage` — runs tests with coverage through the same wrapper. May take 3–5 minutes for the full suite. If the process exits with a signal after "Ran all test suites", the wrapper treats it as success and coverage is written to `coverage/`. Use `test:coverage:nyc` only if you need nyc-specific reporters.
|
|
128
|
+
|
|
124
129
|
## Requirements
|
|
125
130
|
|
|
126
131
|
- **Docker Desktop** - For running containers
|
package/lib/api/wizard.api.js
CHANGED
|
@@ -326,6 +326,28 @@ async function getDeploymentDocs(dataplaneUrl, authConfig, systemKey) {
|
|
|
326
326
|
return await client.get(`/api/v1/wizard/deployment-docs/${systemKey}`);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Get known wizard platforms from dataplane.
|
|
331
|
+
* GET /api/v1/wizard/platforms
|
|
332
|
+
* Expected response: { platforms: [ { key: string, displayName?: string } ] } or equivalent.
|
|
333
|
+
* On 404 or error, returns empty array (caller should hide "Known platform" choice).
|
|
334
|
+
* @async
|
|
335
|
+
* @function getWizardPlatforms
|
|
336
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
337
|
+
* @param {Object} authConfig - Authentication configuration
|
|
338
|
+
* @returns {Promise<Array<{key: string, displayName?: string}>>} List of platforms (empty on 404/error)
|
|
339
|
+
*/
|
|
340
|
+
async function getWizardPlatforms(dataplaneUrl, authConfig) {
|
|
341
|
+
try {
|
|
342
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
343
|
+
const response = await client.get('/api/v1/wizard/platforms');
|
|
344
|
+
const platforms = response?.data?.platforms ?? response?.platforms ?? [];
|
|
345
|
+
return Array.isArray(platforms) ? platforms : [];
|
|
346
|
+
} catch (error) {
|
|
347
|
+
return [];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
329
351
|
module.exports = {
|
|
330
352
|
createWizardSession,
|
|
331
353
|
getWizardSession,
|
|
@@ -342,5 +364,6 @@ module.exports = {
|
|
|
342
364
|
validateStep,
|
|
343
365
|
getPreview,
|
|
344
366
|
testMcpConnection,
|
|
345
|
-
getDeploymentDocs
|
|
367
|
+
getDeploymentDocs,
|
|
368
|
+
getWizardPlatforms
|
|
346
369
|
};
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -139,14 +139,15 @@ async function validateAppConfiguration(appName) {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
* Checks prerequisites: Docker image and infrastructure
|
|
142
|
+
* Checks prerequisites: Docker image and (optionally) infrastructure
|
|
143
143
|
* @async
|
|
144
144
|
* @param {string} appName - Application name
|
|
145
145
|
* @param {Object} appConfig - Application configuration
|
|
146
146
|
* @param {boolean} [debug=false] - Enable debug logging
|
|
147
|
+
* @param {boolean} [skipInfraCheck=false] - When true, skip infra health check (e.g. when caller already verified, e.g. up-miso)
|
|
147
148
|
* @throws {Error} If prerequisites are not met
|
|
148
149
|
*/
|
|
149
|
-
async function checkPrerequisites(appName, appConfig, debug = false) {
|
|
150
|
+
async function checkPrerequisites(appName, appConfig, debug = false, skipInfraCheck = false) {
|
|
150
151
|
const imageName = composeGenerator.getImageName(appConfig, appName);
|
|
151
152
|
const imageTag = appConfig.image?.tag || 'latest';
|
|
152
153
|
const fullImageName = `${imageName}:${imageTag}`;
|
|
@@ -162,6 +163,10 @@ async function checkPrerequisites(appName, appConfig, debug = false) {
|
|
|
162
163
|
}
|
|
163
164
|
logger.log(chalk.green(`✓ Image ${fullImageName} found`));
|
|
164
165
|
|
|
166
|
+
if (skipInfraCheck) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
165
170
|
logger.log(chalk.blue('Checking infrastructure health...'));
|
|
166
171
|
const infraHealth = await infra.checkInfraHealth();
|
|
167
172
|
if (debug) {
|
package/lib/app/run.js
CHANGED
|
@@ -177,8 +177,8 @@ async function runApp(appName, options = {}) {
|
|
|
177
177
|
// Load and configure application
|
|
178
178
|
const appConfig = await loadAndConfigureApp(appName, debug);
|
|
179
179
|
|
|
180
|
-
// Check prerequisites: image and infrastructure
|
|
181
|
-
await helpers.checkPrerequisites(appName, appConfig, debug);
|
|
180
|
+
// Check prerequisites: image and (unless skipped) infrastructure
|
|
181
|
+
await helpers.checkPrerequisites(appName, appConfig, debug, options.skipInfraCheck === true);
|
|
182
182
|
|
|
183
183
|
// Check if container is already running and stop it if needed
|
|
184
184
|
await checkAndStopContainer(appName, appConfig.developerId, debug);
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-readable display for aifabrix show command.
|
|
3
|
+
* Single display function for offline and online (unified summary shape).
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Display formatting for show command
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 2.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const chalk = require('chalk');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
|
|
15
|
+
function logSourceAndHeader(summary) {
|
|
16
|
+
const isOffline = summary.source === 'offline';
|
|
17
|
+
const sourceLabel = isOffline
|
|
18
|
+
? `Source: offline (${summary.path || '—'})`
|
|
19
|
+
: `Source: online (${summary.controllerUrl || '—'})`;
|
|
20
|
+
logger.log(sourceLabel);
|
|
21
|
+
logger.log('');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function logApplicationRequired(a) {
|
|
25
|
+
logger.log('📱 Application');
|
|
26
|
+
logger.log(` Key: ${a.key ?? '—'}`);
|
|
27
|
+
logger.log(` Display name: ${a.displayName ?? '—'}`);
|
|
28
|
+
logger.log(` Description: ${a.description ?? '—'}`);
|
|
29
|
+
logger.log(` Type: ${a.type ?? '—'}`);
|
|
30
|
+
const port = (a.port !== undefined && a.port !== null) ? a.port : '—';
|
|
31
|
+
logger.log(` Port: ${port}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function logApplicationOptional(a) {
|
|
35
|
+
if (a.deploymentKey !== undefined) logger.log(` Deployment: ${a.deploymentKey ?? '—'}`);
|
|
36
|
+
if (a.image !== undefined) logger.log(` Image: ${a.image ?? '—'}`);
|
|
37
|
+
if (a.registryMode !== undefined) logger.log(` Registry: ${a.registryMode ?? '—'}`);
|
|
38
|
+
if (a.healthCheck !== undefined) logger.log(` Health: ${a.healthCheck ?? '—'}`);
|
|
39
|
+
if (a.build !== undefined) logger.log(` Build: ${a.build ?? '—'}`);
|
|
40
|
+
if (a.status !== undefined) logger.log(` Status: ${a.status ?? '—'}`);
|
|
41
|
+
if (a.url !== undefined) logger.log(` URL: ${a.url ?? '—'}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function logApplicationFields(a) {
|
|
45
|
+
logApplicationRequired(a);
|
|
46
|
+
logApplicationOptional(a);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function logApplicationExternalIntegration(ei) {
|
|
50
|
+
logger.log(' External integration:');
|
|
51
|
+
logger.log(` schemaBasePath: ${ei.schemaBasePath}`);
|
|
52
|
+
logger.log(` systems: [${(ei.systems || []).join(', ')}]`);
|
|
53
|
+
logger.log(` dataSources: [${(ei.dataSources || []).join(', ')}]`);
|
|
54
|
+
logger.log(chalk.gray('\n For external system data as on dataplane, run with --online.'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function logApplicationSection(a, isExternal) {
|
|
58
|
+
logApplicationFields(a);
|
|
59
|
+
if (isExternal && a.externalIntegration) {
|
|
60
|
+
logApplicationExternalIntegration(a.externalIntegration);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function logRolesSection(roles) {
|
|
65
|
+
if (roles.length === 0) return;
|
|
66
|
+
logger.log('');
|
|
67
|
+
logger.log('👥 Roles');
|
|
68
|
+
roles.forEach((r) => {
|
|
69
|
+
const ro = typeof r === 'object' ? r : { name: r, value: r };
|
|
70
|
+
const name = ro.name ?? ro.value ?? '—';
|
|
71
|
+
const value = ro.value ?? ro.name ?? '—';
|
|
72
|
+
logger.log(` • ${name} (${value})`);
|
|
73
|
+
if (ro.description) logger.log(` \t${ro.description}`);
|
|
74
|
+
if (ro.groups && ro.groups.length > 0) logger.log(` \tgroups: [${ro.groups.join(', ')}]`);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function logPermissionsSection(permissions) {
|
|
79
|
+
if (permissions.length === 0) return;
|
|
80
|
+
logger.log('');
|
|
81
|
+
logger.log('🛡️ Permissions');
|
|
82
|
+
permissions.forEach((p) => {
|
|
83
|
+
const name = p.name ?? '—';
|
|
84
|
+
const roleList = (p.roles || []).join(', ');
|
|
85
|
+
logger.log(` • ${name}`);
|
|
86
|
+
logger.log(` \troles: [${roleList}]`);
|
|
87
|
+
if (p.description) logger.log(` \t${p.description}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function logAuthSection(authentication) {
|
|
92
|
+
if (!authentication) return;
|
|
93
|
+
logger.log('');
|
|
94
|
+
logger.log('🔐 Authentication');
|
|
95
|
+
const sso = authentication.enableSSO ? 'enabled' : 'disabled';
|
|
96
|
+
logger.log(` SSO: ${sso} type: ${authentication.type ?? '—'} requiredRoles: [${(authentication.requiredRoles || []).join(', ')}]`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function logConfigurationsSection(portalInputConfigurations) {
|
|
100
|
+
if (portalInputConfigurations.length === 0) return;
|
|
101
|
+
logger.log('');
|
|
102
|
+
logger.log('📝 Configurations');
|
|
103
|
+
portalInputConfigurations.forEach((c) => {
|
|
104
|
+
logger.log(` ${c.label ?? c.name ?? '—'}: ${c.value ?? '—'}`);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function logDatabasesSection(dbNames) {
|
|
109
|
+
if (dbNames.length === 0) return;
|
|
110
|
+
logger.log('');
|
|
111
|
+
logger.log('🗄️ Databases');
|
|
112
|
+
logger.log(` ${dbNames.join(', ')}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function logExternalSystemMain(ext) {
|
|
116
|
+
logger.log('🔗 External system (dataplane)');
|
|
117
|
+
logger.log(` Dataplane: ${ext.dataplaneUrl}`);
|
|
118
|
+
logger.log(` System key: ${ext.systemKey}`);
|
|
119
|
+
logger.log(` Display name: ${ext.displayName}`);
|
|
120
|
+
logger.log(` Type: ${ext.type}`);
|
|
121
|
+
logger.log(` Status: ${ext.status}`);
|
|
122
|
+
logger.log(` Version: ${ext.version ?? '—'}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function logExternalSystemDataSources(dataSources) {
|
|
126
|
+
if (!dataSources || dataSources.length === 0) return;
|
|
127
|
+
logger.log(' DataSources:');
|
|
128
|
+
dataSources.forEach((ds) => {
|
|
129
|
+
logger.log(` • ${ds.key} ${ds.displayName ?? ''} (systemKey: ${ds.systemKey ?? '—'})`);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function logExternalSystemApplication(ap) {
|
|
134
|
+
if (!ap) return;
|
|
135
|
+
logger.log(' Application (from dataplane):');
|
|
136
|
+
logger.log(` key: ${ap.key} displayName: ${ap.displayName} type: ${ap.type}`);
|
|
137
|
+
if (ap.roles) logger.log(` roles: ${Array.isArray(ap.roles) ? ap.roles.join(', ') : ap.roles}`);
|
|
138
|
+
if (ap.permissions) logger.log(` permissions: ${Array.isArray(ap.permissions) ? ap.permissions.join(', ') : ap.permissions}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function logExternalSystemSection(ext) {
|
|
142
|
+
if (!ext) return;
|
|
143
|
+
logger.log('');
|
|
144
|
+
if (ext.error) {
|
|
145
|
+
logger.log('🔗 External system (dataplane): not available (dataplane unreachable or not found).');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
logExternalSystemMain(ext);
|
|
149
|
+
logExternalSystemDataSources(ext.dataSources);
|
|
150
|
+
logExternalSystemApplication(ext.application);
|
|
151
|
+
if (ext.openapiFiles && ext.openapiFiles.length > 0) {
|
|
152
|
+
logger.log(` OpenAPI files: ${ext.openapiFiles.length}`);
|
|
153
|
+
}
|
|
154
|
+
if (ext.openapiEndpoints && ext.openapiEndpoints.length > 0) {
|
|
155
|
+
const sample = ext.openapiEndpoints.slice(0, 3).map((e) => `${e.method || 'GET'} ${e.path || e.pathPattern || e}`).join(', ');
|
|
156
|
+
logger.log(` OpenAPI endpoints: ${ext.openapiEndpoints.length} (e.g. ${sample}${ext.openapiEndpoints.length > 3 ? ' …' : ''})`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Format and print human-readable show output (offline or online).
|
|
162
|
+
* @param {Object} summary - Unified summary (buildOfflineSummaryFromDeployJson or buildOnlineSummary)
|
|
163
|
+
*/
|
|
164
|
+
function display(summary) {
|
|
165
|
+
const a = summary.application;
|
|
166
|
+
const roles = summary.roles ?? a.roles ?? [];
|
|
167
|
+
const permissions = summary.permissions ?? a.permissions ?? [];
|
|
168
|
+
const authentication = summary.authentication ?? a.authentication;
|
|
169
|
+
const portalInputConfigurations = summary.portalInputConfigurations ?? a.portalInputConfigurations ?? [];
|
|
170
|
+
const databases = summary.databases ?? a.databases ?? [];
|
|
171
|
+
const dbNames = Array.isArray(databases) ? databases.map((d) => (d && d.name) || d).filter(Boolean) : [];
|
|
172
|
+
|
|
173
|
+
logSourceAndHeader(summary);
|
|
174
|
+
logApplicationSection(a, summary.isExternal);
|
|
175
|
+
logRolesSection(roles);
|
|
176
|
+
logPermissionsSection(permissions);
|
|
177
|
+
logAuthSection(authentication);
|
|
178
|
+
logConfigurationsSection(portalInputConfigurations);
|
|
179
|
+
logDatabasesSection(dbNames);
|
|
180
|
+
logExternalSystemSection(summary.externalSystem);
|
|
181
|
+
logger.log('');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = { display };
|