@aifabrix/builder 2.31.0 → 2.32.1

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.
Files changed (119) hide show
  1. package/README.md +9 -9
  2. package/integration/hubspot/README.md +2 -2
  3. package/integration/hubspot/hubspot-deploy-company.json +17 -14
  4. package/integration/hubspot/hubspot-deploy-contact.json +19 -16
  5. package/integration/hubspot/hubspot-deploy-deal.json +21 -18
  6. package/lib/api/types/datasources.types.js +31 -5
  7. package/lib/api/types/wizard.types.js +142 -0
  8. package/lib/api/wizard.api.js +177 -0
  9. package/lib/{app-config.js → app/config.js} +4 -4
  10. package/lib/{app-deploy.js → app/deploy.js} +8 -8
  11. package/lib/app/display.js +90 -0
  12. package/lib/{app-dockerfile.js → app/dockerfile.js} +4 -4
  13. package/lib/{app-down.js → app/down.js} +4 -4
  14. package/lib/app/helpers.js +218 -0
  15. package/lib/app/index.js +298 -0
  16. package/lib/{app-list.js → app/list.js} +6 -6
  17. package/lib/{app-push.js → app/push.js} +4 -4
  18. package/lib/{app-readme.js → app/readme.js} +34 -13
  19. package/lib/{app-register.js → app/register.js} +9 -9
  20. package/lib/{app-rotate-secret.js → app/rotate-secret.js} +123 -37
  21. package/lib/{app-run-helpers.js → app/run-helpers.js} +10 -10
  22. package/lib/{app-run.js → app/run.js} +6 -6
  23. package/lib/{build.js → build/index.js} +59 -32
  24. package/lib/build/package.json +7 -0
  25. package/lib/cli.js +245 -179
  26. package/lib/commands/app.js +3 -3
  27. package/lib/commands/datasource.js +4 -4
  28. package/lib/commands/login-credentials.js +209 -0
  29. package/lib/commands/login-device.js +254 -0
  30. package/lib/commands/login.js +67 -378
  31. package/lib/commands/logout.js +1 -1
  32. package/lib/commands/secrets-set.js +1 -1
  33. package/lib/commands/secure.js +2 -2
  34. package/lib/commands/wizard.js +498 -0
  35. package/lib/{audit-logger.js → core/audit-logger.js} +1 -1
  36. package/lib/{config.js → core/config.js} +28 -26
  37. package/lib/{diff.js → core/diff.js} +157 -72
  38. package/lib/{secrets.js → core/secrets.js} +86 -49
  39. package/lib/{templates.js → core/templates-env.js} +14 -222
  40. package/lib/core/templates.js +279 -0
  41. package/lib/{datasource-deploy.js → datasource/deploy.js} +6 -6
  42. package/lib/{datasource-diff.js → datasource/diff.js} +2 -2
  43. package/lib/datasource/list.js +223 -0
  44. package/lib/{datasource-validate.js → datasource/validate.js} +2 -2
  45. package/lib/{deployer.js → deployment/deployer.js} +48 -18
  46. package/lib/{environment-deploy.js → deployment/environment.js} +163 -84
  47. package/lib/{push.js → deployment/push.js} +1 -1
  48. package/lib/external-system/deploy-helpers.js +145 -0
  49. package/lib/{external-system-deploy.js → external-system/deploy.js} +156 -111
  50. package/lib/external-system/download-helpers.js +114 -0
  51. package/lib/{external-system-download.js → external-system/download.js} +92 -135
  52. package/lib/{external-system-generator.js → external-system/generator.js} +15 -11
  53. package/lib/external-system/test-auth.js +40 -0
  54. package/lib/external-system/test-execution.js +84 -0
  55. package/lib/external-system/test-helpers.js +109 -0
  56. package/lib/{external-system-test.js → external-system/test.js} +174 -192
  57. package/lib/{generator-builders.js → generator/builders.js} +87 -10
  58. package/lib/{generator-external.js → generator/external.js} +115 -52
  59. package/lib/{github-generator.js → generator/github.js} +116 -15
  60. package/lib/{generator-helpers.js → generator/helpers.js} +92 -42
  61. package/lib/{generator.js → generator/index.js} +49 -22
  62. package/lib/{generator-split.js → generator/split.js} +108 -55
  63. package/lib/generator/wizard-prompts.js +357 -0
  64. package/lib/generator/wizard.js +490 -0
  65. package/lib/{infra.js → infrastructure/index.js} +49 -22
  66. package/lib/schema/external-datasource.schema.json +145 -133
  67. package/lib/schema/external-system.schema.json +42 -0
  68. package/lib/utils/api.js +9 -5
  69. package/lib/utils/app-register-api.js +60 -32
  70. package/lib/utils/app-register-auth.js +172 -47
  71. package/lib/utils/app-register-config.js +130 -59
  72. package/lib/utils/app-run-containers.js +29 -8
  73. package/lib/utils/build-helpers.js +1 -1
  74. package/lib/utils/cli-utils.js +78 -30
  75. package/lib/utils/compose-generator.js +145 -65
  76. package/lib/utils/config-paths.js +2 -0
  77. package/lib/utils/deployment-errors.js +1 -1
  78. package/lib/utils/device-code.js +99 -41
  79. package/lib/utils/env-config-loader.js +1 -1
  80. package/lib/utils/env-copy.js +21 -18
  81. package/lib/utils/env-endpoints.js +115 -67
  82. package/lib/utils/env-map.js +13 -14
  83. package/lib/utils/env-ports.js +45 -25
  84. package/lib/utils/env-template.js +84 -42
  85. package/lib/utils/error-formatter.js +26 -9
  86. package/lib/utils/error-formatters/error-parser.js +90 -4
  87. package/lib/utils/error-formatters/http-status-errors.js +54 -17
  88. package/lib/utils/error-formatters/network-errors.js +103 -26
  89. package/lib/utils/external-system-display.js +184 -90
  90. package/lib/utils/external-system-validators.js +164 -42
  91. package/lib/utils/file-upload.js +109 -0
  92. package/lib/utils/health-check.js +199 -83
  93. package/lib/utils/infra-containers.js +1 -1
  94. package/lib/utils/infra-status.js +66 -15
  95. package/lib/utils/local-secrets.js +45 -25
  96. package/lib/utils/paths.js +45 -33
  97. package/lib/utils/schema-loader.js +42 -25
  98. package/lib/utils/schema-resolver.js +123 -74
  99. package/lib/utils/secrets-encryption.js +62 -25
  100. package/lib/utils/secrets-helpers.js +126 -63
  101. package/lib/utils/secrets-path.js +1 -1
  102. package/lib/utils/secrets-url.js +1 -1
  103. package/lib/utils/token-manager-refresh.js +181 -0
  104. package/lib/utils/token-manager.js +76 -123
  105. package/lib/utils/variable-transformer.js +154 -77
  106. package/lib/utils/yaml-preserve.js +41 -47
  107. package/lib/{template-validator.js → validation/template.js} +54 -23
  108. package/lib/{validate.js → validation/validate.js} +205 -125
  109. package/lib/{validator.js → validation/validator.js} +58 -39
  110. package/package.json +34 -3
  111. package/scripts/install-local.js +210 -0
  112. package/templates/external-system/deploy.ps1.hbs +34 -0
  113. package/templates/external-system/deploy.sh.hbs +34 -0
  114. package/templates/external-system/external-datasource.json.hbs +31 -12
  115. package/lib/app.js +0 -467
  116. package/lib/datasource-list.js +0 -141
  117. /package/lib/{app-prompts.js → app/prompts.js} +0 -0
  118. /package/lib/{env-reader.js → core/env-reader.js} +0 -0
  119. /package/lib/{key-generator.js → core/key-generator.js} +0 -0
