@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 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
@@ -0,0 +1,8 @@
1
+ appName: test-hubspot
2
+ mode: create-system
3
+ source:
4
+ type: openapi-file
5
+ filePath: /workspace/aifabrix-builder/integration/hubspot/companies.json
6
+ credential:
7
+ action: skip
8
+ preferences: {}
@@ -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
  };
@@ -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 };