@aifabrix/builder 2.31.1 → 2.32.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/README.md +9 -9
- package/integration/hubspot/README.md +2 -2
- package/integration/hubspot/hubspot-deploy-company.json +17 -14
- package/integration/hubspot/hubspot-deploy-contact.json +19 -16
- package/integration/hubspot/hubspot-deploy-deal.json +21 -18
- package/lib/api/types/datasources.types.js +31 -5
- package/lib/api/types/wizard.types.js +142 -0
- package/lib/api/wizard.api.js +177 -0
- package/lib/{app-config.js → app/config.js} +4 -4
- package/lib/{app-deploy.js → app/deploy.js} +8 -8
- package/lib/app/display.js +90 -0
- package/lib/{app-dockerfile.js → app/dockerfile.js} +4 -4
- package/lib/{app-down.js → app/down.js} +4 -4
- package/lib/app/helpers.js +218 -0
- package/lib/app/index.js +298 -0
- package/lib/{app-list.js → app/list.js} +6 -6
- package/lib/{app-push.js → app/push.js} +4 -4
- package/lib/{app-readme.js → app/readme.js} +34 -13
- package/lib/{app-register.js → app/register.js} +9 -9
- package/lib/{app-rotate-secret.js → app/rotate-secret.js} +10 -10
- package/lib/{app-run-helpers.js → app/run-helpers.js} +10 -10
- package/lib/{app-run.js → app/run.js} +6 -6
- package/lib/{build.js → build/index.js} +59 -32
- package/lib/build/package.json +7 -0
- package/lib/cli.js +245 -179
- package/lib/commands/app.js +3 -3
- package/lib/commands/datasource.js +4 -4
- package/lib/commands/login-credentials.js +209 -0
- package/lib/commands/login-device.js +254 -0
- package/lib/commands/login.js +67 -378
- package/lib/commands/logout.js +1 -1
- package/lib/commands/secrets-set.js +1 -1
- package/lib/commands/secure.js +2 -2
- package/lib/commands/wizard.js +498 -0
- package/lib/{audit-logger.js → core/audit-logger.js} +1 -1
- package/lib/{config.js → core/config.js} +28 -26
- package/lib/{diff.js → core/diff.js} +157 -72
- package/lib/{secrets.js → core/secrets.js} +86 -49
- package/lib/{templates.js → core/templates-env.js} +14 -222
- package/lib/core/templates.js +279 -0
- package/lib/{datasource-deploy.js → datasource/deploy.js} +6 -6
- package/lib/{datasource-diff.js → datasource/diff.js} +2 -2
- package/lib/datasource/list.js +223 -0
- package/lib/{datasource-validate.js → datasource/validate.js} +2 -2
- package/lib/{deployer.js → deployment/deployer.js} +48 -18
- package/lib/{environment-deploy.js → deployment/environment.js} +163 -84
- package/lib/{push.js → deployment/push.js} +1 -1
- package/lib/external-system/deploy-helpers.js +145 -0
- package/lib/{external-system-deploy.js → external-system/deploy.js} +156 -111
- package/lib/external-system/download-helpers.js +114 -0
- package/lib/{external-system-download.js → external-system/download.js} +92 -135
- package/lib/{external-system-generator.js → external-system/generator.js} +15 -11
- package/lib/external-system/test-auth.js +40 -0
- package/lib/external-system/test-execution.js +84 -0
- package/lib/external-system/test-helpers.js +109 -0
- package/lib/{external-system-test.js → external-system/test.js} +174 -192
- package/lib/{generator-builders.js → generator/builders.js} +87 -10
- package/lib/{generator-external.js → generator/external.js} +115 -52
- package/lib/{github-generator.js → generator/github.js} +116 -15
- package/lib/{generator-helpers.js → generator/helpers.js} +92 -42
- package/lib/{generator.js → generator/index.js} +49 -22
- package/lib/{generator-split.js → generator/split.js} +108 -55
- package/lib/generator/wizard-prompts.js +357 -0
- package/lib/generator/wizard.js +490 -0
- package/lib/{infra.js → infrastructure/index.js} +49 -22
- package/lib/schema/external-datasource.schema.json +158 -136
- package/lib/schema/external-system.schema.json +43 -1
- package/lib/utils/api.js +9 -5
- package/lib/utils/app-register-api.js +60 -32
- package/lib/utils/app-register-auth.js +172 -47
- package/lib/utils/app-register-config.js +130 -59
- package/lib/utils/app-run-containers.js +29 -8
- package/lib/utils/build-helpers.js +1 -1
- package/lib/utils/cli-utils.js +78 -30
- package/lib/utils/compose-generator.js +145 -65
- package/lib/utils/config-paths.js +2 -0
- package/lib/utils/deployment-errors.js +1 -1
- package/lib/utils/device-code.js +99 -41
- package/lib/utils/env-config-loader.js +1 -1
- package/lib/utils/env-copy.js +21 -18
- package/lib/utils/env-endpoints.js +115 -67
- package/lib/utils/env-map.js +13 -14
- package/lib/utils/env-ports.js +45 -25
- package/lib/utils/env-template.js +84 -42
- package/lib/utils/error-formatter.js +26 -9
- package/lib/utils/error-formatters/error-parser.js +90 -4
- package/lib/utils/error-formatters/http-status-errors.js +54 -17
- package/lib/utils/error-formatters/network-errors.js +103 -26
- package/lib/utils/external-system-display.js +184 -90
- package/lib/utils/external-system-validators.js +164 -42
- package/lib/utils/file-upload.js +109 -0
- package/lib/utils/health-check.js +199 -83
- package/lib/utils/infra-containers.js +1 -1
- package/lib/utils/infra-status.js +66 -15
- package/lib/utils/local-secrets.js +45 -25
- package/lib/utils/paths.js +45 -33
- package/lib/utils/schema-loader.js +42 -25
- package/lib/utils/schema-resolver.js +123 -74
- package/lib/utils/secrets-encryption.js +62 -25
- package/lib/utils/secrets-helpers.js +126 -63
- package/lib/utils/secrets-path.js +1 -1
- package/lib/utils/secrets-url.js +1 -1
- package/lib/utils/token-manager-refresh.js +181 -0
- package/lib/utils/token-manager.js +76 -123
- package/lib/utils/variable-transformer.js +154 -77
- package/lib/utils/yaml-preserve.js +41 -47
- package/lib/{template-validator.js → validation/template.js} +54 -23
- package/lib/{validate.js → validation/validate.js} +205 -125
- package/lib/{validator.js → validation/validator.js} +58 -39
- package/package.json +31 -2
- package/templates/external-system/deploy.ps1.hbs +34 -0
- package/templates/external-system/deploy.sh.hbs +34 -0
- package/templates/external-system/external-datasource.json.hbs +31 -12
- package/lib/app.js +0 -467
- package/lib/datasource-list.js +0 -141
- /package/lib/{app-prompts.js → app/prompts.js} +0 -0
- /package/lib/{env-reader.js → core/env-reader.js} +0 -0
- /package/lib/{key-generator.js → core/key-generator.js} +0 -0
package/lib/commands/login.js
CHANGED
|
@@ -11,24 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
const inquirer = require('inquirer');
|
|
13
13
|
const chalk = require('chalk');
|
|
14
|
-
const
|
|
15
|
-
const { setCurrentEnvironment, saveDeviceToken, saveClientToken } = require('../config');
|
|
16
|
-
const { getToken, initiateDeviceCodeFlow } = require('../api/auth.api');
|
|
17
|
-
const { pollDeviceCodeToken, displayDeviceCodeInfo } = require('../utils/api');
|
|
18
|
-
const { formatApiError } = require('../utils/api-error-handler');
|
|
19
|
-
const { loadClientCredentials } = require('../utils/token-manager');
|
|
14
|
+
const { setCurrentEnvironment, saveClientToken } = require('../core/config');
|
|
20
15
|
const logger = require('../utils/logger');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* Validate environment key format
|
|
24
|
-
* @param {string} envKey - Environment key to validate
|
|
25
|
-
* @throws {Error} If environment key format is invalid
|
|
26
|
-
*/
|
|
27
|
-
function validateEnvironmentKey(envKey) {
|
|
28
|
-
if (!/^[a-z0-9-_]+$/i.test(envKey)) {
|
|
29
|
-
throw new Error('Environment key must contain only letters, numbers, hyphens, and underscores');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
16
|
+
const { handleCredentialsLogin } = require('./login-credentials');
|
|
17
|
+
const { handleDeviceCodeLogin } = require('./login-device');
|
|
32
18
|
|
|
33
19
|
/**
|
|
34
20
|
* Determine and validate authentication method
|
|
@@ -57,95 +43,6 @@ async function determineAuthMethod(method) {
|
|
|
57
43
|
return authMethod.method;
|
|
58
44
|
}
|
|
59
45
|
|
|
60
|
-
/**
|
|
61
|
-
* Prompt for credentials if not provided
|
|
62
|
-
* @async
|
|
63
|
-
* @param {string} [clientId] - Existing client ID
|
|
64
|
-
* @param {string} [clientSecret] - Existing client secret
|
|
65
|
-
* @returns {Promise<{clientId: string, clientSecret: string}>} Credentials
|
|
66
|
-
*/
|
|
67
|
-
async function promptForCredentials(clientId, clientSecret) {
|
|
68
|
-
if (clientId && clientSecret) {
|
|
69
|
-
return { clientId: clientId.trim(), clientSecret: clientSecret.trim() };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const credentials = await inquirer.prompt([
|
|
73
|
-
{
|
|
74
|
-
type: 'input',
|
|
75
|
-
name: 'clientId',
|
|
76
|
-
message: 'Client ID:',
|
|
77
|
-
default: clientId || '',
|
|
78
|
-
validate: (input) => {
|
|
79
|
-
const value = input.trim();
|
|
80
|
-
if (!value || value.length === 0) {
|
|
81
|
-
return 'Client ID is required';
|
|
82
|
-
}
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
type: 'password',
|
|
88
|
-
name: 'clientSecret',
|
|
89
|
-
message: 'Client Secret:',
|
|
90
|
-
default: clientSecret || '',
|
|
91
|
-
mask: '*',
|
|
92
|
-
validate: (input) => {
|
|
93
|
-
const value = input.trim();
|
|
94
|
-
if (!value || value.length === 0) {
|
|
95
|
-
return 'Client Secret is required';
|
|
96
|
-
}
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
]);
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
clientId: credentials.clientId.trim(),
|
|
104
|
-
clientSecret: credentials.clientSecret.trim()
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Get and validate environment key
|
|
110
|
-
* @async
|
|
111
|
-
* @param {string} [environment] - Environment key from options
|
|
112
|
-
* @returns {Promise<string>} Validated environment key
|
|
113
|
-
*/
|
|
114
|
-
async function getEnvironmentKey(environment) {
|
|
115
|
-
if (environment) {
|
|
116
|
-
const envKey = environment.trim();
|
|
117
|
-
validateEnvironmentKey(envKey);
|
|
118
|
-
return envKey;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const envPrompt = await inquirer.prompt([{
|
|
122
|
-
type: 'input',
|
|
123
|
-
name: 'environment',
|
|
124
|
-
message: 'Environment key (e.g., miso, dev, tst, pro):',
|
|
125
|
-
validate: (input) => {
|
|
126
|
-
if (!input || input.trim().length === 0) {
|
|
127
|
-
return 'Environment key is required';
|
|
128
|
-
}
|
|
129
|
-
validateEnvironmentKey(input.trim());
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
}]);
|
|
133
|
-
|
|
134
|
-
return envPrompt.environment.trim();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Save device token configuration (root level, controller-specific)
|
|
139
|
-
* @async
|
|
140
|
-
* @param {string} controllerUrl - Controller URL (used as key)
|
|
141
|
-
* @param {string} token - Authentication token
|
|
142
|
-
* @param {string} refreshToken - Refresh token for token renewal
|
|
143
|
-
* @param {string} expiresAt - Token expiration time
|
|
144
|
-
*/
|
|
145
|
-
async function saveDeviceLoginConfig(controllerUrl, token, refreshToken, expiresAt) {
|
|
146
|
-
await saveDeviceToken(controllerUrl, token, refreshToken, expiresAt);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
46
|
/**
|
|
150
47
|
* Save client credentials token configuration
|
|
151
48
|
* @async
|
|
@@ -160,314 +57,106 @@ async function saveCredentialsLoginConfig(controllerUrl, token, expiresAt, envir
|
|
|
160
57
|
}
|
|
161
58
|
|
|
162
59
|
/**
|
|
163
|
-
* Handle
|
|
164
|
-
* Uses OpenAPI /api/v1/auth/token endpoint with x-client-id and x-client-secret headers
|
|
165
|
-
* Reads credentials from secrets.local.yaml using pattern <app-name>-client-idKeyVault
|
|
60
|
+
* Handle login command action
|
|
166
61
|
* @async
|
|
167
|
-
* @
|
|
168
|
-
* @param {
|
|
169
|
-
* @param {string} [
|
|
170
|
-
* @param {string} [
|
|
171
|
-
* @
|
|
62
|
+
* @function handleLogin
|
|
63
|
+
* @param {Object} options - Login options
|
|
64
|
+
* @param {string} [options.controller] - Controller URL (default: 'http://localhost:3000')
|
|
65
|
+
* @param {string} [options.method] - Authentication method ('device' or 'credentials')
|
|
66
|
+
* @param {string} [options.app] - Application name (for credentials method, reads from secrets.local.yaml)
|
|
67
|
+
* @param {string} [options.clientId] - Client ID (for credentials method, overrides secrets.local.yaml)
|
|
68
|
+
* @param {string} [options.clientSecret] - Client Secret (for credentials method, overrides secrets.local.yaml)
|
|
69
|
+
* @param {string} [options.environment] - Environment key (updates root-level environment in config.yaml)
|
|
70
|
+
* @returns {Promise<void>} Resolves when login completes
|
|
71
|
+
* @throws {Error} If login fails
|
|
172
72
|
*/
|
|
173
|
-
async function handleCredentialsLogin(controllerUrl, appName, clientId, clientSecret) {
|
|
174
|
-
let credentials;
|
|
175
|
-
|
|
176
|
-
// Try to load from secrets.local.yaml if appName provided and credentials not provided
|
|
177
|
-
if (appName && !clientId && !clientSecret) {
|
|
178
|
-
credentials = await loadClientCredentials(appName);
|
|
179
|
-
if (!credentials) {
|
|
180
|
-
logger.log(chalk.yellow(`⚠️ Credentials not found in secrets.local.yaml for app '${appName}'`));
|
|
181
|
-
logger.log(chalk.gray(` Looking for: '${appName}-client-idKeyVault' and '${appName}-client-secretKeyVault'`));
|
|
182
|
-
logger.log(chalk.gray(' Prompting for credentials...\n'));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// If still no credentials, prompt for them
|
|
187
|
-
if (!credentials) {
|
|
188
|
-
credentials = await promptForCredentials(clientId, clientSecret);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Use centralized API client for token generation
|
|
192
|
-
const response = await getToken(credentials.clientId, credentials.clientSecret, controllerUrl);
|
|
193
|
-
|
|
194
|
-
if (!response.success) {
|
|
195
|
-
const formattedError = response.formattedError || formatApiError(response);
|
|
196
|
-
logger.error(formattedError);
|
|
197
|
-
|
|
198
|
-
// Provide additional context for login failures
|
|
199
|
-
if (response.status === 401) {
|
|
200
|
-
logger.log(chalk.gray('\n💡 Tip: Verify your client credentials are correct.'));
|
|
201
|
-
logger.log(chalk.gray(' Check secrets.local.yaml for:'));
|
|
202
|
-
logger.log(chalk.gray(` - ${appName}-client-idKeyVault`));
|
|
203
|
-
logger.log(chalk.gray(` - ${appName}-client-secretKeyVault`));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// OpenAPI spec response: { success: boolean, token: string, expiresIn: number, expiresAt: string, ... }
|
|
210
|
-
// Handle both flat and nested response structures (some APIs wrap in data field)
|
|
211
|
-
const apiResponse = response.data;
|
|
212
|
-
const responseData = apiResponse.data || apiResponse;
|
|
213
|
-
|
|
214
|
-
if (!responseData || !responseData.token) {
|
|
215
|
-
logger.error(chalk.red('❌ Invalid response: missing token'));
|
|
216
|
-
if (responseData) {
|
|
217
|
-
logger.error(chalk.gray(`Response structure: ${JSON.stringify(responseData, null, 2)}`));
|
|
218
|
-
}
|
|
219
|
-
process.exit(1);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Calculate expiration (use expiresAt if provided, otherwise calculate from expiresIn, default to 24 hours)
|
|
223
|
-
let expiresAt;
|
|
224
|
-
if (responseData.expiresAt) {
|
|
225
|
-
expiresAt = responseData.expiresAt;
|
|
226
|
-
} else if (responseData.expiresIn) {
|
|
227
|
-
expiresAt = new Date(Date.now() + responseData.expiresIn * 1000).toISOString();
|
|
228
|
-
} else {
|
|
229
|
-
expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
token: responseData.token,
|
|
234
|
-
expiresAt: expiresAt
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
73
|
/**
|
|
239
|
-
*
|
|
240
|
-
* @
|
|
241
|
-
* @param {
|
|
242
|
-
* @
|
|
243
|
-
* @param {number} interval - Polling interval
|
|
244
|
-
* @param {number} expiresIn - Expiration time
|
|
245
|
-
* @param {string} envKey - Environment key
|
|
246
|
-
* @returns {Promise<{token: string, environment: string}>} Token and environment
|
|
74
|
+
* Normalizes and logs controller URL
|
|
75
|
+
* @function normalizeControllerUrl
|
|
76
|
+
* @param {Object} options - Login options
|
|
77
|
+
* @returns {string} Normalized controller URL
|
|
247
78
|
*/
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}).start();
|
|
253
|
-
|
|
254
|
-
let pollCount = 0;
|
|
255
|
-
const pollCallback = () => {
|
|
256
|
-
pollCount++;
|
|
257
|
-
spinner.text = `Waiting for approval (attempt ${pollCount})...`;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
try {
|
|
261
|
-
const tokenResponse = await pollDeviceCodeToken(
|
|
262
|
-
controllerUrl,
|
|
263
|
-
deviceCode,
|
|
264
|
-
interval,
|
|
265
|
-
expiresIn,
|
|
266
|
-
pollCallback
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
spinner.succeed('Authentication approved!');
|
|
270
|
-
|
|
271
|
-
const token = tokenResponse.access_token;
|
|
272
|
-
const refreshToken = tokenResponse.refresh_token;
|
|
273
|
-
const expiresAt = new Date(Date.now() + (tokenResponse.expires_in * 1000)).toISOString();
|
|
274
|
-
|
|
275
|
-
// Save device token at root level (controller-specific, not environment-specific)
|
|
276
|
-
await saveDeviceLoginConfig(controllerUrl, token, refreshToken, expiresAt);
|
|
277
|
-
|
|
278
|
-
// Still set current environment if provided (for other purposes)
|
|
279
|
-
if (envKey) {
|
|
280
|
-
await setCurrentEnvironment(envKey);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
logger.log(chalk.green('\n✅ Successfully logged in!'));
|
|
284
|
-
logger.log(chalk.gray(`Controller: ${controllerUrl}`));
|
|
285
|
-
if (envKey) {
|
|
286
|
-
logger.log(chalk.gray(`Environment: ${envKey}`));
|
|
287
|
-
}
|
|
288
|
-
logger.log(chalk.gray('Token stored securely in ~/.aifabrix/config.yaml\n'));
|
|
289
|
-
|
|
290
|
-
return { token, environment: envKey };
|
|
291
|
-
|
|
292
|
-
} catch (pollError) {
|
|
293
|
-
spinner.fail('Authentication failed');
|
|
294
|
-
throw pollError;
|
|
295
|
-
}
|
|
79
|
+
function normalizeControllerUrl(options) {
|
|
80
|
+
const controllerUrl = (options.controller || options.url || 'http://localhost:3000').replace(/\/$/, '');
|
|
81
|
+
logger.log(chalk.gray(`Controller URL: ${controllerUrl}`));
|
|
82
|
+
return controllerUrl;
|
|
296
83
|
}
|
|
297
84
|
|
|
298
85
|
/**
|
|
299
|
-
*
|
|
300
|
-
* @
|
|
301
|
-
* @
|
|
302
|
-
* @
|
|
86
|
+
* Handles environment configuration
|
|
87
|
+
* @async
|
|
88
|
+
* @function handleEnvironmentConfig
|
|
89
|
+
* @param {Object} options - Login options
|
|
90
|
+
* @returns {Promise<string>} Environment key
|
|
303
91
|
*/
|
|
304
|
-
function
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return `${customScope} offline_access`;
|
|
311
|
-
}
|
|
312
|
-
return customScope;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Default scope with optional offline_access
|
|
316
|
-
if (offline) {
|
|
317
|
-
return `${defaultScope} offline_access`;
|
|
92
|
+
async function handleEnvironmentConfig(options) {
|
|
93
|
+
if (options.environment) {
|
|
94
|
+
const environment = options.environment.trim();
|
|
95
|
+
await setCurrentEnvironment(environment);
|
|
96
|
+
logger.log(chalk.gray(`Environment: ${environment}`));
|
|
97
|
+
return environment;
|
|
318
98
|
}
|
|
319
|
-
|
|
320
|
-
|
|
99
|
+
// Get current environment from config
|
|
100
|
+
const { getCurrentEnvironment } = require('../core/config');
|
|
101
|
+
return await getCurrentEnvironment();
|
|
321
102
|
}
|
|
322
103
|
|
|
323
104
|
/**
|
|
324
|
-
*
|
|
325
|
-
* @
|
|
326
|
-
* @
|
|
105
|
+
* Validates scope options for credentials method
|
|
106
|
+
* @function validateScopeOptions
|
|
107
|
+
* @param {string} method - Authentication method
|
|
108
|
+
* @param {Object} options - Login options
|
|
327
109
|
*/
|
|
328
|
-
function
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (!deviceCodeApiResponse.success) {
|
|
334
|
-
const errorMessage = deviceCodeApiResponse.formattedError ||
|
|
335
|
-
deviceCodeApiResponse.error ||
|
|
336
|
-
'Device code flow initiation failed';
|
|
337
|
-
const error = new Error(errorMessage);
|
|
338
|
-
if (deviceCodeApiResponse.formattedError) {
|
|
339
|
-
error.formattedError = deviceCodeApiResponse.formattedError;
|
|
340
|
-
}
|
|
341
|
-
throw error;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (!deviceCodeApiResponse.data) {
|
|
345
|
-
throw new Error('Device code flow initiation returned no data');
|
|
110
|
+
function validateScopeOptions(method, options) {
|
|
111
|
+
if (method === 'credentials' && (options.offline || options.scope)) {
|
|
112
|
+
logger.log(chalk.yellow('⚠️ Warning: --offline and --scope options are only available for device flow'));
|
|
113
|
+
logger.log(chalk.gray(' These options will be ignored for credentials method\n'));
|
|
346
114
|
}
|
|
347
115
|
}
|
|
348
116
|
|
|
349
117
|
/**
|
|
350
|
-
*
|
|
351
|
-
* @param {Object} apiResponse - API response data
|
|
352
|
-
* @returns {Object} Device code response in snake_case format
|
|
353
|
-
*/
|
|
354
|
-
function convertDeviceCodeResponse(apiResponse) {
|
|
355
|
-
const deviceCodeData = apiResponse.data || apiResponse;
|
|
356
|
-
return {
|
|
357
|
-
device_code: deviceCodeData.deviceCode || deviceCodeData.device_code,
|
|
358
|
-
user_code: deviceCodeData.userCode || deviceCodeData.user_code,
|
|
359
|
-
verification_uri: deviceCodeData.verificationUri || deviceCodeData.verification_uri,
|
|
360
|
-
expires_in: deviceCodeData.expiresIn || deviceCodeData.expires_in || 600,
|
|
361
|
-
interval: deviceCodeData.interval || 5
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Handle device code flow login
|
|
118
|
+
* Handles credentials login flow
|
|
367
119
|
* @async
|
|
120
|
+
* @function handleCredentialsLoginFlow
|
|
368
121
|
* @param {string} controllerUrl - Controller URL
|
|
369
|
-
* @param {string}
|
|
370
|
-
* @param {
|
|
371
|
-
* @
|
|
372
|
-
* @returns {Promise<{token: string, environment: string}>} Token and environment
|
|
122
|
+
* @param {string} environment - Environment key
|
|
123
|
+
* @param {Object} options - Login options
|
|
124
|
+
* @returns {Promise<void>}
|
|
373
125
|
*/
|
|
374
|
-
async function
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
logger.log(chalk.blue('\n📱 Initiating device code flow...\n'));
|
|
379
|
-
if (offline) {
|
|
380
|
-
logger.log(chalk.gray(`Requesting offline token (scope: ${requestScope})\n`));
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
try {
|
|
384
|
-
// Use centralized API client for device code flow initiation
|
|
385
|
-
const deviceCodeApiResponse = await initiateDeviceCodeFlow(controllerUrl, envKey, requestScope);
|
|
386
|
-
|
|
387
|
-
// Validate response structure
|
|
388
|
-
validateDeviceCodeResponse(deviceCodeApiResponse);
|
|
389
|
-
|
|
390
|
-
// Convert API response to device code format
|
|
391
|
-
const apiResponse = deviceCodeApiResponse.data;
|
|
392
|
-
const deviceCodeResponse = convertDeviceCodeResponse(apiResponse);
|
|
393
|
-
|
|
394
|
-
displayDeviceCodeInfo(deviceCodeResponse.user_code, deviceCodeResponse.verification_uri, logger, chalk);
|
|
395
|
-
|
|
396
|
-
return await pollAndSaveDeviceCodeToken(
|
|
397
|
-
controllerUrl,
|
|
398
|
-
deviceCodeResponse.device_code,
|
|
399
|
-
deviceCodeResponse.interval,
|
|
400
|
-
deviceCodeResponse.expires_in,
|
|
401
|
-
envKey
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
} catch (deviceError) {
|
|
405
|
-
// Display formatted error if available (includes detailed validation info)
|
|
406
|
-
if (deviceError.formattedError) {
|
|
407
|
-
logger.error(chalk.red('\n❌ Device code flow failed:'));
|
|
408
|
-
logger.log(deviceError.formattedError);
|
|
409
|
-
} else {
|
|
410
|
-
logger.error(chalk.red(`\n❌ Device code flow failed: ${deviceError.message}`));
|
|
411
|
-
}
|
|
126
|
+
async function handleCredentialsLoginFlow(controllerUrl, environment, options) {
|
|
127
|
+
if (!options.app) {
|
|
128
|
+
logger.error(chalk.red('❌ --app is required for credentials login method'));
|
|
412
129
|
process.exit(1);
|
|
413
130
|
}
|
|
131
|
+
const loginResult = await handleCredentialsLogin(controllerUrl, options.app, options.clientId, options.clientSecret);
|
|
132
|
+
await saveCredentialsLoginConfig(controllerUrl, loginResult.token, loginResult.expiresAt, environment, options.app);
|
|
414
133
|
}
|
|
415
134
|
|
|
416
135
|
/**
|
|
417
|
-
*
|
|
136
|
+
* Handles device code login flow
|
|
418
137
|
* @async
|
|
419
|
-
* @function
|
|
138
|
+
* @function handleDeviceCodeLoginFlow
|
|
139
|
+
* @param {string} controllerUrl - Controller URL
|
|
420
140
|
* @param {Object} options - Login options
|
|
421
|
-
* @
|
|
422
|
-
* @param {string} [options.method] - Authentication method ('device' or 'credentials')
|
|
423
|
-
* @param {string} [options.app] - Application name (for credentials method, reads from secrets.local.yaml)
|
|
424
|
-
* @param {string} [options.clientId] - Client ID (for credentials method, overrides secrets.local.yaml)
|
|
425
|
-
* @param {string} [options.clientSecret] - Client Secret (for credentials method, overrides secrets.local.yaml)
|
|
426
|
-
* @param {string} [options.environment] - Environment key (updates root-level environment in config.yaml)
|
|
427
|
-
* @returns {Promise<void>} Resolves when login completes
|
|
428
|
-
* @throws {Error} If login fails
|
|
141
|
+
* @returns {Promise<{token: string, environment: string}>} Login result
|
|
429
142
|
*/
|
|
143
|
+
async function handleDeviceCodeLoginFlow(controllerUrl, options) {
|
|
144
|
+
return await handleDeviceCodeLogin(controllerUrl, options.environment, options.offline, options.scope);
|
|
145
|
+
}
|
|
146
|
+
|
|
430
147
|
async function handleLogin(options) {
|
|
431
148
|
logger.log(chalk.blue('\n🔐 Logging in to Miso Controller...\n'));
|
|
432
149
|
|
|
433
|
-
const controllerUrl = (options
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
// Update root-level environment if provided
|
|
437
|
-
let environment = null;
|
|
438
|
-
if (options.environment) {
|
|
439
|
-
environment = options.environment.trim();
|
|
440
|
-
await setCurrentEnvironment(environment);
|
|
441
|
-
logger.log(chalk.gray(`Environment: ${environment}`));
|
|
442
|
-
} else {
|
|
443
|
-
// Get current environment from config
|
|
444
|
-
const { getCurrentEnvironment } = require('../config');
|
|
445
|
-
environment = await getCurrentEnvironment();
|
|
446
|
-
}
|
|
447
|
-
|
|
150
|
+
const controllerUrl = normalizeControllerUrl(options);
|
|
151
|
+
const environment = await handleEnvironmentConfig(options);
|
|
448
152
|
const method = await determineAuthMethod(options.method);
|
|
449
|
-
let token;
|
|
450
|
-
let expiresAt;
|
|
451
153
|
|
|
452
|
-
|
|
453
|
-
if (method === 'credentials' && (options.offline || options.scope)) {
|
|
454
|
-
logger.log(chalk.yellow('⚠️ Warning: --offline and --scope options are only available for device flow'));
|
|
455
|
-
logger.log(chalk.gray(' These options will be ignored for credentials method\n'));
|
|
456
|
-
}
|
|
154
|
+
validateScopeOptions(method, options);
|
|
457
155
|
|
|
458
156
|
if (method === 'credentials') {
|
|
459
|
-
|
|
460
|
-
logger.error(chalk.red('❌ --app is required for credentials login method'));
|
|
461
|
-
process.exit(1);
|
|
462
|
-
}
|
|
463
|
-
const loginResult = await handleCredentialsLogin(controllerUrl, options.app, options.clientId, options.clientSecret);
|
|
464
|
-
token = loginResult.token;
|
|
465
|
-
expiresAt = loginResult.expiresAt;
|
|
466
|
-
await saveCredentialsLoginConfig(controllerUrl, token, expiresAt, environment, options.app);
|
|
157
|
+
await handleCredentialsLoginFlow(controllerUrl, environment, options);
|
|
467
158
|
} else if (method === 'device') {
|
|
468
|
-
|
|
469
|
-
token = result.token;
|
|
470
|
-
environment = result.environment;
|
|
159
|
+
await handleDeviceCodeLoginFlow(controllerUrl, options);
|
|
471
160
|
return; // Early return for device flow (already saved config)
|
|
472
161
|
}
|
|
473
162
|
|
package/lib/commands/logout.js
CHANGED
|
@@ -17,7 +17,7 @@ const {
|
|
|
17
17
|
clearAllClientTokens,
|
|
18
18
|
clearClientTokensForEnvironment,
|
|
19
19
|
normalizeControllerUrl
|
|
20
|
-
} = require('../config');
|
|
20
|
+
} = require('../core/config');
|
|
21
21
|
const logger = require('../utils/logger');
|
|
22
22
|
const os = require('os');
|
|
23
23
|
const path = require('path');
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const chalk = require('chalk');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
|
-
const { getAifabrixSecretsPath } = require('../config');
|
|
15
|
+
const { getAifabrixSecretsPath } = require('../core/config');
|
|
16
16
|
const { saveLocalSecret, saveSecret } = require('../utils/local-secrets');
|
|
17
17
|
const pathsUtil = require('../utils/paths');
|
|
18
18
|
|
package/lib/commands/secure.js
CHANGED
|
@@ -15,7 +15,7 @@ const yaml = require('js-yaml');
|
|
|
15
15
|
const inquirer = require('inquirer');
|
|
16
16
|
const chalk = require('chalk');
|
|
17
17
|
const logger = require('../utils/logger');
|
|
18
|
-
const { setSecretsEncryptionKey, getSecretsEncryptionKey } = require('../config');
|
|
18
|
+
const { setSecretsEncryptionKey, getSecretsEncryptionKey } = require('../core/config');
|
|
19
19
|
const { validateEncryptionKey } = require('../utils/secrets-encryption');
|
|
20
20
|
const { encryptYamlValues } = require('../utils/yaml-preserve');
|
|
21
21
|
const pathsUtil = require('../utils/paths');
|
|
@@ -39,7 +39,7 @@ async function findSecretsFiles() {
|
|
|
39
39
|
|
|
40
40
|
// Check config.yaml for aifabrix-secrets
|
|
41
41
|
try {
|
|
42
|
-
const { getAifabrixSecretsPath } = require('../config');
|
|
42
|
+
const { getAifabrixSecretsPath } = require('../core/config');
|
|
43
43
|
const generalSecretsPath = await getAifabrixSecretsPath();
|
|
44
44
|
if (generalSecretsPath) {
|
|
45
45
|
const resolvedPath = path.isAbsolute(generalSecretsPath)
|