package/lib/app.js DELETED
@@ -1,467 +0,0 @@
1
- /**
2
- * AI Fabrix Builder Application Management
3
- *
4
- * This module handles application building, running, and deployment.
5
- * Includes runtime detection, Dockerfile generation, and container management.
6
- *
7
- * @fileoverview Application build and run management for AI Fabrix Builder
8
- * @author AI Fabrix Team
9
- * @version 2.0.0
10
- */
11
-
12
- const fs = require('fs').promises;
13
- const path = require('path');
14
- const chalk = require('chalk');
15
- const { readExistingEnv } = require('./env-reader');
16
- const build = require('./build');
17
- const appRun = require('./app-run');
18
- const { validateTemplate, copyTemplateFiles, copyAppFiles } = require('./template-validator');
19
- const { promptForOptions } = require('./app-prompts');
20
- const { generateConfigFiles } = require('./app-config');
21
- const { validateAppName, pushApp } = require('./app-push');
22
- const { generateDockerfileForApp } = require('./app-dockerfile');
23
- const { loadTemplateVariables, updateTemplateVariables, mergeTemplateVariables } = require('./utils/template-helpers');
24
- const logger = require('./utils/logger');
25
- const auditLogger = require('./audit-logger');
26
- const { downApp } = require('./app-down');
27
- const { getAppPath } = require('./utils/paths');
28
-
29
- /**
30
- * Displays success message after app creation
31
- * @param {string} appName - Application name
32
- * @param {Object} config - Final configuration
33
- * @param {string} envConversionMessage - Environment conversion message
34
- */
35
- function displaySuccessMessage(appName, config, envConversionMessage, hasAppFiles = false, appPath = null) {
36
- logger.log(chalk.green('\n✓ Application created successfully!'));
37
- logger.log(chalk.blue(`\nApplication: ${appName}`));
38
-
39
- // Determine location based on app type
40
- const baseDir = config.type === 'external' ? 'integration' : 'builder';
41
- const location = appPath ? path.relative(process.cwd(), appPath) : `${baseDir}/${appName}/`;
42
- logger.log(chalk.blue(`Location: ${location}`));
43
-
44
- if (hasAppFiles) {
45
- logger.log(chalk.blue(`Application files: apps/${appName}/`));
46
- }
47
-
48
- if (config.type === 'external') {
49
- logger.log(chalk.blue('Type: External System'));
50
- logger.log(chalk.blue(`System Key: ${config.systemKey || appName}`));
51
- logger.log(chalk.green('\nNext steps:'));
52
- logger.log(chalk.white('1. Edit external system JSON files in ' + location));
53
- logger.log(chalk.white('2. Run: aifabrix app register ' + appName + ' --environment dev'));
54
- logger.log(chalk.white('3. Run: aifabrix build ' + appName + ' (deploys to dataplane)'));
55
- logger.log(chalk.white('4. Run: aifabrix deploy ' + appName + ' (publishes to dataplane)'));
56
- } else {
57
- logger.log(chalk.blue(`Language: ${config.language}`));
58
- logger.log(chalk.blue(`Port: ${config.port}`));
59
-
60
- if (config.database) logger.log(chalk.yellow(' - Database enabled'));
61
- if (config.redis) logger.log(chalk.yellow(' - Redis enabled'));
62
- if (config.storage) logger.log(chalk.yellow(' - Storage enabled'));
63
- if (config.authentication) logger.log(chalk.yellow(' - Authentication enabled'));
64
-
65
- logger.log(chalk.gray(envConversionMessage));
66
-
67
- logger.log(chalk.green('\nNext steps:'));
68
- logger.log(chalk.white('1. Copy env.template to .env and fill in your values'));
69
- logger.log(chalk.white('2. Run: aifabrix build ' + appName));
70
- logger.log(chalk.white('3. Run: aifabrix run ' + appName));
71
- }
72
- }
73
-
74
- /**
75
- * Validates that app directory doesn't already exist
76
- * @async
77
- * @param {string} appPath - Application directory path
78
- * @param {string} appName - Application name
79
- * @throws {Error} If directory already exists
80
- */
81
- async function validateAppDirectoryNotExists(appPath, appName, baseDir = 'builder') {
82
- try {
83
- await fs.access(appPath);
84
- throw new Error(`Application '${appName}' already exists in ${baseDir}/${appName}/`);
85
- } catch (error) {
86
- if (error.code !== 'ENOENT') {
87
- throw error;
88
- }
89
- }
90
- }
91
-
92
- /**
93
- * Gets the base directory path for an app based on its type
94
- * @param {string} appName - Application name
95
- * @param {string} appType - Application type ('external' or other)
96
- * @returns {string} Base directory path ('integration' or 'builder')
97
- */
98
- function getBaseDirForAppType(appType) {
99
- return appType === 'external' ? 'integration' : 'builder';
100
- }
101
-
102
- /**
103
- * Handles GitHub workflow generation if requested
104
- * @async
105
- * @param {Object} options - Creation options
106
- * @param {Object} config - Final configuration
107
- */
108
- async function handleGitHubWorkflows(options, config) {
109
- if (!options.github) {
110
- return;
111
- }
112
-
113
- const githubGen = require('./github-generator');
114
-
115
- // Parse github-steps if provided
116
- const githubSteps = options.githubSteps
117
- ? options.githubSteps.split(',').map(s => s.trim()).filter(s => s.length > 0)
118
- : [];
119
-
120
- const workflowFiles = await githubGen.generateGithubWorkflows(
121
- process.cwd(),
122
- config,
123
- {
124
- mainBranch: options.mainBranch || 'main',
125
- uploadCoverage: true,
126
- githubSteps: githubSteps
127
- }
128
- );
129
-
130
- logger.log(chalk.green('✓ Generated GitHub Actions workflows:'));
131
- workflowFiles.forEach(file => logger.log(chalk.gray(` - ${file}`)));
132
- }
133
-
134
- /**
135
- * Validates app creation prerequisites
136
- * @async
137
- * @function validateAppCreation
138
- * @param {string} appName - Application name
139
- * @param {Object} options - Creation options
140
- * @param {string} appPath - Application directory path
141
- * @throws {Error} If validation fails
142
- */
143
- async function validateAppCreation(appName, options, appPath, baseDir = 'builder') {
144
- validateAppName(appName);
145
- await validateAppDirectoryNotExists(appPath, appName, baseDir);
146
-
147
- if (!options.app) {
148
- return;
149
- }
150
-
151
- const appsPath = path.join(process.cwd(), 'apps', appName);
152
- try {
153
- await fs.access(appsPath);
154
- throw new Error(`Application '${appName}' already exists in apps/${appName}/`);
155
- } catch (error) {
156
- if (error.code !== 'ENOENT') {
157
- throw error;
158
- }
159
- }
160
- }
161
-
162
- /**
163
- * Processes template files if template is specified
164
- * @async
165
- * @function processTemplateFiles
166
- * @param {string} template - Template name
167
- * @param {string} appPath - Application directory path
168
- * @param {string} appName - Application name
169
- * @param {Object} options - Creation options
170
- * @param {Object} config - Final configuration
171
- * @throws {Error} If template processing fails
172
- */
173
- async function processTemplateFiles(template, appPath, appName, options, config) {
174
- if (!template) {
175
- return;
176
- }
177
-
178
- await validateTemplate(template);
179
- const copiedFiles = await copyTemplateFiles(template, appPath);
180
- logger.log(chalk.green(`✓ Copied ${copiedFiles.length} file(s) from template '${template}'`));
181
- await updateTemplateVariables(appPath, appName, options, config);
182
- }
183
-
184
- /**
185
- * Updates variables.yaml for --app flag
186
- * @async
187
- * @function updateVariablesForAppFlag
188
- * @param {string} appPath - Application directory path
189
- * @param {string} appName - Application name
190
- * @throws {Error} If update fails
191
- */
192
- async function updateVariablesForAppFlag(appPath, appName) {
193
- try {
194
- const yaml = require('js-yaml');
195
- const variablesPath = path.join(appPath, 'variables.yaml');
196
- const variablesContent = await fs.readFile(variablesPath, 'utf8');
197
- const variables = yaml.load(variablesContent);
198
-
199
- if (variables.build) {
200
- variables.build.context = '../..';
201
- variables.build.envOutputPath = `../../apps/${appName}/.env`;
202
- } else {
203
- variables.build = {
204
- context: '../..',
205
- envOutputPath: `../../apps/${appName}/.env`
206
- };
207
- }
208
-
209
- await fs.writeFile(variablesPath, yaml.dump(variables, { indent: 2, lineWidth: 120, noRefs: true }));
210
- } catch (error) {
211
- logger.warn(chalk.yellow(`⚠️ Warning: Could not update variables.yaml: ${error.message}`));
212
- }
213
- }
214
-
215
- /**
216
- * Gets language from config or variables.yaml
217
- * @async
218
- * @function getLanguageForAppFiles
219
- * @param {string} language - Language from config
220
- * @param {string} appPath - Application directory path
221
- * @returns {Promise<string>} Language to use
222
- * @throws {Error} If language cannot be determined
223
- */
224
- async function getLanguageForAppFiles(language, appPath) {
225
- if (language) {
226
- return language;
227
- }
228
-
229
- const yaml = require('js-yaml');
230
- const variablesPath = path.join(appPath, 'variables.yaml');
231
- const variablesContent = await fs.readFile(variablesPath, 'utf8');
232
- const variables = yaml.load(variablesContent);
233
- const languageFromYaml = variables?.build?.language;
234
-
235
- if (!languageFromYaml) {
236
- throw new Error('Language not specified and could not be determined from variables.yaml. Use --language flag or ensure variables.yaml contains build.language');
237
- }
238
-
239
- return languageFromYaml;
240
- }
241
-
242
- /**
243
- * Sets up apps directory and copies application files
244
- * @async
245
- * @function setupAppFiles
246
- * @param {string} appName - Application name
247
- * @param {string} appPath - Application directory path
248
- * @param {Object} config - Final configuration
249
- * @param {Object} options - Creation options
250
- * @throws {Error} If setup fails
251
- */
252
- async function setupAppFiles(appName, appPath, config, options) {
253
- const appsPath = path.join(process.cwd(), 'apps', appName);
254
- await fs.mkdir(appsPath, { recursive: true });
255
- await updateVariablesForAppFlag(appPath, appName);
256
-
257
- const language = await getLanguageForAppFiles(config.language || options.language, appPath);
258
- const copiedFiles = await copyAppFiles(language, appsPath);
259
- logger.log(chalk.green(`✓ Copied ${copiedFiles.length} application file(s) to apps/${appName}/`));
260
- }
261
-
262
- /**
263
- * Creates new application with scaffolded configuration files
264
- * Prompts for configuration options and generates builder/ folder structure
265
- *
266
- * @async
267
- * @function createApp
268
- * @param {string} appName - Name of the application to create
269
- * @param {Object} options - Creation options
270
- * @param {number} [options.port] - Application port
271
- * @param {boolean} [options.database] - Requires database
272
- * @param {boolean} [options.redis] - Requires Redis
273
- * @param {boolean} [options.storage] - Requires file storage
274
- * @param {boolean} [options.authentication] - Requires authentication/RBAC
275
- * @param {string} [options.language] - Runtime language (typescript/python)
276
- * @param {string} [options.template] - Template to use (e.g., controller, keycloak)
277
- * @returns {Promise<void>} Resolves when app is created
278
- * @throws {Error} If creation fails
279
- *
280
- * @example
281
- * await createApp('myapp', { port: 3000, database: true, language: 'typescript' });
282
- * // Creates builder/ with variables.yaml, env.template, rbac.yaml
283
- */
284
- async function createApp(appName, options = {}) {
285
- try {
286
- // Validate appName early
287
- if (!appName || typeof appName !== 'string') {
288
- throw new Error('Application name is required');
289
- }
290
-
291
- // Determine app type from options (will be confirmed during prompts)
292
- // For now, check if type is explicitly set in options
293
- const initialType = options.type || 'webapp';
294
- const baseDir = getBaseDirForAppType(initialType);
295
- const appPath = getAppPath(appName, initialType);
296
-
297
- await validateAppCreation(appName, options, appPath, baseDir);
298
-
299
- if (options.template) {
300
- await validateTemplate(options.template);
301
- }
302
-
303
- const templateVariables = await loadTemplateVariables(options.template);
304
- const mergedOptions = mergeTemplateVariables(options, templateVariables);
305
- const config = await promptForOptions(appName, mergedOptions);
306
-
307
- // Update appPath based on final config type (may have changed during prompts)
308
- const finalBaseDir = getBaseDirForAppType(config.type);
309
- const finalAppPath = getAppPath(appName, config.type);
310
-
311
- // If path changed, validate the new path
312
- if (finalAppPath !== appPath) {
313
- await validateAppDirectoryNotExists(finalAppPath, appName, finalBaseDir);
314
- }
315
-
316
- await fs.mkdir(finalAppPath, { recursive: true });
317
- await processTemplateFiles(options.template, finalAppPath, appName, options, config);
318
-
319
- const existingEnv = await readExistingEnv(process.cwd());
320
- const envConversionMessage = existingEnv
321
- ? '\n✓ Found existing .env file - sensitive values will be converted to kv:// references'
322
- : '';
323
-
324
- await generateConfigFiles(finalAppPath, appName, config, existingEnv);
325
-
326
- // Generate external system files if type is external
327
- if (config.type === 'external') {
328
- const externalGenerator = require('./external-system-generator');
329
- await externalGenerator.generateExternalSystemFiles(finalAppPath, appName, config);
330
- }
331
-
332
- if (options.app) {
333
- await setupAppFiles(appName, finalAppPath, config, options);
334
- }
335
-
336
- await handleGitHubWorkflows(options, config);
337
- displaySuccessMessage(appName, config, envConversionMessage, options.app, finalAppPath);
338
-
339
- // Log application creation for audit trail
340
- await auditLogger.logApplicationCreation(appName, {
341
- language: config.language,
342
- port: config.port,
343
- database: config.database,
344
- redis: config.redis,
345
- storage: config.storage,
346
- authentication: config.authentication,
347
- template: options.template,
348
- api: null // Local operation, no API involved
349
- });
350
- } catch (error) {
351
- throw new Error(`Failed to create application: ${error.message}`);
352
- }
353
- }
354
-
355
- /**
356
- * Builds a container image for the specified application
357
- * Auto-detects runtime and generates Dockerfile if needed
358
- *
359
- * @async
360
- * @function buildApp
361
- * @param {string} appName - Name of the application to build
362
- * @param {Object} options - Build options
363
- * @param {string} [options.language] - Override language detection
364
- * @param {boolean} [options.forceTemplate] - Force rebuild from template
365
- * @param {string} [options.tag] - Image tag (default: latest)
366
- * @returns {Promise<string>} Image tag that was built
367
- * @throws {Error} If build fails or app configuration is invalid
368
- *
369
- * @example
370
- * const imageTag = await buildApp('myapp', { language: 'typescript' });
371
- * // Returns: 'myapp:latest'
372
- */
373
- async function buildApp(appName, options = {}) {
374
- return build.buildApp(appName, options);
375
- }
376
-
377
- /**
378
- * Detects the runtime language of an application
379
- * Analyzes project files to determine TypeScript, Python, etc.
380
- *
381
- * @function detectLanguage
382
- * @param {string} appPath - Path to application directory
383
- * @returns {string} Detected language ('typescript', 'python', etc.)
384
- * @throws {Error} If language cannot be detected
385
- *
386
- * @example
387
- * const language = detectLanguage('./myapp');
388
- * // Returns: 'typescript'
389
- */
390
- function detectLanguage(appPath) {
391
- return build.detectLanguage(appPath);
392
- }
393
-
394
- /**
395
- * Generates a Dockerfile from template based on detected language
396
- * Uses Handlebars templates to create optimized Dockerfiles
397
- *
398
- * @async
399
- * @function generateDockerfile
400
- * @param {string} appPath - Path to application directory
401
- * @param {string} language - Target language ('typescript', 'python')
402
- * @param {Object} config - Application configuration from variables.yaml
403
- * @returns {Promise<string>} Path to generated Dockerfile
404
- * @throws {Error} If template generation fails
405
- *
406
- * @example
407
- * const dockerfilePath = await generateDockerfile('./myapp', 'typescript', config);
408
- * // Returns: './myapp/.aifabrix/Dockerfile.typescript'
409
- */
410
- async function generateDockerfile(appPath, language, config) {
411
- return build.generateDockerfile(appPath, language, config);
412
- }
413
-
414
- /**
415
- * Runs the application locally using Docker
416
- * Starts container with proper port mapping and environment
417
- *
418
- * @async
419
- * @function runApp
420
- * @param {string} appName - Name of the application to run
421
- * @param {Object} options - Run options
422
- * @param {number} [options.port] - Override local port
423
- * @param {boolean} [options.debug] - Enable debug output
424
- * @returns {Promise<void>} Resolves when app is running
425
- * @throws {Error} If run fails or app is not built
426
- *
427
- * @example
428
- * await runApp('myapp', { port: 3001 });
429
- * // Application is now running on localhost:3001
430
- */
431
- async function runApp(appName, options = {}) {
432
- return appRun.runApp(appName, options);
433
- }
434
-
435
- /**
436
- * Deploys application to controller
437
- * @async
438
- * @function deployApp
439
- * @param {string} appName - Name of the application
440
- * @param {Object} options - Deployment options
441
- * @returns {Promise<void>} Resolves when deployment is complete
442
- */
443
- async function deployApp(appName, options = {}) {
444
- const appDeploy = require('./app-deploy');
445
- return appDeploy.deployApp(appName, options);
446
- }
447
-
448
- module.exports = {
449
- createApp,
450
- buildApp,
451
- runApp,
452
- downApp,
453
- detectLanguage,
454
- generateDockerfile,
455
- generateDockerfileForApp,
456
- pushApp,
457
- deployApp,
458
- loadTemplateVariables,
459
- updateTemplateVariables,
460
- mergeTemplateVariables,
461
- checkImageExists: appRun.checkImageExists,
462
- checkContainerRunning: appRun.checkContainerRunning,
463
- stopAndRemoveContainer: appRun.stopAndRemoveContainer,
464
- checkPortAvailable: appRun.checkPortAvailable,
465
- generateDockerCompose: appRun.generateDockerCompose,
466
- waitForHealthCheck: appRun.waitForHealthCheck
467
- };
@@ -1,141 +0,0 @@
1
- /**
2
- * Datasource List Command
3
- *
4
- * Lists datasources from an environment via controller API.
5
- *
6
- * @fileoverview Datasource listing for AI Fabrix Builder
7
- * @author AI Fabrix Team
8
- * @version 2.0.0
9
- */
10
-
11
- const chalk = require('chalk');
12
- const { getConfig } = require('./config');
13
- const { getOrRefreshDeviceToken } = require('./utils/token-manager');
14
- const { listEnvironmentDatasources } = require('./api/environments.api');
15
- const { formatApiError } = require('./utils/api-error-handler');
16
- const logger = require('./utils/logger');
17
-
18
- /**
19
- * Extracts datasources array from API response
20
- * Handles multiple response formats similar to applications list
21
- *
22
- * @function extractDatasources
23
- * @param {Object} response - API response from centralized API client
24
- * @returns {Array} Array of datasources
25
- * @throws {Error} If response format is invalid
26
- */
27
- function extractDatasources(response) {
28
- if (!response.success || !response.data) {
29
- throw new Error('Invalid API response: missing success or data');
30
- }
31
-
32
- const apiResponse = response.data;
33
- let datasources;
34
-
35
- // Check if apiResponse.data is an array (wrapped format)
36
- if (apiResponse && apiResponse.data && Array.isArray(apiResponse.data)) {
37
- datasources = apiResponse.data;
38
- } else if (Array.isArray(apiResponse)) {
39
- // Check if apiResponse is directly an array
40
- datasources = apiResponse;
41
- } else if (apiResponse && Array.isArray(apiResponse.items)) {
42
- // Check if apiResponse.items is an array (paginated format)
43
- datasources = apiResponse.items;
44
- } else if (apiResponse && apiResponse.data && apiResponse.data.items && Array.isArray(apiResponse.data.items)) {
45
- // Check if apiResponse.data.items is an array (wrapped paginated format)
46
- datasources = apiResponse.data.items;
47
- } else {
48
- logger.error(chalk.red('❌ Invalid response: expected data array or items array'));
49
- logger.error(chalk.gray('\nAPI response type:'), typeof apiResponse);
50
- logger.error(chalk.gray('API response:'), JSON.stringify(apiResponse, null, 2));
51
- throw new Error('Invalid API response format: expected array of datasources');
52
- }
53
-
54
- return datasources;
55
- }
56
-
57
- /**
58
- * Displays datasources in a formatted table
59
- *
60
- * @function displayDatasources
61
- * @param {Array} datasources - Array of datasource objects
62
- * @param {string} environment - Environment key
63
- */
64
- function displayDatasources(datasources, environment) {
65
- if (datasources.length === 0) {
66
- logger.log(chalk.yellow(`\nNo datasources found in environment: ${environment}`));
67
- return;
68
- }
69
-
70
- logger.log(chalk.blue(`\n📋 Datasources in environment: ${environment}\n`));
71
- logger.log(chalk.gray('Key'.padEnd(30) + 'Display Name'.padEnd(30) + 'System Key'.padEnd(20) + 'Version'.padEnd(15) + 'Status'));
72
- logger.log(chalk.gray('-'.repeat(120)));
73
-
74
- datasources.forEach((ds) => {
75
- const key = (ds.key || 'N/A').padEnd(30);
76
- const displayName = (ds.displayName || 'N/A').padEnd(30);
77
- const systemKey = (ds.systemKey || 'N/A').padEnd(20);
78
- const version = (ds.version || 'N/A').padEnd(15);
79
- const status = ds.enabled !== false ? chalk.green('enabled') : chalk.red('disabled');
80
- logger.log(`${key}${displayName}${systemKey}${version}${status}`);
81
- });
82
- logger.log('');
83
- }
84
-
85
- /**
86
- * Lists datasources from an environment
87
- *
88
- * @async
89
- * @function listDatasources
90
- * @param {Object} options - Command options
91
- * @param {string} options.environment - Environment ID or key
92
- * @throws {Error} If listing fails
93
- */
94
- async function listDatasources(options) {
95
- const config = await getConfig();
96
-
97
- // Try to get device token
98
- let controllerUrl = null;
99
- let token = null;
100
-
101
- if (config.device) {
102
- const deviceUrls = Object.keys(config.device);
103
- if (deviceUrls.length > 0) {
104
- controllerUrl = deviceUrls[0];
105
- const deviceToken = await getOrRefreshDeviceToken(controllerUrl);
106
- if (deviceToken && deviceToken.token) {
107
- token = deviceToken.token;
108
- controllerUrl = deviceToken.controller;
109
- }
110
- }
111
- }
112
-
113
- if (!token || !controllerUrl) {
114
- logger.error(chalk.red('❌ Not logged in. Run: aifabrix login'));
115
- logger.error(chalk.gray(' Use device code flow: aifabrix login --method device --controller <url>'));
116
- process.exit(1);
117
- }
118
-
119
- // Call controller API using centralized API client
120
- const authConfig = { type: 'bearer', token };
121
- const response = await listEnvironmentDatasources(controllerUrl, options.environment, authConfig);
122
-
123
- if (!response.success || !response.data) {
124
- const formattedError = response.formattedError || formatApiError(response);
125
- logger.error(formattedError);
126
- logger.error(chalk.gray('\nFull response for debugging:'));
127
- logger.error(chalk.gray(JSON.stringify(response, null, 2)));
128
- process.exit(1);
129
- return; // Ensure we don't continue after exit
130
- }
131
-
132
- const datasources = extractDatasources(response);
133
- displayDatasources(datasources, options.environment);
134
- }
135
-
136
- module.exports = {
137
- listDatasources,
138
- displayDatasources,
139
- extractDatasources
140
- };
141
-
File without changes
File without changes
File without changes