@aifabrix/builder 2.32.2 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/project-rules.mdc +8 -0
- package/README.md +36 -8
- package/bin/aifabrix.js +6 -8
- package/integration/hubspot/README.md +8 -7
- package/integration/hubspot/companies.json +2048 -0
- package/integration/hubspot/create-hubspot.js +665 -0
- package/integration/hubspot/{hubspot-deploy-company.json → hubspot-datasource-company.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-contact.json → hubspot-datasource-contact.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-deal.json → hubspot-datasource-deal.json} +1 -1
- package/integration/hubspot/hubspot-deploy.json +832 -81
- package/integration/hubspot/hubspot-system.json +99 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-credential-real.yaml +20 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-env-vars.yaml +9 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-add-datasource.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-app-name.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-create.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-select.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-known-platform.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-app.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-mode.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-file.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-url.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-source.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-array-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +5 -0
- package/integration/hubspot/test-dataplane-down-helpers.js +246 -0
- package/integration/hubspot/test-dataplane-down-tests.js +419 -0
- package/integration/hubspot/test-dataplane-down.js +157 -0
- package/integration/hubspot/test.js +1517 -0
- package/integration/hubspot/variables.yaml +4 -4
- package/integration/hubspot/wizard-hubspot-e2e.yaml +16 -0
- package/integration/hubspot/wizard-hubspot-platform.yaml +8 -0
- package/lib/api/applications.api.js +1 -0
- package/lib/api/index.js +10 -5
- package/lib/api/types/wizard.types.js +176 -38
- package/lib/api/wizard.api.js +207 -38
- package/lib/app/deploy.js +116 -54
- package/lib/app/display.js +6 -5
- package/lib/app/dockerfile.js +2 -1
- package/lib/app/list.js +78 -37
- package/lib/app/prompts.js +9 -5
- package/lib/app/readme.js +41 -112
- package/lib/app/register.js +44 -9
- package/lib/app/rotate-secret.js +50 -32
- package/lib/cli.js +243 -65
- package/lib/commands/app.js +4 -9
- package/lib/commands/auth-config.js +125 -0
- package/lib/commands/auth-status.js +261 -0
- package/lib/commands/datasource.js +3 -6
- package/lib/commands/login-credentials.js +4 -4
- package/lib/commands/login-device.js +43 -29
- package/lib/commands/login.js +22 -13
- package/lib/commands/wizard-config-normalizer.js +92 -0
- package/lib/commands/wizard-core.js +515 -0
- package/lib/commands/wizard-dataplane.js +122 -0
- package/lib/commands/wizard-headless.js +115 -0
- package/lib/commands/wizard.js +129 -357
- package/lib/core/config.js +46 -0
- package/lib/core/secrets.js +3 -22
- package/lib/core/templates-env.js +1 -1
- package/lib/datasource/deploy.js +34 -23
- package/lib/datasource/list.js +8 -6
- package/lib/deployment/deployer.js +25 -0
- package/lib/deployment/environment.js +10 -13
- package/lib/external-system/delete.js +151 -0
- package/lib/external-system/deploy.js +54 -378
- package/lib/external-system/download-helpers.js +45 -65
- package/lib/external-system/download.js +34 -13
- package/lib/external-system/generator.js +11 -7
- package/lib/external-system/test-auth.js +5 -3
- package/lib/generator/builders.js +3 -1
- package/lib/generator/external-controller-manifest.js +157 -0
- package/lib/generator/external-schema-utils.js +236 -0
- package/lib/generator/external.js +55 -3
- package/lib/generator/index.js +22 -10
- package/lib/generator/wizard-prompts.js +33 -10
- package/lib/generator/wizard.js +69 -86
- package/lib/infrastructure/compose.js +100 -0
- package/lib/infrastructure/helpers.js +139 -0
- package/lib/infrastructure/index.js +52 -311
- package/lib/infrastructure/services.js +168 -0
- package/lib/schema/application-schema.json +24 -5
- package/lib/schema/external-datasource.schema.json +303 -17
- package/lib/schema/external-system.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +234 -0
- package/lib/utils/api.js +37 -42
- package/lib/utils/app-existence.js +42 -0
- package/lib/utils/app-register-config.js +7 -2
- package/lib/utils/app-register-display.js +2 -1
- package/lib/utils/auth-config-validator.js +92 -0
- package/lib/utils/cli-utils.js +3 -1
- package/lib/utils/command-header.js +43 -0
- package/lib/utils/compose-generator.js +113 -70
- package/lib/utils/controller-url.js +115 -0
- package/lib/utils/dataplane-health.js +115 -0
- package/lib/utils/dataplane-resolver.js +29 -0
- package/lib/utils/dev-config.js +6 -2
- package/lib/utils/env-copy.js +2 -1
- package/lib/utils/env-map.js +2 -1
- package/lib/utils/env-ports.js +2 -1
- package/lib/utils/env-template.js +1 -1
- package/lib/utils/error-formatter.js +149 -28
- package/lib/utils/external-readme.js +125 -0
- package/lib/utils/help-builder.js +190 -0
- package/lib/utils/infra-status.js +13 -3
- package/lib/utils/paths.js +17 -2
- package/lib/utils/port-resolver.js +111 -0
- package/lib/utils/secrets-helpers.js +3 -15
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/token-manager.js +69 -4
- package/lib/utils/variable-transformer.js +7 -2
- package/lib/validation/external-manifest-validator.js +202 -0
- package/lib/validation/validate-display.js +406 -0
- package/lib/validation/validate.js +159 -123
- package/lib/validation/validator.js +38 -4
- package/lib/validation/wizard-config-validator.js +267 -0
- package/package.json +4 -2
- package/templates/applications/README.md.hbs +19 -17
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +7 -7
- package/templates/external-system/README.md.hbs +99 -0
- package/templates/external-system/external-system.json.hbs +1 -1
- package/templates/infra/compose.yaml.hbs +35 -0
- package/templates/python/docker-compose.hbs +26 -0
- package/templates/typescript/docker-compose.hbs +26 -0
package/lib/cli.js
CHANGED
|
@@ -18,12 +18,17 @@ const validator = require('./validation/validator');
|
|
|
18
18
|
const config = require('./core/config');
|
|
19
19
|
const devConfig = require('./utils/dev-config');
|
|
20
20
|
const chalk = require('chalk');
|
|
21
|
+
const path = require('path');
|
|
21
22
|
const logger = require('./utils/logger');
|
|
22
23
|
const { validateCommand, handleCommandError } = require('./utils/cli-utils');
|
|
23
24
|
const { handleLogin } = require('./commands/login');
|
|
24
25
|
const { handleLogout } = require('./commands/logout');
|
|
26
|
+
const { handleAuthStatus } = require('./commands/auth-status');
|
|
25
27
|
const { handleSecure } = require('./commands/secure');
|
|
26
28
|
const { handleSecretsSet } = require('./commands/secrets-set');
|
|
29
|
+
const { handleAuthConfig } = require('./commands/auth-config');
|
|
30
|
+
const { setupAppCommands: setupAppManagementCommands } = require('./commands/app');
|
|
31
|
+
const { setupDatasourceCommands } = require('./commands/datasource');
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
34
|
* Sets up authentication commands
|
|
@@ -32,14 +37,14 @@ const { handleSecretsSet } = require('./commands/secrets-set');
|
|
|
32
37
|
function setupAuthCommands(program) {
|
|
33
38
|
program.command('login')
|
|
34
39
|
.description('Authenticate with Miso Controller')
|
|
35
|
-
.option('-c, --controller <url>', 'Controller URL
|
|
36
|
-
.option('-m, --method <method>', 'Authentication method (device|credentials)')
|
|
40
|
+
.option('-c, --controller <url>', 'Controller URL (default: from config or developer ID, e.g. http://localhost:3000)')
|
|
41
|
+
.option('-m, --method <method>', 'Authentication method (device|credentials)', 'device')
|
|
37
42
|
.option('-a, --app <app>', 'Application name (required for credentials method, reads from secrets.local.yaml)')
|
|
38
43
|
.option('--client-id <id>', 'Client ID (for credentials method, overrides secrets.local.yaml)')
|
|
39
44
|
.option('--client-secret <secret>', 'Client Secret (for credentials method, overrides secrets.local.yaml)')
|
|
40
45
|
.option('-e, --environment <env>', 'Environment key (updates root-level environment in config.yaml, e.g., miso, dev, tst, pro)')
|
|
41
|
-
.option('--
|
|
42
|
-
.option('--scope <scopes>', 'Custom OAuth2 scope string (device flow only, default: "openid profile email")')
|
|
46
|
+
.option('--online', 'Request online-only token (excludes offline_access scope, device flow only)')
|
|
47
|
+
.option('--scope <scopes>', 'Custom OAuth2 scope string (device flow only, default: "openid profile email offline_access")')
|
|
43
48
|
.action(async(options) => {
|
|
44
49
|
try {
|
|
45
50
|
await handleLogin(options);
|
|
@@ -62,6 +67,70 @@ function setupAuthCommands(program) {
|
|
|
62
67
|
process.exit(1);
|
|
63
68
|
}
|
|
64
69
|
});
|
|
70
|
+
|
|
71
|
+
const authStatusHandler = async(options) => {
|
|
72
|
+
try {
|
|
73
|
+
await handleAuthStatus(options);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
handleCommandError(error, 'auth status');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Use nested command pattern for multi-word commands (like environment deploy)
|
|
81
|
+
const auth = program
|
|
82
|
+
.command('auth')
|
|
83
|
+
.description('Authentication commands');
|
|
84
|
+
|
|
85
|
+
auth
|
|
86
|
+
.command('status')
|
|
87
|
+
.description('Display authentication status for current controller and environment')
|
|
88
|
+
.action(authStatusHandler);
|
|
89
|
+
|
|
90
|
+
auth
|
|
91
|
+
.command('config')
|
|
92
|
+
.description('Configure authentication settings (controller, environment)')
|
|
93
|
+
.option('--set-controller <url>', 'Set default controller URL')
|
|
94
|
+
.option('--set-environment <env>', 'Set default environment')
|
|
95
|
+
.action(async(options) => {
|
|
96
|
+
try {
|
|
97
|
+
await handleAuthConfig(options);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
handleCommandError(error, 'auth config');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Runs the up command: resolves developer ID, traefik, and starts infra.
|
|
107
|
+
* @param {Object} options - Commander options (developer, traefik)
|
|
108
|
+
* @returns {Promise<void>}
|
|
109
|
+
*/
|
|
110
|
+
async function runUpCommand(options) {
|
|
111
|
+
let developerId = null;
|
|
112
|
+
if (options.developer) {
|
|
113
|
+
const id = parseInt(options.developer, 10);
|
|
114
|
+
if (isNaN(id) || id < 0) {
|
|
115
|
+
throw new Error('Developer ID must be a non-negative number (0 = default infra, > 0 = developer-specific)');
|
|
116
|
+
}
|
|
117
|
+
await config.setDeveloperId(id);
|
|
118
|
+
process.env.AIFABRIX_DEVELOPERID = id.toString();
|
|
119
|
+
developerId = id;
|
|
120
|
+
logger.log(chalk.green(`✓ Developer ID set to ${id}`));
|
|
121
|
+
}
|
|
122
|
+
const cfg = await config.getConfig();
|
|
123
|
+
if (options.traefik === true) {
|
|
124
|
+
cfg.traefik = true;
|
|
125
|
+
await config.saveConfig(cfg);
|
|
126
|
+
logger.log(chalk.green('✓ Traefik enabled and saved to config'));
|
|
127
|
+
} else if (options.traefik === false) {
|
|
128
|
+
cfg.traefik = false;
|
|
129
|
+
await config.saveConfig(cfg);
|
|
130
|
+
logger.log(chalk.green('✓ Traefik disabled and saved to config'));
|
|
131
|
+
}
|
|
132
|
+
const useTraefik = options.traefik === true ? true : (options.traefik === false ? false : !!(cfg.traefik));
|
|
133
|
+
await infra.startInfra(developerId, { traefik: useTraefik });
|
|
65
134
|
}
|
|
66
135
|
|
|
67
136
|
/**
|
|
@@ -72,20 +141,11 @@ function setupInfraCommands(program) {
|
|
|
72
141
|
program.command('up')
|
|
73
142
|
.description('Start local infrastructure services (Postgres, Redis, pgAdmin, Redis Commander)')
|
|
74
143
|
.option('-d, --developer <id>', 'Set developer ID and start infrastructure')
|
|
144
|
+
.option('--traefik', 'Include Traefik reverse proxy and save to config')
|
|
145
|
+
.option('--no-traefik', 'Exclude Traefik and save to config')
|
|
75
146
|
.action(async(options) => {
|
|
76
147
|
try {
|
|
77
|
-
|
|
78
|
-
if (options.developer) {
|
|
79
|
-
const id = parseInt(options.developer, 10);
|
|
80
|
-
if (isNaN(id) || id < 0) {
|
|
81
|
-
throw new Error('Developer ID must be a non-negative number (0 = default infra, > 0 = developer-specific)');
|
|
82
|
-
}
|
|
83
|
-
await config.setDeveloperId(id);
|
|
84
|
-
process.env.AIFABRIX_DEVELOPERID = id.toString();
|
|
85
|
-
developerId = id;
|
|
86
|
-
logger.log(chalk.green(`✓ Developer ID set to ${id}`));
|
|
87
|
-
}
|
|
88
|
-
await infra.startInfra(developerId);
|
|
148
|
+
await runUpCommand(options);
|
|
89
149
|
} catch (error) {
|
|
90
150
|
handleCommandError(error, 'up');
|
|
91
151
|
process.exit(1);
|
|
@@ -203,6 +263,88 @@ function setupInfraCommands(program) {
|
|
|
203
263
|
});
|
|
204
264
|
}
|
|
205
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Normalize options for external system creation
|
|
268
|
+
* @function normalizeExternalOptions
|
|
269
|
+
* @param {Object} options - Raw CLI options
|
|
270
|
+
* @returns {Object} Normalized options
|
|
271
|
+
*/
|
|
272
|
+
function normalizeExternalOptions(options) {
|
|
273
|
+
const normalized = { ...options };
|
|
274
|
+
if (options.displayName) normalized.systemDisplayName = options.displayName;
|
|
275
|
+
if (options.description) normalized.systemDescription = options.description;
|
|
276
|
+
if (options.systemType) normalized.systemType = options.systemType;
|
|
277
|
+
if (options.authType) normalized.authType = options.authType;
|
|
278
|
+
if (options.datasources !== undefined) {
|
|
279
|
+
const parsedCount = parseInt(options.datasources, 10);
|
|
280
|
+
if (Number.isNaN(parsedCount) || parsedCount < 1 || parsedCount > 10) {
|
|
281
|
+
throw new Error('Datasources count must be a number between 1 and 10');
|
|
282
|
+
}
|
|
283
|
+
normalized.datasourceCount = parsedCount;
|
|
284
|
+
}
|
|
285
|
+
if (options.controller) {
|
|
286
|
+
normalized.controller = true;
|
|
287
|
+
normalized.controllerUrl = options.controller;
|
|
288
|
+
}
|
|
289
|
+
return normalized;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Validate required options for non-interactive external creation
|
|
294
|
+
* @function validateNonInteractiveExternalOptions
|
|
295
|
+
* @param {Object} normalizedOptions - Normalized options
|
|
296
|
+
* @throws {Error} If required options are missing
|
|
297
|
+
*/
|
|
298
|
+
function validateNonInteractiveExternalOptions(normalizedOptions) {
|
|
299
|
+
const missing = [];
|
|
300
|
+
if (!normalizedOptions.systemDisplayName) missing.push('--display-name');
|
|
301
|
+
if (!normalizedOptions.systemDescription) missing.push('--description');
|
|
302
|
+
if (!normalizedOptions.systemType) missing.push('--system-type');
|
|
303
|
+
if (!normalizedOptions.authType) missing.push('--auth-type');
|
|
304
|
+
if (!normalizedOptions.datasourceCount) missing.push('--datasources');
|
|
305
|
+
if (missing.length > 0) {
|
|
306
|
+
throw new Error(`Missing required options for non-interactive external create: ${missing.join(', ')}`);
|
|
307
|
+
}
|
|
308
|
+
if (!Object.prototype.hasOwnProperty.call(normalizedOptions, 'github')) {
|
|
309
|
+
normalizedOptions.github = false;
|
|
310
|
+
}
|
|
311
|
+
if (!Object.prototype.hasOwnProperty.call(normalizedOptions, 'controller')) {
|
|
312
|
+
normalizedOptions.controller = false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Handle create command execution
|
|
318
|
+
* @async
|
|
319
|
+
* @function handleCreateCommand
|
|
320
|
+
* @param {string} appName - Application name
|
|
321
|
+
* @param {Object} options - CLI options
|
|
322
|
+
*/
|
|
323
|
+
async function handleCreateCommand(appName, options) {
|
|
324
|
+
const validTypes = ['webapp', 'api', 'service', 'functionapp', 'external'];
|
|
325
|
+
if (options.type && !validTypes.includes(options.type)) {
|
|
326
|
+
throw new Error(`Invalid type: ${options.type}. Must be one of: ${validTypes.join(', ')}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const wizardOptions = { app: appName, ...options };
|
|
330
|
+
const normalizedOptions = normalizeExternalOptions(options);
|
|
331
|
+
|
|
332
|
+
const isExternalType = options.type === 'external';
|
|
333
|
+
const isNonInteractive = process.stdin && process.stdin.isTTY === false;
|
|
334
|
+
|
|
335
|
+
if (isExternalType && !options.wizard && isNonInteractive) {
|
|
336
|
+
validateNonInteractiveExternalOptions(normalizedOptions);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const shouldUseWizard = options.wizard && (options.type === 'external' || (!options.type && validTypes.includes('external')));
|
|
340
|
+
if (shouldUseWizard) {
|
|
341
|
+
const { handleWizard } = require('./commands/wizard');
|
|
342
|
+
await handleWizard(wizardOptions);
|
|
343
|
+
} else {
|
|
344
|
+
await app.createApp(appName, normalizedOptions);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
206
348
|
/**
|
|
207
349
|
* Sets up application lifecycle commands
|
|
208
350
|
* @param {Command} program - Commander program instance
|
|
@@ -223,20 +365,14 @@ function setupAppCommands(program) {
|
|
|
223
365
|
.option('--github-steps <steps>', 'Extra GitHub workflow steps (comma-separated, e.g., npm,test)')
|
|
224
366
|
.option('--main-branch <branch>', 'Main branch name for workflows', 'main')
|
|
225
367
|
.option('--wizard', 'Use interactive wizard for external system creation')
|
|
368
|
+
.option('--display-name <name>', 'External system display name')
|
|
369
|
+
.option('--description <desc>', 'External system description')
|
|
370
|
+
.option('--system-type <type>', 'External system type (openapi, mcp, custom)')
|
|
371
|
+
.option('--auth-type <type>', 'External system auth type (oauth2, apikey, basic)')
|
|
372
|
+
.option('--datasources <count>', 'Number of datasources to create')
|
|
226
373
|
.action(async(appName, options) => {
|
|
227
374
|
try {
|
|
228
|
-
|
|
229
|
-
const validTypes = ['webapp', 'api', 'service', 'functionapp', 'external'];
|
|
230
|
-
if (options.type && !validTypes.includes(options.type)) {
|
|
231
|
-
throw new Error(`Invalid type: ${options.type}. Must be one of: ${validTypes.join(', ')}`);
|
|
232
|
-
}
|
|
233
|
-
// If wizard flag is set and type is external, use wizard instead
|
|
234
|
-
if (options.wizard && (options.type === 'external' || (!options.type && validTypes.includes('external')))) {
|
|
235
|
-
const { handleWizard } = require('./commands/wizard');
|
|
236
|
-
await handleWizard({ app: appName, ...options });
|
|
237
|
-
} else {
|
|
238
|
-
await app.createApp(appName, options);
|
|
239
|
-
}
|
|
375
|
+
await handleCreateCommand(appName, options);
|
|
240
376
|
} catch (error) {
|
|
241
377
|
handleCommandError(error, 'create');
|
|
242
378
|
process.exit(1);
|
|
@@ -244,11 +380,9 @@ function setupAppCommands(program) {
|
|
|
244
380
|
});
|
|
245
381
|
|
|
246
382
|
program.command('wizard')
|
|
247
|
-
.description('Interactive wizard for creating external systems')
|
|
383
|
+
.description('Interactive wizard for creating external systems (or headless with --config)')
|
|
248
384
|
.option('-a, --app <app>', 'Application name (if not provided, will prompt)')
|
|
249
|
-
.option('
|
|
250
|
-
.option('-e, --environment <env>', 'Environment (dev, tst, pro)', 'dev')
|
|
251
|
-
.option('--dataplane <url>', 'Dataplane URL (overrides controller lookup)')
|
|
385
|
+
.option('--config <file>', 'Path to wizard.yaml config file for headless mode')
|
|
252
386
|
.action(async(options) => {
|
|
253
387
|
try {
|
|
254
388
|
const { handleWizard } = require('./commands/wizard');
|
|
@@ -302,8 +436,6 @@ function setupAppCommands(program) {
|
|
|
302
436
|
|
|
303
437
|
program.command('deploy <app>')
|
|
304
438
|
.description('Deploy to Azure via Miso Controller')
|
|
305
|
-
.option('-c, --controller <url>', 'Controller URL')
|
|
306
|
-
.option('-e, --environment <env>', 'Environment (miso, dev, tst, pro)', 'dev')
|
|
307
439
|
.option('--client-id <id>', 'Client ID (overrides config)')
|
|
308
440
|
.option('--client-secret <secret>', 'Client Secret (overrides config)')
|
|
309
441
|
.option('--poll', 'Poll for deployment status', true)
|
|
@@ -355,7 +487,6 @@ function setupEnvironmentCommands(program) {
|
|
|
355
487
|
environment
|
|
356
488
|
.command('deploy <env>')
|
|
357
489
|
.description('Deploy/setup environment in Miso Controller')
|
|
358
|
-
.option('-c, --controller <url>', 'Controller URL (required)')
|
|
359
490
|
.option('--config <file>', 'Environment configuration file')
|
|
360
491
|
.option('--skip-validation', 'Skip environment validation')
|
|
361
492
|
.option('--poll', 'Poll for deployment status', true)
|
|
@@ -370,7 +501,6 @@ function setupEnvironmentCommands(program) {
|
|
|
370
501
|
env
|
|
371
502
|
.command('deploy <env>')
|
|
372
503
|
.description('Deploy/setup environment in Miso Controller')
|
|
373
|
-
.option('-c, --controller <url>', 'Controller URL (required)')
|
|
374
504
|
.option('--config <file>', 'Environment configuration file')
|
|
375
505
|
.option('--skip-validation', 'Skip environment validation')
|
|
376
506
|
.option('--poll', 'Poll for deployment status', true)
|
|
@@ -378,6 +508,52 @@ function setupEnvironmentCommands(program) {
|
|
|
378
508
|
.action(deployEnvHandler);
|
|
379
509
|
}
|
|
380
510
|
|
|
511
|
+
/**
|
|
512
|
+
* Handles split-json command logic
|
|
513
|
+
* @async
|
|
514
|
+
* @function handleSplitJsonCommand
|
|
515
|
+
* @param {string} appName - Application name
|
|
516
|
+
* @param {Object} options - Command options
|
|
517
|
+
* @returns {Promise<Object>} Paths to generated files
|
|
518
|
+
*/
|
|
519
|
+
async function handleSplitJsonCommand(appName, options) {
|
|
520
|
+
const fs = require('fs');
|
|
521
|
+
const { detectAppType, getDeployJsonPath } = require('./utils/paths');
|
|
522
|
+
const { appPath, appType } = await detectAppType(appName, options);
|
|
523
|
+
|
|
524
|
+
const outputDir = options.output || appPath;
|
|
525
|
+
if (appType === 'external') {
|
|
526
|
+
const schemaPath = path.join(appPath, 'application-schema.json');
|
|
527
|
+
if (!fs.existsSync(schemaPath)) {
|
|
528
|
+
throw new Error(`application-schema.json not found: ${schemaPath}`);
|
|
529
|
+
}
|
|
530
|
+
return generator.splitExternalApplicationSchema(schemaPath, outputDir);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const deployJsonPath = getDeployJsonPath(appName, appType, true);
|
|
534
|
+
if (!fs.existsSync(deployJsonPath)) {
|
|
535
|
+
throw new Error(`Deployment JSON file not found: ${deployJsonPath}`);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return generator.splitDeployJson(deployJsonPath, outputDir);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Logs split-json results
|
|
543
|
+
* @function logSplitJsonResult
|
|
544
|
+
* @param {Object} result - Generated file paths
|
|
545
|
+
* @returns {void}
|
|
546
|
+
*/
|
|
547
|
+
function logSplitJsonResult(result) {
|
|
548
|
+
logger.log(chalk.green('\n✓ Successfully split deployment JSON into component files:'));
|
|
549
|
+
logger.log(` • env.template: ${result.envTemplate}`);
|
|
550
|
+
logger.log(` • variables.yaml: ${result.variables}`);
|
|
551
|
+
if (result.rbac) {
|
|
552
|
+
logger.log(` • rbac.yml: ${result.rbac}`);
|
|
553
|
+
}
|
|
554
|
+
logger.log(` • README.md: ${result.readme}`);
|
|
555
|
+
}
|
|
556
|
+
|
|
381
557
|
/**
|
|
382
558
|
* Sets up utility commands
|
|
383
559
|
* @param {Command} program - Commander program instance
|
|
@@ -412,9 +588,10 @@ function setupUtilityCommands(program) {
|
|
|
412
588
|
|
|
413
589
|
program.command('json <app>')
|
|
414
590
|
.description('Generate deployment JSON (aifabrix-deploy.json for normal apps, application-schema.json for external systems)')
|
|
415
|
-
.
|
|
591
|
+
.option('--type <type>', 'Application type (external) - if set, only checks integration folder')
|
|
592
|
+
.action(async(appName, options) => {
|
|
416
593
|
try {
|
|
417
|
-
const result = await generator.generateDeployJsonWithValidation(appName);
|
|
594
|
+
const result = await generator.generateDeployJsonWithValidation(appName, options);
|
|
418
595
|
if (result.success) {
|
|
419
596
|
const fileName = result.path.includes('application-schema.json') ? 'application-schema.json' : 'deployment JSON';
|
|
420
597
|
logger.log(`✓ Generated ${fileName}: ${result.path}`);
|
|
@@ -439,27 +616,11 @@ function setupUtilityCommands(program) {
|
|
|
439
616
|
program.command('split-json <app>')
|
|
440
617
|
.description('Split deployment JSON into component files (env.template, variables.yaml, rbac.yml, README.md)')
|
|
441
618
|
.option('-o, --output <dir>', 'Output directory for component files (defaults to same directory as JSON)')
|
|
619
|
+
.option('--type <type>', 'Application type (external) - if set, only checks integration folder')
|
|
442
620
|
.action(async(appName, options) => {
|
|
443
621
|
try {
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
const { appPath, appType } = await detectAppType(appName);
|
|
447
|
-
const deployJsonPath = getDeployJsonPath(appName, appType, true);
|
|
448
|
-
|
|
449
|
-
if (!fs.existsSync(deployJsonPath)) {
|
|
450
|
-
throw new Error(`Deployment JSON file not found: ${deployJsonPath}`);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const outputDir = options.output || appPath;
|
|
454
|
-
const result = await generator.splitDeployJson(deployJsonPath, outputDir);
|
|
455
|
-
|
|
456
|
-
logger.log(chalk.green('\n✓ Successfully split deployment JSON into component files:'));
|
|
457
|
-
logger.log(` • env.template: ${result.envTemplate}`);
|
|
458
|
-
logger.log(` • variables.yaml: ${result.variables}`);
|
|
459
|
-
if (result.rbac) {
|
|
460
|
-
logger.log(` • rbac.yml: ${result.rbac}`);
|
|
461
|
-
}
|
|
462
|
-
logger.log(` • README.md: ${result.readme}`);
|
|
622
|
+
const result = await handleSplitJsonCommand(appName, options);
|
|
623
|
+
logSplitJsonResult(result);
|
|
463
624
|
} catch (error) {
|
|
464
625
|
handleCommandError(error, 'split-json');
|
|
465
626
|
process.exit(1);
|
|
@@ -496,10 +657,11 @@ function setupUtilityCommands(program) {
|
|
|
496
657
|
|
|
497
658
|
program.command('validate <appOrFile>')
|
|
498
659
|
.description('Validate application or external integration file')
|
|
499
|
-
.
|
|
660
|
+
.option('--type <type>', 'Application type (external) - if set, only checks integration folder')
|
|
661
|
+
.action(async(appOrFile, options) => {
|
|
500
662
|
try {
|
|
501
663
|
const validate = require('./validation/validate');
|
|
502
|
-
const result = await validate.validateAppOrFile(appOrFile);
|
|
664
|
+
const result = await validate.validateAppOrFile(appOrFile, options);
|
|
503
665
|
validate.displayValidationResults(result);
|
|
504
666
|
if (!result.valid) {
|
|
505
667
|
process.exit(1);
|
|
@@ -659,8 +821,6 @@ function setupSecretsCommands(program) {
|
|
|
659
821
|
function setupExternalSystemCommands(program) {
|
|
660
822
|
program.command('download <system-key>')
|
|
661
823
|
.description('Download external system from dataplane to local development structure')
|
|
662
|
-
.option('-e, --environment <env>', 'Environment (dev, tst, pro)', 'dev')
|
|
663
|
-
.option('-c, --controller <url>', 'Controller URL')
|
|
664
824
|
.option('--dry-run', 'Show what would be downloaded without actually downloading')
|
|
665
825
|
.action(async(systemKey, options) => {
|
|
666
826
|
try {
|
|
@@ -672,6 +832,24 @@ function setupExternalSystemCommands(program) {
|
|
|
672
832
|
}
|
|
673
833
|
});
|
|
674
834
|
|
|
835
|
+
program.command('delete <system-key>')
|
|
836
|
+
.description('Delete external system from dataplane (also deletes all associated datasources)')
|
|
837
|
+
.option('--type <type>', 'Application type (external) - required for external systems')
|
|
838
|
+
.option('--yes', 'Skip confirmation prompt')
|
|
839
|
+
.option('--force', 'Skip confirmation prompt (alias for --yes)')
|
|
840
|
+
.action(async(systemKey, options) => {
|
|
841
|
+
try {
|
|
842
|
+
if (options.type !== 'external') {
|
|
843
|
+
throw new Error('Delete command for external systems requires --type external');
|
|
844
|
+
}
|
|
845
|
+
const externalDelete = require('./external-system/delete');
|
|
846
|
+
await externalDelete.deleteExternalSystem(systemKey, options);
|
|
847
|
+
} catch (error) {
|
|
848
|
+
handleCommandError(error, 'delete');
|
|
849
|
+
process.exit(1);
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
675
853
|
program.command('test <app>')
|
|
676
854
|
.description('Run unit tests for external system (local validation, no API calls)')
|
|
677
855
|
.option('-d, --datasource <key>', 'Test specific datasource only')
|
|
@@ -694,8 +872,6 @@ function setupExternalSystemCommands(program) {
|
|
|
694
872
|
.description('Run integration tests via dataplane pipeline API')
|
|
695
873
|
.option('-d, --datasource <key>', 'Test specific datasource only')
|
|
696
874
|
.option('-p, --payload <file>', 'Path to custom test payload file')
|
|
697
|
-
.option('-e, --environment <env>', 'Environment (dev, tst, pro)', 'dev')
|
|
698
|
-
.option('-c, --controller <url>', 'Controller URL')
|
|
699
875
|
.option('-v, --verbose', 'Show detailed test output')
|
|
700
876
|
.option('--timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
701
877
|
.action(async(appName, options) => {
|
|
@@ -718,14 +894,16 @@ function setupExternalSystemCommands(program) {
|
|
|
718
894
|
* @param {Command} program - Commander program instance
|
|
719
895
|
*/
|
|
720
896
|
function setupCommands(program) {
|
|
721
|
-
setupAuthCommands(program);
|
|
722
897
|
setupInfraCommands(program);
|
|
898
|
+
setupAuthCommands(program);
|
|
723
899
|
setupAppCommands(program);
|
|
724
900
|
setupEnvironmentCommands(program);
|
|
901
|
+
setupAppManagementCommands(program);
|
|
902
|
+
setupDatasourceCommands(program);
|
|
725
903
|
setupUtilityCommands(program);
|
|
904
|
+
setupExternalSystemCommands(program);
|
|
726
905
|
setupDevCommands(program);
|
|
727
906
|
setupSecretsCommands(program);
|
|
728
|
-
setupExternalSystemCommands(program);
|
|
729
907
|
}
|
|
730
908
|
|
|
731
909
|
module.exports = {
|
package/lib/commands/app.js
CHANGED
|
@@ -24,13 +24,12 @@ function setupAppCommands(program) {
|
|
|
24
24
|
.command('app')
|
|
25
25
|
.description('Manage applications');
|
|
26
26
|
|
|
27
|
-
// Register command
|
|
27
|
+
// Register command (controller and environment from config.yaml)
|
|
28
28
|
app
|
|
29
29
|
.command('register <appKey>')
|
|
30
30
|
.description('Register application and get pipeline credentials')
|
|
31
|
-
.requiredOption('-e, --environment <env>', 'Environment ID or key')
|
|
32
|
-
.option('-c, --controller <url>', 'Controller URL (overrides variables.yaml)')
|
|
33
31
|
.option('-p, --port <port>', 'Application port (default: from variables.yaml)')
|
|
32
|
+
.option('-u, --url <url>', 'Application URL. If omitted: app.url, deployment.dataplaneUrl or deployment.appUrl in variables.yaml; else http://localhost:{build.localPort or port}')
|
|
34
33
|
.option('-n, --name <name>', 'Override display name')
|
|
35
34
|
.option('-d, --description <desc>', 'Override description')
|
|
36
35
|
.action(async(appKey, options) => {
|
|
@@ -42,12 +41,10 @@ function setupAppCommands(program) {
|
|
|
42
41
|
}
|
|
43
42
|
});
|
|
44
43
|
|
|
45
|
-
// List command
|
|
44
|
+
// List command (controller and environment from config.yaml)
|
|
46
45
|
app
|
|
47
46
|
.command('list')
|
|
48
47
|
.description('List applications')
|
|
49
|
-
.requiredOption('-e, --environment <env>', 'Environment ID or key')
|
|
50
|
-
.option('-c, --controller <url>', 'Controller URL (optional, uses configured controller if not provided)')
|
|
51
48
|
.action(async(options) => {
|
|
52
49
|
try {
|
|
53
50
|
await listApplications(options);
|
|
@@ -57,12 +54,10 @@ function setupAppCommands(program) {
|
|
|
57
54
|
}
|
|
58
55
|
});
|
|
59
56
|
|
|
60
|
-
// Rotate secret command
|
|
57
|
+
// Rotate secret command (controller and environment from config.yaml)
|
|
61
58
|
app
|
|
62
59
|
.command('rotate-secret <appKey>')
|
|
63
60
|
.description('Rotate pipeline ClientSecret for an application')
|
|
64
|
-
.requiredOption('-e, --environment <env>', 'Environment ID or key')
|
|
65
|
-
.option('-c, --controller <url>', 'Controller URL (optional, uses configured controller if not provided)')
|
|
66
61
|
.action(async(appKey, options) => {
|
|
67
62
|
try {
|
|
68
63
|
await rotateSecret(appKey, options);
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Configuration Commands
|
|
3
|
+
*
|
|
4
|
+
* Handles setting controller, environment, and dataplane URLs in config.yaml
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview Authentication configuration commands
|
|
7
|
+
* @author AI Fabrix Team
|
|
8
|
+
* @version 2.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const {
|
|
13
|
+
setControllerUrl,
|
|
14
|
+
setCurrentEnvironment,
|
|
15
|
+
getControllerUrl
|
|
16
|
+
} = require('../core/config');
|
|
17
|
+
const {
|
|
18
|
+
validateControllerUrl,
|
|
19
|
+
validateEnvironment,
|
|
20
|
+
checkUserLoggedIn
|
|
21
|
+
} = require('../utils/auth-config-validator');
|
|
22
|
+
const logger = require('../utils/logger');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Handle set-controller command
|
|
26
|
+
* @async
|
|
27
|
+
* @function handleSetController
|
|
28
|
+
* @param {string} url - Controller URL to set
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
* @throws {Error} If validation fails
|
|
31
|
+
*/
|
|
32
|
+
async function handleSetController(url) {
|
|
33
|
+
try {
|
|
34
|
+
// Validate URL format
|
|
35
|
+
validateControllerUrl(url);
|
|
36
|
+
|
|
37
|
+
// Check if user is logged in to that controller
|
|
38
|
+
const isLoggedIn = await checkUserLoggedIn(url);
|
|
39
|
+
if (!isLoggedIn) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`You are not logged in to controller ${url}.\n` +
|
|
42
|
+
'Please run "aifabrix login" first to authenticate with this controller.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Save controller URL
|
|
47
|
+
await setControllerUrl(url);
|
|
48
|
+
|
|
49
|
+
logger.log(chalk.green(`✓ Controller URL set to: ${url}`));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
logger.error(chalk.red(`✗ Failed to set controller URL: ${error.message}`));
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Handle set-environment command
|
|
58
|
+
* @async
|
|
59
|
+
* @function handleSetEnvironment
|
|
60
|
+
* @param {string} environment - Environment key to set
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
* @throws {Error} If validation fails
|
|
63
|
+
*/
|
|
64
|
+
async function handleSetEnvironment(environment) {
|
|
65
|
+
try {
|
|
66
|
+
// Validate environment format
|
|
67
|
+
validateEnvironment(environment);
|
|
68
|
+
|
|
69
|
+
// Get current controller from config
|
|
70
|
+
const controllerUrl = await getControllerUrl();
|
|
71
|
+
if (!controllerUrl) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
'No controller URL found in config.\n' +
|
|
74
|
+
'Please run "aifabrix login" first to set the controller URL.'
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if user is logged in to that controller
|
|
79
|
+
const isLoggedIn = await checkUserLoggedIn(controllerUrl);
|
|
80
|
+
if (!isLoggedIn) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`You are not logged in to controller ${controllerUrl}.\n` +
|
|
83
|
+
'Please run "aifabrix login" first to authenticate with this controller.'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Save environment
|
|
88
|
+
await setCurrentEnvironment(environment);
|
|
89
|
+
|
|
90
|
+
logger.log(chalk.green(`✓ Environment set to: ${environment}`));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
logger.error(chalk.red(`✗ Failed to set environment: ${error.message}`));
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Handle auth config command
|
|
99
|
+
* @async
|
|
100
|
+
* @function handleAuthConfig
|
|
101
|
+
* @param {Object} options - Command options
|
|
102
|
+
* @param {string} [options.setController] - Controller URL to set
|
|
103
|
+
* @param {string} [options.setEnvironment] - Environment to set
|
|
104
|
+
* @returns {Promise<void>}
|
|
105
|
+
* @throws {Error} If command fails
|
|
106
|
+
*/
|
|
107
|
+
async function handleAuthConfig(options) {
|
|
108
|
+
if (!options.setController && !options.setEnvironment) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'No action specified. Use one of:\n' +
|
|
111
|
+
' --set-controller <url>\n' +
|
|
112
|
+
' --set-environment <env>'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (options.setController) {
|
|
116
|
+
await handleSetController(options.setController);
|
|
117
|
+
}
|
|
118
|
+
if (options.setEnvironment) {
|
|
119
|
+
await handleSetEnvironment(options.setEnvironment);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
handleAuthConfig
|
|
125
|
+
};
|