@aifabrix/builder 2.39.3 → 2.40.2
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 +6 -6
- package/README.md +3 -3
- package/babel.config.js +6 -0
- package/integration/hubspot/README.md +53 -141
- package/integration/hubspot/application.yaml +37 -0
- package/integration/hubspot/env.template +2 -11
- package/integration/hubspot/hubspot-deploy.json +1 -0
- package/integration/hubspot/test.js +5 -5
- package/jest.config.manual.js +29 -0
- package/lib/api/credentials.api.js +5 -5
- package/lib/api/deployments.api.js +2 -2
- package/lib/api/pipeline.api.js +17 -17
- package/lib/api/wizard.api.js +2 -2
- package/lib/app/config.js +11 -6
- package/lib/app/deploy-config.js +13 -16
- package/lib/app/deploy.js +29 -22
- package/lib/app/display.js +1 -1
- package/lib/app/dockerfile.js +11 -12
- package/lib/app/helpers.js +51 -13
- package/lib/app/index.js +14 -2
- package/lib/app/prompts.js +37 -45
- package/lib/app/push.js +8 -11
- package/lib/app/readme.js +16 -12
- package/lib/app/register.js +1 -1
- package/lib/app/run-helpers.js +31 -22
- package/lib/app/run.js +44 -5
- package/lib/app/show-display.js +104 -44
- package/lib/app/show.js +123 -43
- package/lib/build/index.js +11 -18
- package/lib/cli/setup-app.js +36 -29
- package/lib/cli/setup-auth.js +19 -15
- package/lib/cli/setup-credential-deployment.js +3 -1
- package/lib/cli/setup-external-system.js +35 -16
- package/lib/cli/setup-infra.js +45 -23
- package/lib/cli/setup-utility.js +85 -31
- package/lib/commands/app-logs.js +28 -20
- package/lib/commands/app.js +30 -26
- package/lib/commands/auth-status.js +36 -3
- package/lib/commands/convert.js +202 -0
- package/lib/commands/credential-list.js +78 -17
- package/lib/commands/datasource.js +24 -24
- package/lib/commands/deployment-list.js +13 -6
- package/lib/commands/up-common.js +80 -42
- package/lib/commands/up-dataplane.js +15 -14
- package/lib/commands/up-miso.js +15 -14
- package/lib/commands/upload.js +163 -0
- package/lib/commands/wizard-core.js +5 -4
- package/lib/core/diff.js +84 -9
- package/lib/core/key-generator.js +9 -12
- package/lib/core/secrets-docker-env.js +2 -2
- package/lib/core/secrets.js +3 -2
- package/lib/core/templates.js +2 -2
- package/lib/datasource/deploy.js +2 -1
- package/lib/deployment/deployer.js +76 -48
- package/lib/external-system/delete.js +0 -1
- package/lib/external-system/deploy-helpers.js +5 -6
- package/lib/external-system/deploy.js +7 -2
- package/lib/external-system/download-helpers.js +4 -4
- package/lib/external-system/download.js +11 -10
- package/lib/external-system/generator.js +19 -17
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +1 -1
- package/lib/generator/external-controller-manifest.js +26 -29
- package/lib/generator/external-schema-utils.js +6 -18
- package/lib/generator/external.js +32 -27
- package/lib/generator/github.js +1 -1
- package/lib/generator/helpers.js +12 -19
- package/lib/generator/index.js +15 -15
- package/lib/generator/parse-image.js +35 -0
- package/lib/generator/split-readme.js +105 -0
- package/lib/generator/split-variables.js +149 -0
- package/lib/generator/split.js +86 -246
- package/lib/generator/wizard.js +51 -70
- package/lib/schema/application-schema.json +4 -4
- package/lib/schema/external-datasource.schema.json +5 -0
- package/lib/schema/external-system.schema.json +10 -0
- package/lib/utils/app-config-resolver.js +52 -0
- package/lib/utils/app-register-api.js +1 -1
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +16 -23
- package/lib/utils/app-register-validator.js +2 -2
- package/lib/utils/cli-utils.js +47 -3
- package/lib/utils/config-format.js +154 -0
- package/lib/utils/config-paths.js +19 -52
- package/lib/utils/config-tokens.js +1 -0
- package/lib/utils/docker-build.js +71 -94
- package/lib/utils/dockerfile-utils.js +1 -1
- package/lib/utils/env-copy.js +4 -4
- package/lib/utils/env-ports.js +2 -2
- package/lib/utils/error-formatter.js +1 -1
- package/lib/utils/error-formatters/validation-errors.js +1 -1
- package/lib/utils/external-readme.js +12 -5
- package/lib/utils/external-system-test-helpers.js +2 -0
- package/lib/utils/health-check.js +55 -66
- package/lib/utils/image-version.js +12 -21
- package/lib/utils/paths.js +45 -66
- package/lib/utils/port-resolver.js +8 -8
- package/lib/utils/schema-loader.js +22 -0
- package/lib/utils/schema-resolver.js +23 -33
- package/lib/utils/secrets-helpers.js +7 -7
- package/lib/utils/secrets-utils.js +10 -12
- package/lib/utils/template-helpers.js +13 -13
- package/lib/utils/token-manager.js +20 -2
- package/lib/utils/variable-transformer.js +2 -2
- package/lib/validation/validate-display.js +3 -4
- package/lib/validation/validate.js +34 -28
- package/lib/validation/validator.js +50 -30
- package/package.json +4 -1
- package/templates/README.md +1 -1
- package/templates/applications/README.md.hbs +3 -3
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/external-system/README.md.hbs +4 -4
- package/templates/external-system/external-system.json.hbs +1 -16
- package/integration/hubspot/variables.yaml +0 -17
- /package/templates/applications/dataplane/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/keycloak/{variables.yaml → application.yaml} +0 -0
- /package/templates/applications/miso-controller/{variables.yaml → application.yaml} +0 -0
package/lib/app/readme.js
CHANGED
|
@@ -12,7 +12,8 @@ const fs = require('fs').promises;
|
|
|
12
12
|
const fsSync = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const handlebars = require('handlebars');
|
|
15
|
-
const
|
|
15
|
+
const { resolveApplicationConfigPath } = require('../utils/paths');
|
|
16
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
16
17
|
const { generateExternalReadmeContent } = require('../utils/external-readme');
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -104,7 +105,7 @@ function buildExternalDatasourcePlaceholders(systemKey, datasourceCount) {
|
|
|
104
105
|
return {
|
|
105
106
|
entityType,
|
|
106
107
|
displayName: `Datasource ${index + 1}`,
|
|
107
|
-
fileName: `${systemKey}-datasource-${entityType}.
|
|
108
|
+
fileName: `${systemKey}-datasource-${entityType}.yaml`
|
|
108
109
|
};
|
|
109
110
|
});
|
|
110
111
|
}
|
|
@@ -117,7 +118,7 @@ function buildExternalDatasourcePlaceholders(systemKey, datasourceCount) {
|
|
|
117
118
|
* @returns {Object} Template context
|
|
118
119
|
*/
|
|
119
120
|
function buildReadmeContext(appName, config) {
|
|
120
|
-
const displayName = formatAppDisplayName(appName);
|
|
121
|
+
const displayName = config.displayName || formatAppDisplayName(appName);
|
|
121
122
|
const port = config.port ?? 3000;
|
|
122
123
|
const localPort = (typeof config.build?.localPort === 'number' && config.build.localPort > 0)
|
|
123
124
|
? config.build.localPort
|
|
@@ -144,7 +145,9 @@ function buildReadmeContext(appName, config) {
|
|
|
144
145
|
function generateReadmeMd(appName, config) {
|
|
145
146
|
if (config.type === 'external') {
|
|
146
147
|
const systemKey = config.systemKey || appName;
|
|
147
|
-
const datasources =
|
|
148
|
+
const datasources = Array.isArray(config.datasources) && config.datasources.length > 0
|
|
149
|
+
? config.datasources
|
|
150
|
+
: buildExternalDatasourcePlaceholders(systemKey, config.datasourceCount);
|
|
148
151
|
return generateExternalReadmeContent({
|
|
149
152
|
appName,
|
|
150
153
|
systemKey,
|
|
@@ -164,7 +167,7 @@ function generateReadmeMd(appName, config) {
|
|
|
164
167
|
* @function generateReadmeMdFile
|
|
165
168
|
* @param {string} appPath - Path to application directory
|
|
166
169
|
* @param {string} appName - Application name
|
|
167
|
-
* @param {Object} config - Application configuration (e.g. from
|
|
170
|
+
* @param {Object} config - Application configuration (e.g. from application.yaml/application.json)
|
|
168
171
|
* @param {Object} [options] - Options
|
|
169
172
|
* @param {boolean} [options.force] - If true, overwrite existing README.md (dynamic generation)
|
|
170
173
|
* @returns {Promise<void>} Resolves when README.md is generated or skipped
|
|
@@ -181,21 +184,22 @@ async function generateReadmeMdFile(appPath, appName, config, options = {}) {
|
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
/**
|
|
184
|
-
* Loads
|
|
187
|
+
* Loads application config from app path and generates README.md (overwrites if present).
|
|
185
188
|
* Used when copying template apps or running up-miso / up-platform / up-dataplane.
|
|
186
189
|
* @async
|
|
187
190
|
* @function ensureReadmeForAppPath
|
|
188
|
-
* @param {string} appPath - Path to application directory (must contain
|
|
191
|
+
* @param {string} appPath - Path to application directory (must contain application config)
|
|
189
192
|
* @param {string} appName - Application name
|
|
190
|
-
* @returns {Promise<void>} Resolves when README.md is written or skipped (no
|
|
193
|
+
* @returns {Promise<void>} Resolves when README.md is written or skipped (no application config)
|
|
191
194
|
*/
|
|
192
195
|
async function ensureReadmeForAppPath(appPath, appName) {
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
let configPath;
|
|
197
|
+
try {
|
|
198
|
+
configPath = resolveApplicationConfigPath(appPath);
|
|
199
|
+
} catch {
|
|
195
200
|
return;
|
|
196
201
|
}
|
|
197
|
-
const
|
|
198
|
-
const config = yaml.load(content) || {};
|
|
202
|
+
const config = loadConfigFile(configPath) || {};
|
|
199
203
|
await generateReadmeMdFile(appPath, appName, config, { force: true });
|
|
200
204
|
}
|
|
201
205
|
|
package/lib/app/register.js
CHANGED
|
@@ -152,7 +152,7 @@ async function registerApplication(appKey, options = {}) {
|
|
|
152
152
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
153
153
|
const { resolveEnvironment } = require('../core/config');
|
|
154
154
|
|
|
155
|
-
// Load
|
|
155
|
+
// Load application config
|
|
156
156
|
const { variables, created } = await loadVariablesYaml(appKey);
|
|
157
157
|
const finalVariables = created
|
|
158
158
|
? await createMinimalAppIfNeeded(appKey, options)
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -13,8 +13,8 @@ const fs = require('fs').promises;
|
|
|
13
13
|
const fsSync = require('fs');
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const chalk = require('chalk');
|
|
16
|
-
const yaml = require('js-yaml');
|
|
17
16
|
const { exec } = require('child_process');
|
|
17
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
18
18
|
const { promisify } = require('util');
|
|
19
19
|
const validator = require('../validation/validator');
|
|
20
20
|
const infra = require('../infrastructure');
|
|
@@ -31,7 +31,7 @@ const { resolveVersionForApp } = require('../utils/image-version');
|
|
|
31
31
|
|
|
32
32
|
const execAsync = promisify(exec);
|
|
33
33
|
|
|
34
|
-
/** Template apps (keycloak, miso-controller, dataplane) - never update
|
|
34
|
+
/** Template apps (keycloak, miso-controller, dataplane) - never update application config when running */
|
|
35
35
|
const TEMPLATE_APP_KEYS = ['keycloak', 'miso-controller', 'dataplane'];
|
|
36
36
|
|
|
37
37
|
// Re-export container helper functions
|
|
@@ -72,19 +72,18 @@ function checkBuilderDirectory(appName) {
|
|
|
72
72
|
function loadAppConfig(appName) {
|
|
73
73
|
const currentDir = process.cwd();
|
|
74
74
|
const builderPath = pathsUtil.getBuilderPath(appName);
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
let configPath;
|
|
76
|
+
try {
|
|
77
|
+
configPath = pathsUtil.resolveApplicationConfigPath(builderPath);
|
|
78
|
+
} catch {
|
|
77
79
|
throw new Error(
|
|
78
|
-
`Application configuration not found
|
|
80
|
+
`Application configuration not found in ${builderPath}\n` +
|
|
79
81
|
`Current directory: ${currentDir}\n` +
|
|
80
|
-
`Expected location: ${builderPath}\n` +
|
|
81
82
|
'Make sure you\'re running from the project root (where \'builder\' directory exists)\n' +
|
|
82
83
|
`Run 'aifabrix create ${appName}' first if configuration doesn't exist`
|
|
83
84
|
);
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
-
const configContent = fsSync.readFileSync(configPath, 'utf8');
|
|
87
|
-
return yaml.load(configContent);
|
|
86
|
+
return loadConfigFile(configPath);
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
/**
|
|
@@ -96,7 +95,7 @@ function formatValidationErrors(validation) {
|
|
|
96
95
|
const allErrors = [];
|
|
97
96
|
|
|
98
97
|
if (validation.variables && validation.variables.errors && validation.variables.errors.length > 0) {
|
|
99
|
-
allErrors.push('
|
|
98
|
+
allErrors.push('application config:');
|
|
100
99
|
allErrors.push(...validation.variables.errors.map(err => ` ${err}`));
|
|
101
100
|
}
|
|
102
101
|
if (validation.rbac && validation.rbac.errors && validation.rbac.errors.length > 0) {
|
|
@@ -129,8 +128,8 @@ async function validateAppConfiguration(appName) {
|
|
|
129
128
|
// Load config
|
|
130
129
|
const appConfig = loadAppConfig(appName);
|
|
131
130
|
|
|
132
|
-
// Validate configuration
|
|
133
|
-
const validation = await validator.validateApplication(appName);
|
|
131
|
+
// Validate configuration (run only uses builder/ apps)
|
|
132
|
+
const validation = await validator.validateApplication(appName, { type: 'app' });
|
|
134
133
|
if (!validation.valid) {
|
|
135
134
|
const allErrors = formatValidationErrors(validation);
|
|
136
135
|
if (allErrors.length === 0) {
|
|
@@ -143,8 +142,8 @@ async function validateAppConfiguration(appName) {
|
|
|
143
142
|
}
|
|
144
143
|
|
|
145
144
|
/**
|
|
146
|
-
* Resolves version from image and updates builder
|
|
147
|
-
* Template apps (keycloak, miso-controller, dataplane) are never updated -
|
|
145
|
+
* Resolves version from image and updates builder application config when running.
|
|
146
|
+
* Template apps (keycloak, miso-controller, dataplane) are never updated - config stays pristine.
|
|
148
147
|
* @async
|
|
149
148
|
* @param {string} appName - Application name
|
|
150
149
|
* @param {Object} appConfig - Application configuration
|
|
@@ -263,18 +262,21 @@ async function copyEnvToDev(builderEnvPath, devEnvPath) {
|
|
|
263
262
|
* Handle envOutputPath configuration
|
|
264
263
|
* @async
|
|
265
264
|
* @param {string} appName - Application name
|
|
266
|
-
* @param {string}
|
|
265
|
+
* @param {string} configPath - Path to application config file
|
|
267
266
|
* @param {string} builderEnvPath - Path to builder .env file
|
|
268
267
|
* @param {string} devEnvPath - Path to dev .env file
|
|
269
268
|
* @param {boolean} [skipOutputPath=false] - When true, skip (e.g. up-miso/up-dataplane, no local code)
|
|
270
269
|
*/
|
|
271
|
-
async function handleEnvOutputPath(appName,
|
|
272
|
-
if (skipOutputPath
|
|
270
|
+
async function handleEnvOutputPath(appName, configPath, builderEnvPath, devEnvPath, skipOutputPath = false) {
|
|
271
|
+
if (skipOutputPath) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
let variables;
|
|
275
|
+
try {
|
|
276
|
+
variables = loadConfigFile(configPath);
|
|
277
|
+
} catch {
|
|
273
278
|
return;
|
|
274
279
|
}
|
|
275
|
-
|
|
276
|
-
const variablesContent = fsSync.readFileSync(variablesPath, 'utf8');
|
|
277
|
-
const variables = yaml.load(variablesContent);
|
|
278
280
|
|
|
279
281
|
if (variables?.build?.envOutputPath && variables.build.envOutputPath !== null) {
|
|
280
282
|
logger.log(chalk.blue('Ensuring .env file in apps/ directory is updated for Docker...'));
|
|
@@ -338,8 +340,15 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
338
340
|
await copyEnvToDev(builderEnvPath, devEnvPath);
|
|
339
341
|
|
|
340
342
|
// Handle envOutputPath if configured (skipped when skipEnvOutputPath e.g. up-miso/up-dataplane)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
+
let configPath;
|
|
344
|
+
try {
|
|
345
|
+
configPath = pathsUtil.resolveApplicationConfigPath(devDir);
|
|
346
|
+
} catch {
|
|
347
|
+
configPath = null;
|
|
348
|
+
}
|
|
349
|
+
if (configPath) {
|
|
350
|
+
await handleEnvOutputPath(appName, configPath, builderEnvPath, devEnvPath, skipEnvOutputPath);
|
|
351
|
+
}
|
|
343
352
|
|
|
344
353
|
// Generate Docker Compose
|
|
345
354
|
const composeOptions = { ...options };
|
package/lib/app/run.js
CHANGED
|
@@ -10,13 +10,18 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const chalk = require('chalk');
|
|
13
|
+
const { exec } = require('child_process');
|
|
14
|
+
const { promisify } = require('util');
|
|
13
15
|
const config = require('../core/config');
|
|
14
16
|
const logger = require('../utils/logger');
|
|
15
17
|
const { checkPortAvailable, waitForHealthCheck } = require('../utils/health-check');
|
|
16
18
|
const composeGenerator = require('../utils/compose-generator');
|
|
19
|
+
const containerHelpers = require('../utils/app-run-containers');
|
|
17
20
|
// Helper functions extracted to reduce file size and complexity
|
|
18
21
|
const helpers = require('./run-helpers');
|
|
19
22
|
|
|
23
|
+
const execAsync = promisify(exec);
|
|
24
|
+
|
|
20
25
|
/**
|
|
21
26
|
* Validate app for run and check if it's an external system
|
|
22
27
|
* @async
|
|
@@ -30,18 +35,20 @@ async function validateAppForRun(appName, _debug) {
|
|
|
30
35
|
throw new Error('Application name is required');
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
//
|
|
38
|
+
// Run only supports regular apps in builder/ (path resolution: integration first, then builder)
|
|
34
39
|
const { detectAppType } = require('../utils/paths');
|
|
35
40
|
try {
|
|
36
|
-
const { isExternal } = await detectAppType(appName);
|
|
37
|
-
if (isExternal) {
|
|
41
|
+
const { isExternal, baseDir } = await detectAppType(appName);
|
|
42
|
+
if (baseDir !== 'builder' || isExternal) {
|
|
38
43
|
logger.log(chalk.yellow('⚠️ External systems don\'t run as Docker containers.'));
|
|
39
44
|
logger.log(chalk.blue('Use "aifabrix build" to deploy to dataplane, then test via OpenAPI endpoints.'));
|
|
40
45
|
return false;
|
|
41
46
|
}
|
|
42
47
|
} catch (error) {
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Application "${appName}" not found in builder/. Only applications in builder/ can be run.\n` +
|
|
50
|
+
(error.message || '')
|
|
51
|
+
);
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
return true;
|
|
@@ -202,8 +209,40 @@ async function runApp(appName, options = {}) {
|
|
|
202
209
|
throw new Error(`Failed to run application: ${error.message}`);
|
|
203
210
|
}
|
|
204
211
|
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Restart a running application container (Docker restart).
|
|
215
|
+
* Only applies to apps in builder/ run via aifabrix run.
|
|
216
|
+
*
|
|
217
|
+
* @async
|
|
218
|
+
* @function restartApp
|
|
219
|
+
* @param {string} appName - Application name (must be running)
|
|
220
|
+
* @returns {Promise<void>} Resolves when container is restarted
|
|
221
|
+
* @throws {Error} If app name is invalid, container not found, or restart fails
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* await restartApp('myapp');
|
|
225
|
+
*/
|
|
226
|
+
async function restartApp(appName) {
|
|
227
|
+
if (!appName || typeof appName !== 'string') {
|
|
228
|
+
throw new Error('Application name is required and must be a string');
|
|
229
|
+
}
|
|
230
|
+
const developerId = await config.getDeveloperId();
|
|
231
|
+
const containerName = containerHelpers.getContainerName(appName, developerId);
|
|
232
|
+
try {
|
|
233
|
+
await execAsync(`docker restart ${containerName}`);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const msg = (error.stderr || error.stdout || error.message || '').toLowerCase();
|
|
236
|
+
if (msg.includes('no such container') || msg.includes('is not running')) {
|
|
237
|
+
throw new Error(`Application '${appName}' is not running. Start it with: aifabrix run ${appName}`);
|
|
238
|
+
}
|
|
239
|
+
throw new Error(`Failed to restart application: ${error.message}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
205
243
|
module.exports = {
|
|
206
244
|
runApp,
|
|
245
|
+
restartApp,
|
|
207
246
|
checkImageExists: helpers.checkImageExists,
|
|
208
247
|
checkContainerRunning: helpers.checkContainerRunning,
|
|
209
248
|
stopAndRemoveContainer: helpers.stopAndRemoveContainer,
|
package/lib/app/show-display.js
CHANGED
|
@@ -15,14 +15,15 @@ const logger = require('../utils/logger');
|
|
|
15
15
|
function logSourceAndHeader(summary) {
|
|
16
16
|
const isOffline = summary.source === 'offline';
|
|
17
17
|
const sourceLabel = isOffline
|
|
18
|
-
?
|
|
19
|
-
:
|
|
18
|
+
? `🔴 Source: offline (${summary.path || '—'})`
|
|
19
|
+
: `🟢 Source: online (${summary.controllerUrl || '—'})`;
|
|
20
20
|
logger.log(sourceLabel);
|
|
21
21
|
logger.log('');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function logApplicationRequired(a) {
|
|
25
|
-
|
|
25
|
+
const typeLabel = (a.type && String(a.type).toLowerCase()) || 'webapp';
|
|
26
|
+
logger.log(`📱 Application - ${typeLabel}`);
|
|
26
27
|
logger.log(` Key: ${a.key ?? '—'}`);
|
|
27
28
|
logger.log(` Display name: ${a.displayName ?? '—'}`);
|
|
28
29
|
logger.log(` Description: ${a.description ?? '—'}`);
|
|
@@ -53,19 +54,49 @@ function logApplicationFields(a) {
|
|
|
53
54
|
logApplicationOptional(a);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
/** Application section for online + external: Key, Display name, Description, Deployment, Status (no Type; Dataplane block follows). */
|
|
58
|
+
function logApplicationFieldsOnlineExternal(a) {
|
|
59
|
+
logger.log('🧷 Application - external');
|
|
60
|
+
logger.log(` Key: ${a.key ?? '—'}`);
|
|
61
|
+
logger.log(` Display name: ${a.displayName ?? '—'}`);
|
|
62
|
+
logger.log(` Description: ${a.description ?? '—'}`);
|
|
63
|
+
logger.log(' Deployment: —');
|
|
64
|
+
if (a.status !== undefined && a.status !== '—') logger.log(` Status: ${a.status}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Application section for offline + external: Key, Display name, Description, Deployment, Status, Version when present. */
|
|
68
|
+
function logApplicationFieldsOfflineExternal(a) {
|
|
69
|
+
logger.log('🧷 Application - external');
|
|
70
|
+
logger.log(` Key: ${a.key ?? '—'}`);
|
|
71
|
+
logger.log(` Display name: ${a.displayName ?? '—'}`);
|
|
72
|
+
logger.log(` Description: ${a.description ?? '—'}`);
|
|
73
|
+
logger.log(' Deployment: —');
|
|
74
|
+
if (a.status !== undefined && a.status !== '—') logger.log(` Status: ${a.status}`);
|
|
75
|
+
if (a.version !== undefined && a.version !== null) logger.log(` Version: ${a.version}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function logApplicationExternalIntegration(ei, options = {}) {
|
|
57
79
|
logger.log(' External integration:');
|
|
58
80
|
logger.log(` schemaBasePath: ${ei.schemaBasePath}`);
|
|
59
81
|
logger.log(` systems: [${(ei.systems || []).join(', ')}]`);
|
|
60
82
|
logger.log(` dataSources: [${(ei.dataSources || []).join(', ')}]`);
|
|
61
|
-
|
|
83
|
+
if (!options.skipHint) {
|
|
84
|
+
logger.log(chalk.gray('\n For external system data as on dataplane, run: aifabrix show <appKey> --online or aifabrix app show <appKey>.'));
|
|
85
|
+
}
|
|
62
86
|
}
|
|
63
87
|
|
|
64
|
-
function logApplicationSection(a,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
function logApplicationSection(a, summary) {
|
|
89
|
+
const isExternal = summary.isExternal;
|
|
90
|
+
const onlineExternal = summary.source === 'online' && isExternal;
|
|
91
|
+
const offlineExternal = summary.source === 'offline' && isExternal;
|
|
92
|
+
if (onlineExternal) {
|
|
93
|
+
logApplicationFieldsOnlineExternal(a);
|
|
94
|
+
} else if (offlineExternal) {
|
|
95
|
+
logApplicationFieldsOfflineExternal(a);
|
|
96
|
+
} else {
|
|
97
|
+
logApplicationFields(a);
|
|
68
98
|
}
|
|
99
|
+
/* External integration is logged after Dataplane block in display() when external */
|
|
69
100
|
}
|
|
70
101
|
|
|
71
102
|
function logRolesSection(roles) {
|
|
@@ -125,13 +156,13 @@ function logDatabasesSection(dbNames) {
|
|
|
125
156
|
}
|
|
126
157
|
|
|
127
158
|
function logExternalSystemMain(ext) {
|
|
128
|
-
logger.log('
|
|
129
|
-
logger.log(` Dataplane: ${ext.dataplaneUrl}`);
|
|
130
|
-
logger.log(` System key: ${ext.systemKey}`);
|
|
131
|
-
logger.log(` Display name: ${ext.displayName}`);
|
|
132
|
-
logger.log(` Type: ${ext.type}`);
|
|
133
|
-
logger.log(` Status: ${ext.status}`);
|
|
159
|
+
logger.log('🧩 Dataplane');
|
|
134
160
|
logger.log(` Version: ${ext.version ?? '—'}`);
|
|
161
|
+
logger.log(` Credential: ${ext.credentialId ?? '—'}`);
|
|
162
|
+
logger.log(` Status: ${ext.status ?? '—'}`);
|
|
163
|
+
logger.log(` API docs: ${ext.openApiDocsPageUrl ?? ext.apiDocumentUrl ?? '—'}`);
|
|
164
|
+
logger.log(` MCP server: ${ext.mcpServerUrl ?? '—'}`);
|
|
165
|
+
logger.log(` OpenAPI spec: ${ext.apiDocumentUrl ?? '—'}`);
|
|
135
166
|
}
|
|
136
167
|
|
|
137
168
|
function logExternalSystemDataSources(dataSources) {
|
|
@@ -144,8 +175,8 @@ function logExternalSystemDataSources(dataSources) {
|
|
|
144
175
|
|
|
145
176
|
/**
|
|
146
177
|
* Log OpenAPI and MCP documentation links for external system (dataplane endpoints).
|
|
147
|
-
*
|
|
148
|
-
* @param {Object} ext - External system result (dataplaneUrl, systemKey, dataSources, openapiFiles, openapiEndpoints)
|
|
178
|
+
* Uses openApiDocsPageUrl from dataplane when available; otherwise REST/MCP constructed URLs.
|
|
179
|
+
* @param {Object} ext - External system result (dataplaneUrl, systemKey, dataSources, openapiFiles, openapiEndpoints, openApiDocsPageUrl)
|
|
149
180
|
*/
|
|
150
181
|
function logExternalSystemServiceLinks(ext) {
|
|
151
182
|
if (!ext || !ext.dataplaneUrl || !ext.systemKey) return;
|
|
@@ -155,9 +186,13 @@ function logExternalSystemServiceLinks(ext) {
|
|
|
155
186
|
(ext.openapiEndpoints && ext.openapiEndpoints.length > 0);
|
|
156
187
|
const dataSources = ext.dataSources || [];
|
|
157
188
|
const hasMcp = dataSources.length > 0;
|
|
158
|
-
|
|
189
|
+
const openApiDocsPageUrl = ext.openApiDocsPageUrl;
|
|
190
|
+
if (!hasOpenApi && !hasMcp && !openApiDocsPageUrl) return;
|
|
159
191
|
|
|
160
192
|
logger.log(' Service links:');
|
|
193
|
+
if (openApiDocsPageUrl) {
|
|
194
|
+
logger.log(` OpenAPI docs page: ${openApiDocsPageUrl}`);
|
|
195
|
+
}
|
|
161
196
|
logger.log(` REST OpenAPI: ${base}/api/v1/rest/${sk}/docs`);
|
|
162
197
|
if (hasMcp) {
|
|
163
198
|
dataSources.forEach((ds) => {
|
|
@@ -175,24 +210,55 @@ function logExternalSystemApplication(ap) {
|
|
|
175
210
|
if (ap.permissions) logger.log(` permissions: ${Array.isArray(ap.permissions) ? ap.permissions.join(', ') : ap.permissions}`);
|
|
176
211
|
}
|
|
177
212
|
|
|
178
|
-
function logExternalSystemSection(ext) {
|
|
213
|
+
function logExternalSystemSection(ext, options = {}) {
|
|
179
214
|
if (!ext) return;
|
|
180
|
-
logger.log('');
|
|
215
|
+
if (!options.afterApplication) logger.log('');
|
|
181
216
|
if (ext.error) {
|
|
182
|
-
logger.log('
|
|
217
|
+
logger.log('🧩 Dataplane: not available (dataplane unreachable or not found).');
|
|
183
218
|
return;
|
|
184
219
|
}
|
|
185
220
|
logExternalSystemMain(ext);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
221
|
+
if (!options.compact) {
|
|
222
|
+
logExternalSystemDataSources(ext.dataSources);
|
|
223
|
+
logExternalSystemApplication(ext.application);
|
|
224
|
+
if (ext.openapiFiles && ext.openapiFiles.length > 0) {
|
|
225
|
+
logger.log(` OpenAPI files: ${ext.openapiFiles.length}`);
|
|
226
|
+
}
|
|
227
|
+
if (ext.openapiEndpoints && ext.openapiEndpoints.length > 0) {
|
|
228
|
+
const sample = ext.openapiEndpoints.slice(0, 3).map((e) => `${e.method || 'GET'} ${e.path || e.pathPattern || e}`).join(', ');
|
|
229
|
+
logger.log(` OpenAPI endpoints: ${ext.openapiEndpoints.length} (e.g. ${sample}${ext.openapiEndpoints.length > 3 ? ' …' : ''})`);
|
|
230
|
+
}
|
|
231
|
+
logExternalSystemServiceLinks(ext);
|
|
190
232
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function getDisplayContext(summary) {
|
|
236
|
+
const a = summary.application;
|
|
237
|
+
const databases = summary.databases ?? a.databases ?? [];
|
|
238
|
+
const dbNames = Array.isArray(databases) ? databases.map((d) => (d && d.name) || d).filter(Boolean) : [];
|
|
239
|
+
return {
|
|
240
|
+
a,
|
|
241
|
+
roles: summary.roles ?? a.roles ?? [],
|
|
242
|
+
permissions: summary.permissions ?? a.permissions ?? [],
|
|
243
|
+
authentication: summary.authentication ?? a.authentication,
|
|
244
|
+
portalInputConfigurations: summary.portalInputConfigurations ?? a.portalInputConfigurations ?? [],
|
|
245
|
+
dbNames
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function displayExternalAppBlock(summary) {
|
|
250
|
+
logger.log('');
|
|
251
|
+
if (summary.externalSystem && !summary.externalSystem.error) {
|
|
252
|
+
logExternalSystemSection(summary.externalSystem, { afterApplication: true, compact: true });
|
|
253
|
+
} else {
|
|
254
|
+
const reason = summary.externalSystem && summary.externalSystem.error ? ` (${summary.externalSystem.error})` : '';
|
|
255
|
+
logger.log(`🧩 Dataplane: not available${reason}`);
|
|
256
|
+
}
|
|
257
|
+
if (summary.application.externalIntegration) {
|
|
258
|
+
logger.log('');
|
|
259
|
+
const skipHint = summary.source === 'online' && summary.externalSystem && !summary.externalSystem.error;
|
|
260
|
+
logApplicationExternalIntegration(summary.application.externalIntegration, { skipHint });
|
|
194
261
|
}
|
|
195
|
-
logExternalSystemServiceLinks(ext);
|
|
196
262
|
}
|
|
197
263
|
|
|
198
264
|
/**
|
|
@@ -202,27 +268,21 @@ function logExternalSystemSection(ext) {
|
|
|
202
268
|
* @param {boolean} [options.permissionsOnly] - When true, output only source and Permissions section
|
|
203
269
|
*/
|
|
204
270
|
function display(summary, options = {}) {
|
|
205
|
-
const
|
|
206
|
-
const roles = summary.roles ?? a.roles ?? [];
|
|
207
|
-
const permissions = summary.permissions ?? a.permissions ?? [];
|
|
208
|
-
const authentication = summary.authentication ?? a.authentication;
|
|
209
|
-
const portalInputConfigurations = summary.portalInputConfigurations ?? a.portalInputConfigurations ?? [];
|
|
210
|
-
const databases = summary.databases ?? a.databases ?? [];
|
|
211
|
-
const dbNames = Array.isArray(databases) ? databases.map((d) => (d && d.name) || d).filter(Boolean) : [];
|
|
271
|
+
const ctx = getDisplayContext(summary);
|
|
212
272
|
|
|
213
273
|
logSourceAndHeader(summary);
|
|
214
274
|
if (options.permissionsOnly) {
|
|
215
|
-
logPermissionsSection(permissions, { showWhenEmpty: true });
|
|
275
|
+
logPermissionsSection(ctx.permissions, { showWhenEmpty: true });
|
|
216
276
|
logger.log('');
|
|
217
277
|
return;
|
|
218
278
|
}
|
|
219
|
-
logApplicationSection(a, summary
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
logAuthSection(authentication);
|
|
223
|
-
logConfigurationsSection(portalInputConfigurations);
|
|
224
|
-
logDatabasesSection(dbNames);
|
|
225
|
-
logExternalSystemSection(summary.externalSystem);
|
|
279
|
+
logApplicationSection(ctx.a, summary);
|
|
280
|
+
if (summary.isExternal) displayExternalAppBlock(summary);
|
|
281
|
+
logRolesSection(ctx.roles);
|
|
282
|
+
logAuthSection(ctx.authentication);
|
|
283
|
+
logConfigurationsSection(ctx.portalInputConfigurations);
|
|
284
|
+
logDatabasesSection(ctx.dbNames);
|
|
285
|
+
if (!summary.isExternal) logExternalSystemSection(summary.externalSystem);
|
|
226
286
|
logger.log('');
|
|
227
287
|
}
|
|
228
288
|
|