@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
|
@@ -317,10 +317,11 @@ function displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo) {
|
|
|
317
317
|
* Controller and environment come from config.yaml (set via aifabrix login or aifabrix auth config).
|
|
318
318
|
* @async
|
|
319
319
|
* @function handleAuthStatus
|
|
320
|
-
* @param {Object}
|
|
320
|
+
* @param {Object} options - Command options
|
|
321
|
+
* @param {boolean} [options.validate] - If true, exit with code 1 when not authenticated
|
|
321
322
|
* @returns {Promise<void>} Resolves when status is displayed
|
|
322
323
|
*/
|
|
323
|
-
async function handleAuthStatus(
|
|
324
|
+
async function handleAuthStatus(options = {}) {
|
|
324
325
|
const { resolveEnvironment } = require('../core/config');
|
|
325
326
|
const controllerUrl = await resolveControllerUrl();
|
|
326
327
|
const environment = await resolveEnvironment();
|
|
@@ -342,6 +343,38 @@ async function handleAuthStatus(_options) {
|
|
|
342
343
|
}
|
|
343
344
|
|
|
344
345
|
displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo);
|
|
346
|
+
|
|
347
|
+
if (options.validate && (!tokenInfo || !tokenInfo.authenticated)) {
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Get resolved auth context (controller URL, environment, authConfig) for use after validation.
|
|
354
|
+
* Call only when auth status --validate has already passed (e.g. in manual tests).
|
|
355
|
+
* @async
|
|
356
|
+
* @function getValidatedAuthContext
|
|
357
|
+
* @returns {Promise<{controllerUrl: string, environment: string, authConfig: Object, dataplaneUrl: string|null}>}
|
|
358
|
+
* @throws {Error} If not authenticated
|
|
359
|
+
*/
|
|
360
|
+
async function getValidatedAuthContext() {
|
|
361
|
+
const { resolveEnvironment } = require('../core/config');
|
|
362
|
+
const controllerUrl = await resolveControllerUrl();
|
|
363
|
+
const environment = await resolveEnvironment();
|
|
364
|
+
|
|
365
|
+
let tokenInfo = await checkDeviceToken(controllerUrl);
|
|
366
|
+
if (!tokenInfo) {
|
|
367
|
+
tokenInfo = await checkClientToken(controllerUrl, environment);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (!tokenInfo || !tokenInfo.authenticated || !tokenInfo.token) {
|
|
371
|
+
throw new Error('Not authenticated. Run "aifabrix login" or configure client credentials.');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const authConfig = { type: 'bearer', token: tokenInfo.token };
|
|
375
|
+
const dataplaneUrl = await resolveDataplaneUrlSilent(controllerUrl, environment, authConfig);
|
|
376
|
+
|
|
377
|
+
return { controllerUrl, environment, authConfig, dataplaneUrl };
|
|
345
378
|
}
|
|
346
379
|
|
|
347
|
-
module.exports = { handleAuthStatus };
|
|
380
|
+
module.exports = { handleAuthStatus, getValidatedAuthContext };
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert command: convert integration/external system and datasource config files between JSON and YAML.
|
|
3
|
+
*
|
|
4
|
+
* Process: validate first, then (unless --force) prompt for confirmation, then convert (write new files),
|
|
5
|
+
* update application config links, then delete old files.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Convert config format (JSON/YAML) for external integration files
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const readline = require('readline');
|
|
17
|
+
const { detectAppType } = require('../utils/paths');
|
|
18
|
+
const { logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
19
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
20
|
+
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
21
|
+
|
|
22
|
+
const TARGET_EXT = { yaml: '.yaml', json: '.json' };
|
|
23
|
+
const APP_CONFIG_NAMES = { yaml: 'application.yaml', json: 'application.json' };
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Prompts the user for confirmation (y/N).
|
|
27
|
+
*
|
|
28
|
+
* @param {string} message - Prompt message
|
|
29
|
+
* @returns {Promise<boolean>} True if user confirms (y/yes), false otherwise
|
|
30
|
+
*/
|
|
31
|
+
function promptConfirm(message) {
|
|
32
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
33
|
+
return new Promise(resolve => {
|
|
34
|
+
rl.question(message, answer => {
|
|
35
|
+
rl.close();
|
|
36
|
+
const normalized = (answer || '').trim().toLowerCase();
|
|
37
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns the target filename for a given file path and format (same base name, new extension).
|
|
44
|
+
*
|
|
45
|
+
* @param {string} filePath - Current file path
|
|
46
|
+
* @param {string} format - Target format: 'json' or 'yaml'
|
|
47
|
+
* @returns {string} New filename (basename only)
|
|
48
|
+
*/
|
|
49
|
+
function targetFileName(filePath, format) {
|
|
50
|
+
const base = path.basename(filePath, path.extname(filePath));
|
|
51
|
+
const ext = TARGET_EXT[format] || (format === 'json' ? '.json' : '.yaml');
|
|
52
|
+
return base + ext;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Converts a single config file to the target format: writes to new path, returns old and new paths.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} sourcePath - Absolute path to existing file
|
|
59
|
+
* @param {string} targetPath - Absolute path for new file
|
|
60
|
+
* @param {string} format - 'json' or 'yaml'
|
|
61
|
+
* @returns {{ oldPath: string, newPath: string }}
|
|
62
|
+
*/
|
|
63
|
+
function convertOneFile(sourcePath, targetPath, format) {
|
|
64
|
+
const obj = loadConfigFile(sourcePath);
|
|
65
|
+
writeConfigFile(targetPath, obj, format);
|
|
66
|
+
return { oldPath: sourcePath, newPath: targetPath };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Converts a list of config files (system or datasource) in a directory; skips missing files.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} schemaBasePath - Base directory for files
|
|
73
|
+
* @param {string[]} fileNames - List of filenames
|
|
74
|
+
* @param {string} format - 'json' or 'yaml'
|
|
75
|
+
* @returns {{ converted: string[], toDelete: string[], newNames: string[] }}
|
|
76
|
+
*/
|
|
77
|
+
function convertFileList(schemaBasePath, fileNames, format) {
|
|
78
|
+
const converted = [];
|
|
79
|
+
const toDelete = [];
|
|
80
|
+
const newNames = [];
|
|
81
|
+
for (const fileName of fileNames) {
|
|
82
|
+
const sourcePath = path.join(schemaBasePath, fileName);
|
|
83
|
+
if (!fs.existsSync(sourcePath)) {
|
|
84
|
+
newNames.push(fileName);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const newFileName = targetFileName(sourcePath, format);
|
|
88
|
+
const targetPath = path.join(schemaBasePath, newFileName);
|
|
89
|
+
convertOneFile(sourcePath, targetPath, format);
|
|
90
|
+
converted.push(targetPath);
|
|
91
|
+
newNames.push(newFileName);
|
|
92
|
+
if (path.normalize(sourcePath) !== path.normalize(targetPath)) {
|
|
93
|
+
toDelete.push(sourcePath);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { converted, toDelete, newNames };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validates app and prompts for confirmation unless opts.force.
|
|
101
|
+
*
|
|
102
|
+
* @param {Object} opts - Options
|
|
103
|
+
* @param {string} opts.appName - Application name
|
|
104
|
+
* @param {Object} opts.cmdOptions - Command options (force, type)
|
|
105
|
+
* @param {string} opts.schemaBasePath - Base path for external files
|
|
106
|
+
* @param {string[]} opts.systemFiles - System file names
|
|
107
|
+
* @param {string[]} opts.datasourceFiles - Datasource file names
|
|
108
|
+
* @param {string} opts.configPath - Current application config path
|
|
109
|
+
* @param {string} opts.format - Target format
|
|
110
|
+
* @throws {Error} If validation fails or user cancels
|
|
111
|
+
*/
|
|
112
|
+
async function validateAndPrompt(opts) {
|
|
113
|
+
const validate = require('../validation/validate');
|
|
114
|
+
const result = await validate.validateAppOrFile(opts.appName, opts.cmdOptions);
|
|
115
|
+
if (!result.valid) {
|
|
116
|
+
validate.displayValidationResults(result);
|
|
117
|
+
throw new Error('Validation failed. Fix errors before converting.');
|
|
118
|
+
}
|
|
119
|
+
const { configPath, format, schemaBasePath, systemFiles, datasourceFiles } = opts;
|
|
120
|
+
const appConfigName = APP_CONFIG_NAMES[format];
|
|
121
|
+
const targetConfigPath = path.join(path.dirname(configPath), appConfigName);
|
|
122
|
+
const willConvertAppConfig = path.normalize(configPath) !== path.normalize(targetConfigPath) ||
|
|
123
|
+
path.extname(configPath) !== (format === 'json' ? '.json' : '.yaml');
|
|
124
|
+
const summaryLines = [...systemFiles, ...datasourceFiles]
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.map(f => ` • ${f} → ${targetFileName(path.join(schemaBasePath, f), format)}`);
|
|
127
|
+
if (willConvertAppConfig) summaryLines.push(` • application config → ${appConfigName}`);
|
|
128
|
+
summaryLines.push(' Old files will be removed after writing new ones.');
|
|
129
|
+
if (!opts.cmdOptions.force) {
|
|
130
|
+
const confirmed = await promptConfirm(`Convert the following to ${format}?\n${summaryLines.join('\n')}\nAre you sure? (y/N) `);
|
|
131
|
+
if (!confirmed) throw new Error('Convert cancelled.');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Converts files, updates application config, and deletes old files.
|
|
137
|
+
*
|
|
138
|
+
* @param {Object} variables - Current application config
|
|
139
|
+
* @param {string} configPath - Current application config path
|
|
140
|
+
* @param {string} schemaBasePath - Base path for external files
|
|
141
|
+
* @param {string[]} systemFiles - System file names
|
|
142
|
+
* @param {string[]} datasourceFiles - Datasource file names
|
|
143
|
+
* @param {string} format - Target format
|
|
144
|
+
* @returns {{ converted: string[], deleted: string[] }}
|
|
145
|
+
*/
|
|
146
|
+
function executeConversion(variables, configPath, schemaBasePath, systemFiles, datasourceFiles, format) {
|
|
147
|
+
const sys = convertFileList(schemaBasePath, systemFiles, format);
|
|
148
|
+
const ds = convertFileList(schemaBasePath, datasourceFiles, format);
|
|
149
|
+
const converted = [...sys.converted, ...ds.converted];
|
|
150
|
+
const toDelete = [...sys.toDelete, ...ds.toDelete];
|
|
151
|
+
const updatedVariables = { ...variables };
|
|
152
|
+
if (updatedVariables.externalIntegration) {
|
|
153
|
+
updatedVariables.externalIntegration = { ...updatedVariables.externalIntegration };
|
|
154
|
+
updatedVariables.externalIntegration.systems = sys.newNames;
|
|
155
|
+
updatedVariables.externalIntegration.dataSources = ds.newNames;
|
|
156
|
+
}
|
|
157
|
+
const appConfigName = APP_CONFIG_NAMES[format];
|
|
158
|
+
const targetConfigPath = path.join(path.dirname(configPath), appConfigName);
|
|
159
|
+
writeConfigFile(targetConfigPath, updatedVariables, format);
|
|
160
|
+
converted.push(targetConfigPath);
|
|
161
|
+
if (path.normalize(configPath) !== path.normalize(targetConfigPath)) toDelete.push(configPath);
|
|
162
|
+
toDelete.forEach(oldPath => {
|
|
163
|
+
if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath);
|
|
164
|
+
});
|
|
165
|
+
return { converted, deleted: toDelete };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Runs conversion: validate first, optional prompt, convert files, update app config links, delete old files.
|
|
170
|
+
*
|
|
171
|
+
* @param {string} appName - Application name
|
|
172
|
+
* @param {Object} options - Command options
|
|
173
|
+
* @param {string} options.format - Target format: 'json' or 'yaml'
|
|
174
|
+
* @param {boolean} [options.force] - Skip confirmation prompt
|
|
175
|
+
*
|
|
176
|
+
* @returns {Promise<{ converted: string[], deleted: string[] }>} Lists of converted and deleted file paths
|
|
177
|
+
* @throws {Error} If validation fails, user aborts, or conversion fails
|
|
178
|
+
*/
|
|
179
|
+
async function runConvert(appName, options) {
|
|
180
|
+
const format = (options.format || '').toLowerCase();
|
|
181
|
+
if (format !== 'json' && format !== 'yaml') {
|
|
182
|
+
throw new Error('Option --format is required and must be \'json\' or \'yaml\'');
|
|
183
|
+
}
|
|
184
|
+
const { appPath } = await detectAppType(appName);
|
|
185
|
+
logOfflinePathWhenType(appPath);
|
|
186
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
187
|
+
const variables = loadConfigFile(configPath);
|
|
188
|
+
const schemaBasePath = path.resolve(path.dirname(configPath), variables.externalIntegration?.schemaBasePath || './');
|
|
189
|
+
const systemFiles = variables.externalIntegration?.systems || [];
|
|
190
|
+
const datasourceFiles = variables.externalIntegration?.dataSources || [];
|
|
191
|
+
await validateAndPrompt({
|
|
192
|
+
appName, cmdOptions: options, schemaBasePath, systemFiles, datasourceFiles, configPath, format
|
|
193
|
+
});
|
|
194
|
+
return executeConversion(variables, configPath, schemaBasePath, systemFiles, datasourceFiles, format);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
runConvert,
|
|
199
|
+
promptConfirm,
|
|
200
|
+
targetFileName,
|
|
201
|
+
convertOneFile
|
|
202
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Credential list command – list credentials from
|
|
2
|
+
* Credential list command – list credentials from Dataplane
|
|
3
3
|
* GET /api/v1/credential. Used by `aifabrix credential list`.
|
|
4
|
+
* The Controller does not expose this endpoint; credentials are listed from the Dataplane.
|
|
4
5
|
*
|
|
5
6
|
* @fileoverview Credential list command implementation
|
|
6
7
|
* @author AI Fabrix Team
|
|
@@ -11,7 +12,8 @@ const chalk = require('chalk');
|
|
|
11
12
|
const logger = require('../utils/logger');
|
|
12
13
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
13
14
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
14
|
-
const { normalizeControllerUrl } = require('../core/config');
|
|
15
|
+
const { normalizeControllerUrl, resolveEnvironment } = require('../core/config');
|
|
16
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
15
17
|
const { listCredentials } = require('../api/credentials.api');
|
|
16
18
|
|
|
17
19
|
const DEFAULT_PAGE_SIZE = 50;
|
|
@@ -48,10 +50,10 @@ function extractCredentials(response) {
|
|
|
48
50
|
/**
|
|
49
51
|
* Display credential list to user
|
|
50
52
|
* @param {Array} list - Credentials array
|
|
51
|
-
* @param {string}
|
|
53
|
+
* @param {string} baseUrl - Dataplane (or base) URL for header
|
|
52
54
|
*/
|
|
53
|
-
function displayCredentialList(list,
|
|
54
|
-
logger.log(chalk.bold(`\n🔐 Credentials (${
|
|
55
|
+
function displayCredentialList(list, baseUrl) {
|
|
56
|
+
logger.log(chalk.bold(`\n🔐 Credentials (${baseUrl}):\n`));
|
|
55
57
|
if (list.length === 0) {
|
|
56
58
|
logger.log(chalk.gray(' No credentials found.\n'));
|
|
57
59
|
return;
|
|
@@ -65,36 +67,95 @@ function displayCredentialList(list, controllerUrl) {
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/**
|
|
68
|
-
*
|
|
70
|
+
* Ensure controller URL and auth; exit on failure. Returns { controllerUrl, authConfig } when valid.
|
|
69
71
|
* @async
|
|
70
|
-
* @param {Object} options - CLI options
|
|
71
|
-
* @
|
|
72
|
-
* @param {boolean} [options.activeOnly] - List only active credentials
|
|
73
|
-
* @param {number} [options.pageSize] - Items per page
|
|
74
|
-
* @returns {Promise<void>}
|
|
72
|
+
* @param {Object} options - CLI options with optional controller
|
|
73
|
+
* @returns {Promise<{controllerUrl: string, authConfig: Object}>}
|
|
75
74
|
*/
|
|
76
|
-
async function
|
|
75
|
+
async function ensureControllerAndAuth(options) {
|
|
77
76
|
const controllerUrl = options.controller || (await resolveControllerUrl());
|
|
78
77
|
if (!controllerUrl) {
|
|
79
78
|
logger.error(chalk.red('❌ Controller URL is required. Run "aifabrix login" first.'));
|
|
80
79
|
process.exit(1);
|
|
81
|
-
return;
|
|
82
80
|
}
|
|
83
81
|
const authResult = await getCredentialListAuth(controllerUrl);
|
|
84
82
|
if (!authResult || !authResult.token) {
|
|
85
83
|
logger.error(chalk.red(`❌ No authentication token for controller: ${controllerUrl}`));
|
|
86
84
|
logger.error(chalk.gray('Run: aifabrix login'));
|
|
87
85
|
process.exit(1);
|
|
88
|
-
return;
|
|
89
86
|
}
|
|
90
|
-
|
|
87
|
+
return {
|
|
88
|
+
controllerUrl,
|
|
89
|
+
authConfig: { type: 'bearer', token: authResult.token }
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolve Dataplane URL for credential list (override or discover from controller + environment)
|
|
95
|
+
* @async
|
|
96
|
+
* @param {string} controllerUrl - Controller base URL
|
|
97
|
+
* @param {Object} authConfig - Auth config with token
|
|
98
|
+
* @param {Object} options - CLI options
|
|
99
|
+
* @param {string} [options.dataplane] - Optional Dataplane URL override
|
|
100
|
+
* @returns {Promise<string>} Dataplane base URL
|
|
101
|
+
* @throws {Error} When resolution fails (caller should exit)
|
|
102
|
+
*/
|
|
103
|
+
async function resolveCredentialListDataplaneUrl(controllerUrl, authConfig, options) {
|
|
104
|
+
if (options.dataplane) {
|
|
105
|
+
return options.dataplane.replace(/\/$/, '');
|
|
106
|
+
}
|
|
107
|
+
const environment = await resolveEnvironment();
|
|
108
|
+
return await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Call Dataplane credential API and display results; exits on failure
|
|
113
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
114
|
+
* @param {Object} authConfig - Auth config
|
|
115
|
+
* @param {Object} listOptions - pageSize, activeOnly
|
|
116
|
+
*/
|
|
117
|
+
async function fetchAndDisplayCredentials(dataplaneUrl, authConfig, listOptions) {
|
|
118
|
+
const response = await listCredentials(dataplaneUrl, authConfig, listOptions);
|
|
119
|
+
displayCredentialList(extractCredentials(response), dataplaneUrl);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Resolve Dataplane URL or log error and exit
|
|
124
|
+
* @async
|
|
125
|
+
* @param {string} controllerUrl - Controller URL
|
|
126
|
+
* @param {Object} authConfig - Auth config
|
|
127
|
+
* @param {Object} options - CLI options
|
|
128
|
+
* @returns {Promise<string>} Dataplane URL (never returns on failure; process.exit(1))
|
|
129
|
+
*/
|
|
130
|
+
async function resolveDataplaneUrlOrExit(controllerUrl, authConfig, options) {
|
|
131
|
+
try {
|
|
132
|
+
return await resolveCredentialListDataplaneUrl(controllerUrl, authConfig, options);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
logger.error(chalk.red(`❌ Could not resolve Dataplane URL: ${err.message}`));
|
|
135
|
+
logger.error(chalk.gray('Use --dataplane <url> to specify the Dataplane URL directly.'));
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run credential list command: call GET /api/v1/credential on Dataplane and display results
|
|
142
|
+
* @async
|
|
143
|
+
* @param {Object} options - CLI options
|
|
144
|
+
* @param {string} [options.controller] - Controller URL override
|
|
145
|
+
* @param {string} [options.dataplane] - Dataplane URL override (default: resolved from controller + environment)
|
|
146
|
+
* @param {boolean} [options.activeOnly] - List only active credentials
|
|
147
|
+
* @param {number} [options.pageSize] - Items per page
|
|
148
|
+
* @returns {Promise<void>}
|
|
149
|
+
*/
|
|
150
|
+
async function runCredentialList(options = {}) {
|
|
151
|
+
const { controllerUrl, authConfig } = await ensureControllerAndAuth(options);
|
|
152
|
+
const dataplaneUrl = await resolveDataplaneUrlOrExit(controllerUrl, authConfig, options);
|
|
91
153
|
const listOptions = {
|
|
92
154
|
pageSize: options.pageSize || DEFAULT_PAGE_SIZE,
|
|
93
155
|
activeOnly: options.activeOnly
|
|
94
156
|
};
|
|
95
157
|
try {
|
|
96
|
-
|
|
97
|
-
displayCredentialList(extractCredentials(response), authResult.controllerUrl);
|
|
158
|
+
await fetchAndDisplayCredentials(dataplaneUrl, authConfig, listOptions);
|
|
98
159
|
} catch (error) {
|
|
99
160
|
logger.error(chalk.red(`❌ Failed to list credentials: ${error.message}`));
|
|
100
161
|
process.exit(1);
|
|
@@ -16,18 +16,8 @@ const { listDatasources } = require('../datasource/list');
|
|
|
16
16
|
const { compareDatasources } = require('../datasource/diff');
|
|
17
17
|
const { deployDatasource } = require('../datasource/deploy');
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @param {Command} program - Commander program instance
|
|
22
|
-
*/
|
|
23
|
-
function setupDatasourceCommands(program) {
|
|
24
|
-
const datasource = program
|
|
25
|
-
.command('datasource')
|
|
26
|
-
.description('Manage external data sources');
|
|
27
|
-
|
|
28
|
-
// Validate command
|
|
29
|
-
datasource
|
|
30
|
-
.command('validate <file>')
|
|
19
|
+
function setupDatasourceValidateCommand(datasource) {
|
|
20
|
+
datasource.command('validate <file>')
|
|
31
21
|
.description('Validate external datasource JSON file')
|
|
32
22
|
.action(async(file) => {
|
|
33
23
|
try {
|
|
@@ -36,9 +26,7 @@ function setupDatasourceCommands(program) {
|
|
|
36
26
|
logger.log(chalk.green(`\n✓ Datasource file is valid: ${file}`));
|
|
37
27
|
} else {
|
|
38
28
|
logger.log(chalk.red(`\n✗ Datasource file has errors: ${file}`));
|
|
39
|
-
result.errors.forEach(error => {
|
|
40
|
-
logger.log(chalk.red(` • ${error}`));
|
|
41
|
-
});
|
|
29
|
+
result.errors.forEach(error => logger.log(chalk.red(` • ${error}`)));
|
|
42
30
|
process.exit(1);
|
|
43
31
|
}
|
|
44
32
|
} catch (error) {
|
|
@@ -46,10 +34,10 @@ function setupDatasourceCommands(program) {
|
|
|
46
34
|
process.exit(1);
|
|
47
35
|
}
|
|
48
36
|
});
|
|
37
|
+
}
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
datasource
|
|
52
|
-
.command('list')
|
|
39
|
+
function setupDatasourceListCommand(datasource) {
|
|
40
|
+
datasource.command('list')
|
|
53
41
|
.description('List datasources from environment (uses environment from config.yaml)')
|
|
54
42
|
.action(async() => {
|
|
55
43
|
try {
|
|
@@ -59,10 +47,10 @@ function setupDatasourceCommands(program) {
|
|
|
59
47
|
process.exit(1);
|
|
60
48
|
}
|
|
61
49
|
});
|
|
50
|
+
}
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
datasource
|
|
65
|
-
.command('diff <file1> <file2>')
|
|
52
|
+
function setupDatasourceDiffCommand(datasource) {
|
|
53
|
+
datasource.command('diff <file1> <file2>')
|
|
66
54
|
.description('Compare two datasource configuration files (for dataplane)')
|
|
67
55
|
.action(async(file1, file2) => {
|
|
68
56
|
try {
|
|
@@ -72,10 +60,10 @@ function setupDatasourceCommands(program) {
|
|
|
72
60
|
process.exit(1);
|
|
73
61
|
}
|
|
74
62
|
});
|
|
63
|
+
}
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
datasource
|
|
78
|
-
.command('deploy <myapp> <file>')
|
|
65
|
+
function setupDatasourceDeployCommand(datasource) {
|
|
66
|
+
datasource.command('deploy <myapp> <file>')
|
|
79
67
|
.description('Deploy datasource to dataplane')
|
|
80
68
|
.action(async(myapp, file, options) => {
|
|
81
69
|
try {
|
|
@@ -87,5 +75,17 @@ function setupDatasourceCommands(program) {
|
|
|
87
75
|
});
|
|
88
76
|
}
|
|
89
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Setup datasource management commands
|
|
80
|
+
* @param {Command} program - Commander program instance
|
|
81
|
+
*/
|
|
82
|
+
function setupDatasourceCommands(program) {
|
|
83
|
+
const datasource = program.command('datasource').description('Manage external data sources');
|
|
84
|
+
setupDatasourceValidateCommand(datasource);
|
|
85
|
+
setupDatasourceListCommand(datasource);
|
|
86
|
+
setupDatasourceDiffCommand(datasource);
|
|
87
|
+
setupDatasourceDeployCommand(datasource);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
90
|
module.exports = { setupDatasourceCommands };
|
|
91
91
|
|
|
@@ -36,13 +36,19 @@ async function getDeploymentListAuth(controllerUrl) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Extract deployments array from API response
|
|
40
|
-
*
|
|
39
|
+
* Extract deployments array from API response.
|
|
40
|
+
* Supports OpenAPI/SDK paginated format: { meta, data: Deployment[], links }
|
|
41
|
+
* and legacy shapes: { data: { items } }, { data: { deployments } }, or { data: [] }.
|
|
42
|
+
* @param {Object} response - API response (from ApiClient: { success, data: body, status })
|
|
41
43
|
* @returns {Array}
|
|
42
44
|
*/
|
|
43
45
|
function extractDeployments(response) {
|
|
44
|
-
const
|
|
45
|
-
const items =
|
|
46
|
+
const body = response?.data ?? response;
|
|
47
|
+
const items =
|
|
48
|
+
(Array.isArray(body?.data) ? body.data : undefined) ??
|
|
49
|
+
body?.items ??
|
|
50
|
+
body?.deployments ??
|
|
51
|
+
(Array.isArray(body) ? body : []);
|
|
46
52
|
return Array.isArray(items) ? items : [];
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -60,10 +66,11 @@ function displayDeploymentList(deployments, environment, controllerUrl) {
|
|
|
60
66
|
}
|
|
61
67
|
deployments.forEach((d) => {
|
|
62
68
|
const id = d.id ?? d.deploymentId ?? '-';
|
|
63
|
-
const
|
|
69
|
+
const target =
|
|
70
|
+
d.applicationKey ?? d.appKey ?? d.targetId ?? d.application?.key ?? '-';
|
|
64
71
|
const status = d.status ?? '-';
|
|
65
72
|
const createdAt = d.createdAt ?? d.created ?? '';
|
|
66
|
-
logger.log(` ${chalk.cyan(id)} ${
|
|
73
|
+
logger.log(` ${chalk.cyan(id)} ${target} ${status} ${chalk.gray(createdAt)}`);
|
|
67
74
|
});
|
|
68
75
|
logger.log('');
|
|
69
76
|
}
|