@aifabrix/builder 2.4.0 → 2.5.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.
- package/README.md +3 -0
- package/lib/app-down.js +123 -0
- package/lib/app.js +4 -2
- package/lib/build.js +19 -13
- package/lib/cli.js +52 -9
- package/lib/commands/secure.js +5 -40
- package/lib/config.js +26 -4
- package/lib/env-reader.js +3 -2
- package/lib/generator.js +0 -9
- package/lib/infra.js +30 -3
- package/lib/schema/application-schema.json +0 -15
- package/lib/schema/env-config.yaml +8 -8
- package/lib/secrets.js +167 -253
- package/lib/templates.js +10 -18
- package/lib/utils/api-error-handler.js +182 -147
- package/lib/utils/api.js +144 -354
- package/lib/utils/build-copy.js +6 -13
- package/lib/utils/compose-generator.js +2 -1
- package/lib/utils/device-code.js +349 -0
- package/lib/utils/env-config-loader.js +102 -0
- package/lib/utils/env-copy.js +131 -0
- package/lib/utils/env-endpoints.js +209 -0
- package/lib/utils/env-map.js +116 -0
- package/lib/utils/env-ports.js +60 -0
- package/lib/utils/environment-checker.js +39 -6
- package/lib/utils/image-name.js +49 -0
- package/lib/utils/paths.js +22 -20
- package/lib/utils/secrets-generator.js +3 -3
- package/lib/utils/secrets-helpers.js +359 -0
- package/lib/utils/secrets-path.js +12 -36
- package/lib/utils/secrets-url.js +38 -0
- package/lib/utils/secrets-utils.js +1 -42
- package/lib/utils/variable-transformer.js +0 -9
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +4 -2
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/infra/compose.yaml +4 -0
- package/templates/infra/compose.yaml.hbs +9 -4
|
@@ -11,7 +11,49 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const chalk = require('chalk');
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Extracts missing permissions from error data
|
|
16
|
+
* @param {Object} errorData - Error response data
|
|
17
|
+
* @returns {Array<string>} Array of missing permissions
|
|
18
|
+
*/
|
|
19
|
+
function extractMissingPermissions(errorData) {
|
|
20
|
+
if (errorData.missingPermissions && Array.isArray(errorData.missingPermissions)) {
|
|
21
|
+
return errorData.missingPermissions;
|
|
22
|
+
}
|
|
23
|
+
if (errorData.missing && errorData.missing.permissions && Array.isArray(errorData.missing.permissions)) {
|
|
24
|
+
return errorData.missing.permissions;
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extracts required permissions from error data
|
|
30
|
+
* @param {Object} errorData - Error response data
|
|
31
|
+
* @returns {Array<string>} Array of required permissions
|
|
32
|
+
*/
|
|
33
|
+
function extractRequiredPermissions(errorData) {
|
|
34
|
+
if (errorData.requiredPermissions && Array.isArray(errorData.requiredPermissions)) {
|
|
35
|
+
return errorData.requiredPermissions;
|
|
36
|
+
}
|
|
37
|
+
if (errorData.required && errorData.required.permissions && Array.isArray(errorData.required.permissions)) {
|
|
38
|
+
return errorData.required.permissions;
|
|
39
|
+
}
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Adds permission list to lines array
|
|
44
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
45
|
+
* @param {Array<string>} perms - Permissions array
|
|
46
|
+
* @param {string} label - Label for the permissions list
|
|
47
|
+
*/
|
|
48
|
+
function addPermissionList(lines, perms, label) {
|
|
49
|
+
if (perms.length > 0) {
|
|
50
|
+
lines.push(chalk.yellow(`${label}:`));
|
|
51
|
+
perms.forEach(perm => {
|
|
52
|
+
lines.push(chalk.gray(` - ${perm}`));
|
|
53
|
+
});
|
|
54
|
+
lines.push('');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
15
57
|
/**
|
|
16
58
|
* Formats permission error with missing and required permissions
|
|
17
59
|
* @param {Object} errorData - Error response data
|
|
@@ -21,45 +63,17 @@ function formatPermissionError(errorData) {
|
|
|
21
63
|
const lines = [];
|
|
22
64
|
lines.push(chalk.red('❌ Permission Denied\n'));
|
|
23
65
|
|
|
24
|
-
// Handle detail message if present
|
|
25
66
|
if (errorData.detail) {
|
|
26
67
|
lines.push(chalk.yellow(errorData.detail));
|
|
27
68
|
lines.push('');
|
|
28
69
|
}
|
|
29
70
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (errorData.missingPermissions && Array.isArray(errorData.missingPermissions)) {
|
|
33
|
-
missingPerms = errorData.missingPermissions;
|
|
34
|
-
} else if (errorData.missing && errorData.missing.permissions && Array.isArray(errorData.missing.permissions)) {
|
|
35
|
-
missingPerms = errorData.missing.permissions;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (missingPerms.length > 0) {
|
|
39
|
-
lines.push(chalk.yellow('Missing permissions:'));
|
|
40
|
-
missingPerms.forEach(perm => {
|
|
41
|
-
lines.push(chalk.gray(` - ${perm}`));
|
|
42
|
-
});
|
|
43
|
-
lines.push('');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Extract required permissions (support both flat and nested structures)
|
|
47
|
-
let requiredPerms = [];
|
|
48
|
-
if (errorData.requiredPermissions && Array.isArray(errorData.requiredPermissions)) {
|
|
49
|
-
requiredPerms = errorData.requiredPermissions;
|
|
50
|
-
} else if (errorData.required && errorData.required.permissions && Array.isArray(errorData.required.permissions)) {
|
|
51
|
-
requiredPerms = errorData.required.permissions;
|
|
52
|
-
}
|
|
71
|
+
const missingPerms = extractMissingPermissions(errorData);
|
|
72
|
+
addPermissionList(lines, missingPerms, 'Missing permissions');
|
|
53
73
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
requiredPerms.forEach(perm => {
|
|
57
|
-
lines.push(chalk.gray(` - ${perm}`));
|
|
58
|
-
});
|
|
59
|
-
lines.push('');
|
|
60
|
-
}
|
|
74
|
+
const requiredPerms = extractRequiredPermissions(errorData);
|
|
75
|
+
addPermissionList(lines, requiredPerms, 'Required permissions');
|
|
61
76
|
|
|
62
|
-
// Use instance (from RFC 7807) or url field
|
|
63
77
|
const requestUrl = errorData.instance || errorData.url;
|
|
64
78
|
const method = errorData.method || 'POST';
|
|
65
79
|
if (requestUrl) {
|
|
@@ -72,7 +86,6 @@ function formatPermissionError(errorData) {
|
|
|
72
86
|
|
|
73
87
|
return lines.join('\n');
|
|
74
88
|
}
|
|
75
|
-
|
|
76
89
|
/**
|
|
77
90
|
* Formats validation error with field-level details
|
|
78
91
|
* Handles unified error API response format (RFC 7807 Problem Details)
|
|
@@ -124,7 +137,6 @@ function formatValidationError(errorData) {
|
|
|
124
137
|
|
|
125
138
|
return lines.join('\n');
|
|
126
139
|
}
|
|
127
|
-
|
|
128
140
|
/**
|
|
129
141
|
* Formats authentication error
|
|
130
142
|
* @param {Object} errorData - Error response data
|
|
@@ -148,7 +160,6 @@ function formatAuthenticationError(errorData) {
|
|
|
148
160
|
|
|
149
161
|
return lines.join('\n');
|
|
150
162
|
}
|
|
151
|
-
|
|
152
163
|
/**
|
|
153
164
|
* Formats network error
|
|
154
165
|
* @param {string} errorMessage - Error message
|
|
@@ -241,6 +252,34 @@ function formatConflictError(errorData) {
|
|
|
241
252
|
return lines.join('\n');
|
|
242
253
|
}
|
|
243
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Gets actionable guidance options based on error detail
|
|
257
|
+
* @param {string} detail - Error detail message
|
|
258
|
+
* @returns {Array<string>} Array of guidance options
|
|
259
|
+
*/
|
|
260
|
+
function getNotFoundGuidance(detail) {
|
|
261
|
+
const lowerDetail = detail.toLowerCase();
|
|
262
|
+
if (lowerDetail.includes('environment')) {
|
|
263
|
+
return [
|
|
264
|
+
' • Check the environment key spelling',
|
|
265
|
+
' • List available environments: aifabrix app list -e <environment>',
|
|
266
|
+
' • Verify you have access to this environment'
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
if (lowerDetail.includes('application')) {
|
|
270
|
+
return [
|
|
271
|
+
' • Check the application key spelling',
|
|
272
|
+
' • List applications: aifabrix app list -e <environment>',
|
|
273
|
+
' • Verify the application exists in this environment'
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
return [
|
|
277
|
+
' • Check the resource identifier',
|
|
278
|
+
' • Verify the resource exists',
|
|
279
|
+
' • Check your permissions'
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
|
|
244
283
|
/**
|
|
245
284
|
* Formats not found error (404)
|
|
246
285
|
* @param {Object} errorData - Error response data
|
|
@@ -250,31 +289,17 @@ function formatNotFoundError(errorData) {
|
|
|
250
289
|
const lines = [];
|
|
251
290
|
lines.push(chalk.red('❌ Not Found\n'));
|
|
252
291
|
|
|
253
|
-
// Extract detail from RFC 7807 Problem Details format
|
|
254
292
|
const detail = errorData.detail || errorData.message || '';
|
|
255
|
-
|
|
256
293
|
if (detail) {
|
|
257
294
|
lines.push(chalk.yellow(detail));
|
|
258
295
|
lines.push('');
|
|
259
296
|
}
|
|
260
297
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
lines.push(chalk.gray(
|
|
265
|
-
|
|
266
|
-
lines.push(chalk.gray(' • Verify you have access to this environment'));
|
|
267
|
-
} else if (detail.toLowerCase().includes('application')) {
|
|
268
|
-
lines.push(chalk.gray('Options:'));
|
|
269
|
-
lines.push(chalk.gray(' • Check the application key spelling'));
|
|
270
|
-
lines.push(chalk.gray(' • List applications: aifabrix app list -e <environment>'));
|
|
271
|
-
lines.push(chalk.gray(' • Verify the application exists in this environment'));
|
|
272
|
-
} else {
|
|
273
|
-
lines.push(chalk.gray('Options:'));
|
|
274
|
-
lines.push(chalk.gray(' • Check the resource identifier'));
|
|
275
|
-
lines.push(chalk.gray(' • Verify the resource exists'));
|
|
276
|
-
lines.push(chalk.gray(' • Check your permissions'));
|
|
277
|
-
}
|
|
298
|
+
lines.push(chalk.gray('Options:'));
|
|
299
|
+
const guidance = getNotFoundGuidance(detail);
|
|
300
|
+
guidance.forEach(option => {
|
|
301
|
+
lines.push(chalk.gray(option));
|
|
302
|
+
});
|
|
278
303
|
|
|
279
304
|
if (errorData.correlationId) {
|
|
280
305
|
lines.push('');
|
|
@@ -314,31 +339,109 @@ function formatGenericError(errorData, statusCode) {
|
|
|
314
339
|
}
|
|
315
340
|
|
|
316
341
|
/**
|
|
317
|
-
* Parses error response
|
|
342
|
+
* Parses error response into error data object
|
|
318
343
|
* @param {string|Object} errorResponse - Error response (string or parsed JSON)
|
|
319
|
-
* @
|
|
320
|
-
* @param {boolean} isNetworkError - Whether this is a network error
|
|
321
|
-
* @returns {Object} Parsed error object with type, message, and formatted output
|
|
344
|
+
* @returns {Object} Parsed error data object
|
|
322
345
|
*/
|
|
323
|
-
function
|
|
324
|
-
let errorData = {};
|
|
325
|
-
|
|
326
|
-
// Handle undefined or null error response
|
|
346
|
+
function parseErrorData(errorResponse) {
|
|
327
347
|
if (errorResponse === undefined || errorResponse === null) {
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
348
|
+
return { message: 'Unknown error occurred' };
|
|
349
|
+
}
|
|
350
|
+
if (typeof errorResponse === 'string') {
|
|
331
351
|
try {
|
|
332
|
-
|
|
352
|
+
return JSON.parse(errorResponse);
|
|
333
353
|
} catch {
|
|
334
|
-
|
|
354
|
+
return { message: errorResponse };
|
|
335
355
|
}
|
|
336
|
-
} else if (typeof errorResponse === 'object') {
|
|
337
|
-
errorData = errorResponse;
|
|
338
|
-
} else {
|
|
339
|
-
// Fallback for unexpected types
|
|
340
|
-
errorData = { message: String(errorResponse) };
|
|
341
356
|
}
|
|
357
|
+
if (typeof errorResponse === 'object') {
|
|
358
|
+
return errorResponse;
|
|
359
|
+
}
|
|
360
|
+
return { message: String(errorResponse) };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Creates error result object
|
|
365
|
+
* @param {string} type - Error type
|
|
366
|
+
* @param {string} message - Error message
|
|
367
|
+
* @param {string} formatted - Formatted error message
|
|
368
|
+
* @param {Object} data - Error data
|
|
369
|
+
* @returns {Object} Error result object
|
|
370
|
+
*/
|
|
371
|
+
function createErrorResult(type, message, formatted, data) {
|
|
372
|
+
return { type, message, formatted, data };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Gets error message from error data
|
|
377
|
+
* @param {Object} errorData - Error data object
|
|
378
|
+
* @param {string} defaultMessage - Default message if not found
|
|
379
|
+
* @returns {string} Error message
|
|
380
|
+
*/
|
|
381
|
+
function getErrorMessage(errorData, defaultMessage) {
|
|
382
|
+
return errorData.detail || errorData.title || errorData.message || defaultMessage;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Handles 400 validation error
|
|
387
|
+
* @param {Object} errorData - Error data object
|
|
388
|
+
* @returns {Object} Error result object
|
|
389
|
+
*/
|
|
390
|
+
function handleValidationError(errorData) {
|
|
391
|
+
const errorMessage = getErrorMessage(errorData, 'Validation error');
|
|
392
|
+
return createErrorResult('validation', errorMessage, formatValidationError(errorData), errorData);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Handles specific 4xx client error codes
|
|
397
|
+
* @param {number} statusCode - HTTP status code
|
|
398
|
+
* @param {Object} errorData - Error data object
|
|
399
|
+
* @returns {Object|null} Error result object or null if not handled
|
|
400
|
+
*/
|
|
401
|
+
function handleSpecificClientErrors(statusCode, errorData) {
|
|
402
|
+
switch (statusCode) {
|
|
403
|
+
case 403:
|
|
404
|
+
return createErrorResult('permission', 'Permission denied', formatPermissionError(errorData), errorData);
|
|
405
|
+
case 401:
|
|
406
|
+
return createErrorResult('authentication', 'Authentication failed', formatAuthenticationError(errorData), errorData);
|
|
407
|
+
case 400:
|
|
408
|
+
return handleValidationError(errorData);
|
|
409
|
+
case 404:
|
|
410
|
+
return createErrorResult('notfound', getErrorMessage(errorData, 'Not found'), formatNotFoundError(errorData), errorData);
|
|
411
|
+
case 409:
|
|
412
|
+
return createErrorResult('conflict', getErrorMessage(errorData, 'Conflict'), formatConflictError(errorData), errorData);
|
|
413
|
+
default:
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Handles HTTP status code errors
|
|
420
|
+
* @param {number} statusCode - HTTP status code
|
|
421
|
+
* @param {Object} errorData - Error data object
|
|
422
|
+
* @returns {Object|null} Error result object or null if not handled
|
|
423
|
+
*/
|
|
424
|
+
function handleStatusCodeError(statusCode, errorData) {
|
|
425
|
+
// Handle 4xx client errors
|
|
426
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
427
|
+
return handleSpecificClientErrors(statusCode, errorData);
|
|
428
|
+
}
|
|
429
|
+
// Handle 5xx server errors
|
|
430
|
+
if (statusCode >= 500) {
|
|
431
|
+
return createErrorResult('server', 'Server error', formatServerError(errorData), errorData);
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Parses error response and determines error type
|
|
438
|
+
* @param {string|Object} errorResponse - Error response (string or parsed JSON)
|
|
439
|
+
* @param {number} statusCode - HTTP status code
|
|
440
|
+
* @param {boolean} isNetworkError - Whether this is a network error
|
|
441
|
+
* @returns {Object} Parsed error object with type, message, and formatted output
|
|
442
|
+
*/
|
|
443
|
+
function parseErrorResponse(errorResponse, statusCode, isNetworkError) {
|
|
444
|
+
let errorData = parseErrorData(errorResponse);
|
|
342
445
|
|
|
343
446
|
// Handle nested response structure (some APIs wrap errors in data field)
|
|
344
447
|
if (errorData.data && typeof errorData.data === 'object') {
|
|
@@ -348,84 +451,17 @@ function parseErrorResponse(errorResponse, statusCode, isNetworkError) {
|
|
|
348
451
|
// Handle network errors
|
|
349
452
|
if (isNetworkError) {
|
|
350
453
|
const errorMessage = errorData.message || errorResponse || 'Network error';
|
|
351
|
-
return
|
|
352
|
-
type: 'network',
|
|
353
|
-
message: errorMessage,
|
|
354
|
-
formatted: formatNetworkError(errorMessage, errorData),
|
|
355
|
-
data: errorData
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Handle different HTTP status codes
|
|
360
|
-
if (statusCode === 403) {
|
|
361
|
-
// Permission error
|
|
362
|
-
return {
|
|
363
|
-
type: 'permission',
|
|
364
|
-
message: 'Permission denied',
|
|
365
|
-
formatted: formatPermissionError(errorData),
|
|
366
|
-
data: errorData
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (statusCode === 401) {
|
|
371
|
-
// Authentication error
|
|
372
|
-
return {
|
|
373
|
-
type: 'authentication',
|
|
374
|
-
message: 'Authentication failed',
|
|
375
|
-
formatted: formatAuthenticationError(errorData),
|
|
376
|
-
data: errorData
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (statusCode === 400) {
|
|
381
|
-
// Validation error
|
|
382
|
-
// Extract message from unified error format (RFC 7807)
|
|
383
|
-
const errorMessage = errorData.detail || errorData.title || errorData.message || 'Validation error';
|
|
384
|
-
return {
|
|
385
|
-
type: 'validation',
|
|
386
|
-
message: errorMessage,
|
|
387
|
-
formatted: formatValidationError(errorData),
|
|
388
|
-
data: errorData
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (statusCode === 404) {
|
|
393
|
-
// Not found error
|
|
394
|
-
return {
|
|
395
|
-
type: 'notfound',
|
|
396
|
-
message: errorData.detail || errorData.message || 'Not found',
|
|
397
|
-
formatted: formatNotFoundError(errorData),
|
|
398
|
-
data: errorData
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (statusCode === 409) {
|
|
403
|
-
// Conflict error
|
|
404
|
-
return {
|
|
405
|
-
type: 'conflict',
|
|
406
|
-
message: errorData.detail || errorData.message || 'Conflict',
|
|
407
|
-
formatted: formatConflictError(errorData),
|
|
408
|
-
data: errorData
|
|
409
|
-
};
|
|
454
|
+
return createErrorResult('network', errorMessage, formatNetworkError(errorMessage, errorData), errorData);
|
|
410
455
|
}
|
|
411
456
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
message: 'Server error',
|
|
417
|
-
formatted: formatServerError(errorData),
|
|
418
|
-
data: errorData
|
|
419
|
-
};
|
|
457
|
+
// Handle HTTP status codes
|
|
458
|
+
const statusError = handleStatusCodeError(statusCode, errorData);
|
|
459
|
+
if (statusError) {
|
|
460
|
+
return statusError;
|
|
420
461
|
}
|
|
421
462
|
|
|
422
463
|
// Generic error
|
|
423
|
-
return
|
|
424
|
-
type: 'generic',
|
|
425
|
-
message: errorData.message || errorData.error || 'Unknown error',
|
|
426
|
-
formatted: formatGenericError(errorData, statusCode),
|
|
427
|
-
data: errorData
|
|
428
|
-
};
|
|
464
|
+
return createErrorResult('generic', errorData.message || errorData.error || 'Unknown error', formatGenericError(errorData, statusCode), errorData);
|
|
429
465
|
}
|
|
430
466
|
|
|
431
467
|
/**
|
|
@@ -462,4 +498,3 @@ module.exports = {
|
|
|
462
498
|
formatServerError,
|
|
463
499
|
formatGenericError
|
|
464
500
|
};
|
|
465
|
-
|