@orcapt/cli 1.0.3 → 1.0.4
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/bin/orca.js +3 -0
- package/package.json +1 -1
- package/src/commands/db.js +4 -29
- package/src/commands/lambda.js +105 -116
- package/src/commands/storage.js +9 -84
- package/src/utils/errorHandler.js +153 -0
package/bin/orca.js
CHANGED
|
@@ -335,6 +335,7 @@ lambdaCmd
|
|
|
335
335
|
.command('invoke <function-name>')
|
|
336
336
|
.description('Invoke Lambda function')
|
|
337
337
|
.option('--payload <json>', 'JSON payload')
|
|
338
|
+
.option('--path <path>', 'HTTP path to invoke (e.g., health, api/v1/users)')
|
|
338
339
|
.action((functionName, options) => {
|
|
339
340
|
requireAuth('lambda invoke');
|
|
340
341
|
lambdaInvoke(functionName, options);
|
|
@@ -345,6 +346,8 @@ lambdaCmd
|
|
|
345
346
|
.description('Get Lambda function logs')
|
|
346
347
|
.option('--tail', 'Stream logs in real-time')
|
|
347
348
|
.option('--since <time>', 'Show logs since (e.g., 1h, 30m)', '10m')
|
|
349
|
+
.option('--page <number>', 'Page number (default: 1)', '1')
|
|
350
|
+
.option('--per-page <number>', 'Number of logs per page (default: 100, max: 1000)', '100')
|
|
348
351
|
.action((functionName, options) => {
|
|
349
352
|
requireAuth('lambda logs');
|
|
350
353
|
lambdaLogs(functionName, options);
|
package/package.json
CHANGED
package/src/commands/db.js
CHANGED
|
@@ -9,6 +9,7 @@ const https = require('https');
|
|
|
9
9
|
const http = require('http');
|
|
10
10
|
const { getCredentials } = require('./login');
|
|
11
11
|
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
12
|
+
const { handleError } = require('../utils/errorHandler');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Make API request to orcapt Deploy API
|
|
@@ -109,15 +110,7 @@ async function dbCreate(options) {
|
|
|
109
110
|
|
|
110
111
|
} catch (error) {
|
|
111
112
|
spinner.fail(chalk.red('Failed to create database'));
|
|
112
|
-
|
|
113
|
-
if (error.statusCode === 401) {
|
|
114
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
115
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
116
|
-
} else if (error.response && error.response.detail) {
|
|
117
|
-
console.log(chalk.red(`\n✗ ${error.response.detail}\n`));
|
|
118
|
-
} else {
|
|
119
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
120
|
-
}
|
|
113
|
+
handleError(error, 'Database creation');
|
|
121
114
|
process.exit(1);
|
|
122
115
|
}
|
|
123
116
|
}
|
|
@@ -169,15 +162,7 @@ async function dbList() {
|
|
|
169
162
|
|
|
170
163
|
} catch (error) {
|
|
171
164
|
spinner.fail(chalk.red('Failed to list databases'));
|
|
172
|
-
|
|
173
|
-
if (error.statusCode === 401) {
|
|
174
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
175
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
176
|
-
} else if (error.response && error.response.detail) {
|
|
177
|
-
console.log(chalk.red(`\n✗ ${error.response.detail}\n`));
|
|
178
|
-
} else {
|
|
179
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
180
|
-
}
|
|
165
|
+
handleError(error, 'Database listing');
|
|
181
166
|
process.exit(1);
|
|
182
167
|
}
|
|
183
168
|
}
|
|
@@ -225,17 +210,7 @@ async function dbRemove(databaseName) {
|
|
|
225
210
|
|
|
226
211
|
} catch (error) {
|
|
227
212
|
spinner.fail(chalk.red('Failed to delete database'));
|
|
228
|
-
|
|
229
|
-
if (error.statusCode === 401) {
|
|
230
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
231
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
232
|
-
} else if (error.statusCode === 404) {
|
|
233
|
-
console.log(chalk.red(`\n✗ Database '${databaseName}' not found or doesn't belong to your workspace\n`));
|
|
234
|
-
} else if (error.response && error.response.detail) {
|
|
235
|
-
console.log(chalk.red(`\n✗ ${error.response.detail}\n`));
|
|
236
|
-
} else {
|
|
237
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
238
|
-
}
|
|
213
|
+
handleError(error, 'Database deletion');
|
|
239
214
|
process.exit(1);
|
|
240
215
|
}
|
|
241
216
|
}
|
package/src/commands/lambda.js
CHANGED
|
@@ -11,6 +11,7 @@ const fs = require('fs');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const { getCredentials } = require('./login');
|
|
13
13
|
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
14
|
+
const { handleError } = require('../utils/errorHandler');
|
|
14
15
|
const {
|
|
15
16
|
checkDockerInstalled,
|
|
16
17
|
checkDockerImage,
|
|
@@ -193,17 +194,7 @@ async function lambdaDeploy(functionName, options = {}) {
|
|
|
193
194
|
});
|
|
194
195
|
} catch (error) {
|
|
195
196
|
spinner.fail(chalk.red('✗ Failed to request ECR credentials'));
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (error.statusCode) {
|
|
199
|
-
console.log(chalk.red('Status Code:'), error.statusCode);
|
|
200
|
-
console.log(chalk.red('Response:'), JSON.stringify(error.response, null, 2));
|
|
201
|
-
} else if (error.message) {
|
|
202
|
-
console.log(chalk.red('Error:'), error.message);
|
|
203
|
-
} else {
|
|
204
|
-
console.log(chalk.red('Error:'), error);
|
|
205
|
-
}
|
|
206
|
-
|
|
197
|
+
handleError(error, 'ECR credentials request');
|
|
207
198
|
console.log();
|
|
208
199
|
console.log(chalk.yellow('💡 Troubleshooting tips:'));
|
|
209
200
|
console.log(chalk.white(' 1. Check if backend is running'));
|
|
@@ -289,22 +280,7 @@ async function lambdaDeploy(functionName, options = {}) {
|
|
|
289
280
|
|
|
290
281
|
} catch (error) {
|
|
291
282
|
console.log(chalk.red('\n✗ Deployment failed'));
|
|
292
|
-
|
|
293
|
-
if (error.statusCode === 401) {
|
|
294
|
-
console.log(chalk.red('Authentication failed'));
|
|
295
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
296
|
-
} else if (error.statusCode === 404) {
|
|
297
|
-
console.log(chalk.red('Endpoint not found'));
|
|
298
|
-
console.log(chalk.yellow('The Lambda API may not be implemented yet.'));
|
|
299
|
-
console.log(chalk.cyan('See STORAGE_LAMBDA_ARCHITECTURE.md for details\n'));
|
|
300
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
301
|
-
console.log(chalk.red('Connection refused'));
|
|
302
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
303
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
304
|
-
} else {
|
|
305
|
-
console.log(chalk.red(`Error: ${error.message}\n`));
|
|
306
|
-
}
|
|
307
|
-
|
|
283
|
+
handleError(error, 'Lambda deployment');
|
|
308
284
|
process.exit(1);
|
|
309
285
|
}
|
|
310
286
|
}
|
|
@@ -360,21 +336,7 @@ async function lambdaList() {
|
|
|
360
336
|
|
|
361
337
|
} catch (error) {
|
|
362
338
|
spinner.fail(chalk.red('Failed to list functions'));
|
|
363
|
-
|
|
364
|
-
if (error.statusCode === 401) {
|
|
365
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
366
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
367
|
-
} else if (error.statusCode === 404) {
|
|
368
|
-
console.log(chalk.red('\n✗ Endpoint not found'));
|
|
369
|
-
console.log(chalk.yellow('The Lambda API may not be implemented yet.\n'));
|
|
370
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
371
|
-
console.log(chalk.red('\n✗ Connection refused'));
|
|
372
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
373
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
374
|
-
} else {
|
|
375
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
376
|
-
}
|
|
377
|
-
|
|
339
|
+
handleError(error, 'Lambda listing');
|
|
378
340
|
process.exit(1);
|
|
379
341
|
}
|
|
380
342
|
}
|
|
@@ -403,6 +365,10 @@ async function lambdaInvoke(functionName, options = {}) {
|
|
|
403
365
|
process.exit(1);
|
|
404
366
|
}
|
|
405
367
|
}
|
|
368
|
+
|
|
369
|
+
// Add path (default to 'health' if not provided)
|
|
370
|
+
const path = options.path || 'health';
|
|
371
|
+
console.log(chalk.white('Path: '), chalk.yellow(path));
|
|
406
372
|
|
|
407
373
|
const spinner = ora('Invoking function...').start();
|
|
408
374
|
|
|
@@ -410,11 +376,16 @@ async function lambdaInvoke(functionName, options = {}) {
|
|
|
410
376
|
// Call backend API to invoke Lambda function
|
|
411
377
|
const startTime = Date.now();
|
|
412
378
|
const endpoint = API_ENDPOINTS.LAMBDA_INVOKE.replace('{functionName}', functionName);
|
|
379
|
+
const requestBody = {
|
|
380
|
+
payload,
|
|
381
|
+
path: options.path || 'health' // Default path
|
|
382
|
+
};
|
|
383
|
+
|
|
413
384
|
const response = await makeApiRequest(
|
|
414
385
|
'POST',
|
|
415
386
|
endpoint,
|
|
416
387
|
credentials,
|
|
417
|
-
|
|
388
|
+
requestBody
|
|
418
389
|
);
|
|
419
390
|
|
|
420
391
|
const duration = Date.now() - startTime;
|
|
@@ -433,33 +404,43 @@ async function lambdaInvoke(functionName, options = {}) {
|
|
|
433
404
|
}
|
|
434
405
|
|
|
435
406
|
console.log(chalk.cyan('\nResponse:'));
|
|
436
|
-
|
|
407
|
+
|
|
408
|
+
// Parse and format the response nicely
|
|
409
|
+
const lambdaResponse = response.response || response;
|
|
410
|
+
|
|
411
|
+
// If response has statusCode and body (API Gateway format)
|
|
412
|
+
if (lambdaResponse.statusCode && lambdaResponse.body) {
|
|
413
|
+
console.log(chalk.white('Status Code: '), chalk.green(lambdaResponse.statusCode));
|
|
414
|
+
|
|
415
|
+
// Try to parse body as JSON
|
|
416
|
+
let bodyContent;
|
|
417
|
+
try {
|
|
418
|
+
bodyContent = JSON.parse(lambdaResponse.body);
|
|
419
|
+
console.log(chalk.white('\nBody:'));
|
|
420
|
+
console.log(chalk.green(JSON.stringify(bodyContent, null, 2)));
|
|
421
|
+
} catch (e) {
|
|
422
|
+
// If not JSON, show as string
|
|
423
|
+
console.log(chalk.white('\nBody:'));
|
|
424
|
+
console.log(chalk.green(lambdaResponse.body));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Show headers if available
|
|
428
|
+
if (lambdaResponse.headers && Object.keys(lambdaResponse.headers).length > 0) {
|
|
429
|
+
console.log(chalk.white('\nHeaders:'));
|
|
430
|
+
Object.entries(lambdaResponse.headers).forEach(([key, value]) => {
|
|
431
|
+
console.log(chalk.gray(` ${key}: `), chalk.yellow(value));
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
} else {
|
|
435
|
+
// Regular response format
|
|
436
|
+
console.log(chalk.green(JSON.stringify(lambdaResponse, null, 2)));
|
|
437
|
+
}
|
|
437
438
|
|
|
438
439
|
console.log(chalk.cyan('\n============================================================\n'));
|
|
439
440
|
|
|
440
441
|
} catch (error) {
|
|
441
442
|
spinner.fail(chalk.red('Invocation failed'));
|
|
442
|
-
|
|
443
|
-
if (error.statusCode === 401) {
|
|
444
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
445
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
446
|
-
} else if (error.statusCode === 404) {
|
|
447
|
-
console.log(chalk.red('\n✗ Function not found'));
|
|
448
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
449
|
-
} else if (error.statusCode === 500) {
|
|
450
|
-
console.log(chalk.red('\n✗ Function execution error'));
|
|
451
|
-
if (error.response && error.response.error) {
|
|
452
|
-
console.log(chalk.yellow('Error:'), chalk.white(error.response.error));
|
|
453
|
-
}
|
|
454
|
-
console.log();
|
|
455
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
456
|
-
console.log(chalk.red('\n✗ Connection refused'));
|
|
457
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
458
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
459
|
-
} else {
|
|
460
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
461
|
-
}
|
|
462
|
-
|
|
443
|
+
handleError(error, 'Lambda invocation');
|
|
463
444
|
process.exit(1);
|
|
464
445
|
}
|
|
465
446
|
}
|
|
@@ -486,11 +467,24 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
486
467
|
|
|
487
468
|
const spinner = ora('Fetching logs...').start();
|
|
488
469
|
const endpoint = API_ENDPOINTS.LAMBDA_LOGS.replace('{functionName}', functionName);
|
|
470
|
+
|
|
471
|
+
// Build query parameters for pagination
|
|
472
|
+
const queryParams = {};
|
|
473
|
+
if (options.page) {
|
|
474
|
+
queryParams.page = parseInt(options.page) || 1;
|
|
475
|
+
}
|
|
476
|
+
if (options.perPage) {
|
|
477
|
+
queryParams.per_page = Math.min(parseInt(options.perPage) || 100, 1000);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const queryString = new URLSearchParams(queryParams).toString();
|
|
481
|
+
const fullEndpoint = queryString ? `${endpoint}?${queryString}` : endpoint;
|
|
482
|
+
|
|
489
483
|
try {
|
|
490
484
|
// Call backend API to get logs
|
|
491
485
|
const response = await makeApiRequest(
|
|
492
486
|
'GET',
|
|
493
|
-
|
|
487
|
+
fullEndpoint,
|
|
494
488
|
credentials
|
|
495
489
|
);
|
|
496
490
|
|
|
@@ -498,12 +492,37 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
498
492
|
|
|
499
493
|
console.log(chalk.cyan('\n============================================================'));
|
|
500
494
|
|
|
495
|
+
// Show sync statistics
|
|
496
|
+
if (response.synced_from_cloudwatch !== undefined || response.skipped_duplicates !== undefined) {
|
|
497
|
+
console.log(chalk.cyan('📊 Sync Statistics:'));
|
|
498
|
+
if (response.total_cloudwatch_logs !== undefined) {
|
|
499
|
+
console.log(chalk.white(' CloudWatch logs: '), chalk.yellow(response.total_cloudwatch_logs));
|
|
500
|
+
}
|
|
501
|
+
if (response.synced_from_cloudwatch !== undefined) {
|
|
502
|
+
console.log(chalk.white(' Newly synced: '), chalk.green(response.synced_from_cloudwatch));
|
|
503
|
+
}
|
|
504
|
+
if (response.skipped_duplicates !== undefined) {
|
|
505
|
+
console.log(chalk.white(' Skipped (dup): '), chalk.gray(response.skipped_duplicates));
|
|
506
|
+
}
|
|
507
|
+
console.log();
|
|
508
|
+
}
|
|
509
|
+
|
|
501
510
|
if (!response.logs || response.logs.length === 0) {
|
|
502
|
-
console.log(chalk.yellow('No logs found'));
|
|
511
|
+
console.log(chalk.yellow('No logs found in database'));
|
|
503
512
|
console.log(chalk.cyan('\nTry invoking the function first:'));
|
|
504
513
|
console.log(chalk.white(' orcapt lambda invoke'), chalk.cyan(functionName));
|
|
505
514
|
} else {
|
|
506
|
-
|
|
515
|
+
// Show pagination info
|
|
516
|
+
if (response.pagination) {
|
|
517
|
+
const pagination = response.pagination;
|
|
518
|
+
console.log(chalk.cyan('📄 Pagination:'));
|
|
519
|
+
console.log(chalk.white(' Page: '), chalk.yellow(`${pagination.current_page} / ${pagination.total_pages}`));
|
|
520
|
+
console.log(chalk.white(' Per page: '), chalk.yellow(pagination.per_page));
|
|
521
|
+
console.log(chalk.white(' Total logs: '), chalk.yellow(pagination.total));
|
|
522
|
+
console.log();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
console.log(chalk.green(`✓ Showing ${response.logs.length} log entries (page ${response.pagination?.current_page || 1})`));
|
|
507
526
|
console.log(chalk.cyan('============================================================\n'));
|
|
508
527
|
|
|
509
528
|
response.logs.forEach(log => {
|
|
@@ -521,6 +540,21 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
521
540
|
chalk.white(log.message)
|
|
522
541
|
);
|
|
523
542
|
});
|
|
543
|
+
|
|
544
|
+
// Show pagination navigation hints
|
|
545
|
+
if (response.pagination) {
|
|
546
|
+
const pagination = response.pagination;
|
|
547
|
+
console.log(chalk.cyan('\n============================================================'));
|
|
548
|
+
if (pagination.has_prev_page) {
|
|
549
|
+
console.log(chalk.white(' Previous page: '), chalk.cyan(`orcapt lambda logs ${functionName} --page ${pagination.current_page - 1}`));
|
|
550
|
+
}
|
|
551
|
+
if (pagination.has_next_page) {
|
|
552
|
+
console.log(chalk.white(' Next page: '), chalk.cyan(`orcapt lambda logs ${functionName} --page ${pagination.current_page + 1}`));
|
|
553
|
+
}
|
|
554
|
+
if (!pagination.has_prev_page && !pagination.has_next_page) {
|
|
555
|
+
console.log(chalk.gray(' (No more pages)'));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
524
558
|
}
|
|
525
559
|
|
|
526
560
|
console.log(chalk.cyan('\n============================================================'));
|
|
@@ -534,21 +568,7 @@ async function lambdaLogs(functionName, options = {}) {
|
|
|
534
568
|
|
|
535
569
|
} catch (error) {
|
|
536
570
|
spinner.fail(chalk.red('Failed to fetch logs'));
|
|
537
|
-
|
|
538
|
-
if (error.statusCode === 401) {
|
|
539
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
540
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
541
|
-
} else if (error.statusCode === 404) {
|
|
542
|
-
console.log(chalk.red('\n✗ Function not found'));
|
|
543
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
544
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
545
|
-
console.log(chalk.red('\n✗ Connection refused'));
|
|
546
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
547
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
548
|
-
} else {
|
|
549
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
550
|
-
}
|
|
551
|
-
|
|
571
|
+
handleError(error, 'Lambda logs');
|
|
552
572
|
process.exit(1);
|
|
553
573
|
}
|
|
554
574
|
}
|
|
@@ -595,24 +615,7 @@ async function lambdaRemove(functionName) {
|
|
|
595
615
|
|
|
596
616
|
} catch (error) {
|
|
597
617
|
spinner.fail(chalk.red('Failed to remove function'));
|
|
598
|
-
|
|
599
|
-
if (error.statusCode === 401) {
|
|
600
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
601
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
602
|
-
} else if (error.statusCode === 404) {
|
|
603
|
-
console.log(chalk.red('\n✗ Function not found'));
|
|
604
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
605
|
-
} else if (error.statusCode === 409) {
|
|
606
|
-
console.log(chalk.red('\n✗ Conflict'));
|
|
607
|
-
console.log(chalk.yellow('Function may be currently in use. Please try again later.\n'));
|
|
608
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
609
|
-
console.log(chalk.red('\n✗ Connection refused'));
|
|
610
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
611
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
612
|
-
} else {
|
|
613
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
614
|
-
}
|
|
615
|
-
|
|
618
|
+
handleError(error, 'Lambda removal');
|
|
616
619
|
process.exit(1);
|
|
617
620
|
}
|
|
618
621
|
}
|
|
@@ -706,21 +709,7 @@ async function lambdaInfo(functionName) {
|
|
|
706
709
|
|
|
707
710
|
} catch (error) {
|
|
708
711
|
spinner.fail(chalk.red('Failed to fetch function details'));
|
|
709
|
-
|
|
710
|
-
if (error.statusCode === 401) {
|
|
711
|
-
console.log(chalk.red('\n✗ Authentication failed'));
|
|
712
|
-
console.log(chalk.yellow('Your session may have expired. Please run:'), chalk.white('orcapt login\n'));
|
|
713
|
-
} else if (error.statusCode === 404) {
|
|
714
|
-
console.log(chalk.red('\n✗ Function not found'));
|
|
715
|
-
console.log(chalk.yellow(`Function '${functionName}' does not exist or doesn't belong to your workspace.\n`));
|
|
716
|
-
} else if (error.code === 'ECONNREFUSED') {
|
|
717
|
-
console.log(chalk.red('\n✗ Connection refused'));
|
|
718
|
-
console.log(chalk.yellow('Cannot connect to Orcapt API:'), chalk.white(API_BASE_URL));
|
|
719
|
-
console.log(chalk.cyan('Make sure the backend is running.\n'));
|
|
720
|
-
} else {
|
|
721
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
722
|
-
}
|
|
723
|
-
|
|
712
|
+
handleError(error, 'Lambda info');
|
|
724
713
|
process.exit(1);
|
|
725
714
|
}
|
|
726
715
|
}
|
package/src/commands/storage.js
CHANGED
|
@@ -11,6 +11,7 @@ const https = require('https');
|
|
|
11
11
|
const http = require('http');
|
|
12
12
|
const { getCredentials } = require('./login');
|
|
13
13
|
const { API_BASE_URL, API_ENDPOINTS } = require('../config');
|
|
14
|
+
const { handleError } = require('../utils/errorHandler');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Make API request to orcapt Deploy API
|
|
@@ -253,18 +254,7 @@ async function bucketCreate(bucketName, options = {}) {
|
|
|
253
254
|
|
|
254
255
|
} catch (error) {
|
|
255
256
|
spinner.fail(chalk.red('✗ Failed to create bucket'));
|
|
256
|
-
|
|
257
|
-
if (error.response) {
|
|
258
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
259
|
-
if (error.statusCode === 409) {
|
|
260
|
-
console.log(chalk.yellow(' Bucket name already exists for this workspace'));
|
|
261
|
-
} else if (error.statusCode === 422) {
|
|
262
|
-
console.log(chalk.yellow(' Invalid bucket name or parameters'));
|
|
263
|
-
}
|
|
264
|
-
} else {
|
|
265
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
266
|
-
}
|
|
267
|
-
console.log('');
|
|
257
|
+
handleError(error, 'Bucket creation');
|
|
268
258
|
process.exit(1);
|
|
269
259
|
}
|
|
270
260
|
}
|
|
@@ -336,12 +326,7 @@ async function bucketList() {
|
|
|
336
326
|
|
|
337
327
|
} catch (error) {
|
|
338
328
|
spinner.fail(chalk.red('✗ Failed to list buckets'));
|
|
339
|
-
|
|
340
|
-
if (error.response) {
|
|
341
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}\n`));
|
|
342
|
-
} else {
|
|
343
|
-
console.log(chalk.red(`\n✗ ${error.message}\n`));
|
|
344
|
-
}
|
|
329
|
+
handleError(error, 'Bucket listing');
|
|
345
330
|
process.exit(1);
|
|
346
331
|
}
|
|
347
332
|
}
|
|
@@ -414,18 +399,7 @@ async function fileUpload(bucketName, localPath, options = {}) {
|
|
|
414
399
|
|
|
415
400
|
} catch (error) {
|
|
416
401
|
spinner.fail(chalk.red('✗ Upload failed'));
|
|
417
|
-
|
|
418
|
-
if (error.response) {
|
|
419
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
420
|
-
if (error.statusCode === 404) {
|
|
421
|
-
console.log(chalk.yellow(' Bucket not found'));
|
|
422
|
-
} else if (error.statusCode === 413) {
|
|
423
|
-
console.log(chalk.yellow(' File too large (max 100MB)'));
|
|
424
|
-
}
|
|
425
|
-
} else {
|
|
426
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
427
|
-
}
|
|
428
|
-
console.log('');
|
|
402
|
+
handleError(error, 'File upload');
|
|
429
403
|
process.exit(1);
|
|
430
404
|
}
|
|
431
405
|
}
|
|
@@ -504,16 +478,7 @@ async function fileDownload(bucketName, fileKey, localPath) {
|
|
|
504
478
|
|
|
505
479
|
} catch (error) {
|
|
506
480
|
spinner.fail(chalk.red('✗ Download failed'));
|
|
507
|
-
|
|
508
|
-
if (error.response) {
|
|
509
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
510
|
-
if (error.statusCode === 404) {
|
|
511
|
-
console.log(chalk.yellow(' File or bucket not found'));
|
|
512
|
-
}
|
|
513
|
-
} else {
|
|
514
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
515
|
-
}
|
|
516
|
-
console.log('');
|
|
481
|
+
handleError(error, 'File download');
|
|
517
482
|
process.exit(1);
|
|
518
483
|
}
|
|
519
484
|
}
|
|
@@ -560,16 +525,7 @@ async function bucketInfo(bucketName) {
|
|
|
560
525
|
|
|
561
526
|
} catch (error) {
|
|
562
527
|
spinner.fail(chalk.red('✗ Failed to get bucket info'));
|
|
563
|
-
|
|
564
|
-
if (error.response) {
|
|
565
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
566
|
-
if (error.statusCode === 404) {
|
|
567
|
-
console.log(chalk.yellow(' Bucket not found'));
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
571
|
-
}
|
|
572
|
-
console.log('');
|
|
528
|
+
handleError(error, 'Bucket info');
|
|
573
529
|
process.exit(1);
|
|
574
530
|
}
|
|
575
531
|
}
|
|
@@ -604,20 +560,7 @@ async function bucketDelete(bucketName, options = {}) {
|
|
|
604
560
|
|
|
605
561
|
} catch (error) {
|
|
606
562
|
spinner.fail(chalk.red('✗ Failed to delete bucket'));
|
|
607
|
-
|
|
608
|
-
if (error.response) {
|
|
609
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
610
|
-
if (error.statusCode === 404) {
|
|
611
|
-
console.log(chalk.yellow(' Bucket not found'));
|
|
612
|
-
} else if (error.statusCode === 400 && error.response.file_count) {
|
|
613
|
-
console.log(chalk.yellow(` Bucket contains ${error.response.file_count} file(s)`));
|
|
614
|
-
console.log(chalk.cyan(' Use --force to delete anyway:'));
|
|
615
|
-
console.log(chalk.white(' '), chalk.yellow(`orcapt storage bucket delete ${bucketName} --force`));
|
|
616
|
-
}
|
|
617
|
-
} else {
|
|
618
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
619
|
-
}
|
|
620
|
-
console.log('');
|
|
563
|
+
handleError(error, 'Bucket deletion');
|
|
621
564
|
process.exit(1);
|
|
622
565
|
}
|
|
623
566
|
}
|
|
@@ -713,16 +656,7 @@ async function fileList(bucketName, options = {}) {
|
|
|
713
656
|
|
|
714
657
|
} catch (error) {
|
|
715
658
|
spinner.fail(chalk.red('✗ Failed to list files'));
|
|
716
|
-
|
|
717
|
-
if (error.response) {
|
|
718
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
719
|
-
if (error.statusCode === 404) {
|
|
720
|
-
console.log(chalk.yellow(' Bucket not found'));
|
|
721
|
-
}
|
|
722
|
-
} else {
|
|
723
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
724
|
-
}
|
|
725
|
-
console.log('');
|
|
659
|
+
handleError(error, 'File listing');
|
|
726
660
|
process.exit(1);
|
|
727
661
|
}
|
|
728
662
|
}
|
|
@@ -755,16 +689,7 @@ async function fileDelete(bucketName, fileKey) {
|
|
|
755
689
|
|
|
756
690
|
} catch (error) {
|
|
757
691
|
spinner.fail(chalk.red('✗ Failed to delete file'));
|
|
758
|
-
|
|
759
|
-
if (error.response) {
|
|
760
|
-
console.log(chalk.red(`\n✗ ${error.response.message || 'Unknown error'}`));
|
|
761
|
-
if (error.statusCode === 404) {
|
|
762
|
-
console.log(chalk.yellow(' File or bucket not found'));
|
|
763
|
-
}
|
|
764
|
-
} else {
|
|
765
|
-
console.log(chalk.red(`\n✗ ${error.message}`));
|
|
766
|
-
}
|
|
767
|
-
console.log('');
|
|
692
|
+
handleError(error, 'File deletion');
|
|
768
693
|
process.exit(1);
|
|
769
694
|
}
|
|
770
695
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Error Handler for Orca CLI
|
|
3
|
+
* Handles errors from different API services and formats them for CLI display
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract error message from API response
|
|
10
|
+
* Supports multiple response formats:
|
|
11
|
+
* - databaseasservice: { success: false, error: "message" }
|
|
12
|
+
* - gptclone-api/platform-api: { result: null, message: "message" }
|
|
13
|
+
* - Generic: { detail: "message" } or { message: "message" }
|
|
14
|
+
*/
|
|
15
|
+
function extractErrorMessage(error) {
|
|
16
|
+
if (!error.response) {
|
|
17
|
+
return error.message || 'Unknown error occurred';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const response = error.response;
|
|
21
|
+
|
|
22
|
+
// databaseasservice format: { success: false, error: "message" }
|
|
23
|
+
if (response.error) {
|
|
24
|
+
return response.error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// gptclone-api/platform-api format: { result: null, message: "message" }
|
|
28
|
+
if (response.message) {
|
|
29
|
+
return response.message;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Generic format: { detail: "message" }
|
|
33
|
+
if (response.detail) {
|
|
34
|
+
return response.detail;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Laravel validation errors: { errors: { field: ["message"] } }
|
|
38
|
+
if (response.errors && typeof response.errors === 'object') {
|
|
39
|
+
const errorMessages = [];
|
|
40
|
+
for (const [field, messages] of Object.entries(response.errors)) {
|
|
41
|
+
if (Array.isArray(messages)) {
|
|
42
|
+
errorMessages.push(...messages);
|
|
43
|
+
} else {
|
|
44
|
+
errorMessages.push(messages);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (errorMessages.length > 0) {
|
|
48
|
+
return errorMessages.join(', ');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fallback: try to stringify the response
|
|
53
|
+
if (typeof response === 'string') {
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return 'Unknown error occurred';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get user-friendly error message based on status code
|
|
62
|
+
*/
|
|
63
|
+
function getStatusMessage(statusCode) {
|
|
64
|
+
const messages = {
|
|
65
|
+
400: 'Bad Request - Please check your input parameters',
|
|
66
|
+
401: 'Authentication failed - Your session may have expired',
|
|
67
|
+
403: 'Forbidden - You don\'t have permission to perform this action',
|
|
68
|
+
404: 'Resource not found',
|
|
69
|
+
409: 'Conflict - Resource already exists',
|
|
70
|
+
422: 'Validation error - Please check your input',
|
|
71
|
+
500: 'Internal server error - Please try again later',
|
|
72
|
+
502: 'Bad Gateway - Service temporarily unavailable',
|
|
73
|
+
503: 'Service unavailable - Please try again later',
|
|
74
|
+
504: 'Gateway timeout - Request took too long',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return messages[statusCode] || `HTTP ${statusCode} error`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle and display error to CLI
|
|
82
|
+
*/
|
|
83
|
+
function handleError(error, context = 'Operation') {
|
|
84
|
+
const statusCode = error.statusCode || error.status || (error.response && error.response.statusCode);
|
|
85
|
+
const errorMessage = extractErrorMessage(error);
|
|
86
|
+
|
|
87
|
+
// Network/connection errors
|
|
88
|
+
if (error.code === 'ECONNREFUSED') {
|
|
89
|
+
console.log(chalk.red(`\n✗ Connection refused`));
|
|
90
|
+
console.log(chalk.yellow('The API server is not reachable. Please check:'));
|
|
91
|
+
console.log(chalk.white(' 1. Is the backend service running?'));
|
|
92
|
+
console.log(chalk.white(' 2. Is the API URL correct?'));
|
|
93
|
+
console.log(chalk.white(' 3. Check your network connection\n'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
|
98
|
+
console.log(chalk.red(`\n✗ Network error: ${error.message}`));
|
|
99
|
+
console.log(chalk.yellow('Please check your network connection and API URL\n'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// HTTP status code errors
|
|
104
|
+
if (statusCode) {
|
|
105
|
+
const statusMessage = getStatusMessage(statusCode);
|
|
106
|
+
|
|
107
|
+
console.log(chalk.red(`\n✗ ${context} failed`));
|
|
108
|
+
console.log(chalk.red(` Status: ${statusCode} - ${statusMessage}`));
|
|
109
|
+
|
|
110
|
+
if (errorMessage && errorMessage !== 'Unknown error occurred') {
|
|
111
|
+
console.log(chalk.yellow(` Error: ${errorMessage}`));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Specific handling for common status codes
|
|
115
|
+
if (statusCode === 401) {
|
|
116
|
+
console.log(chalk.cyan('\nPlease run:'), chalk.white('orcapt login'), chalk.cyan('to authenticate\n'));
|
|
117
|
+
} else if (statusCode === 404) {
|
|
118
|
+
console.log(chalk.yellow('\nThe requested resource was not found or doesn\'t belong to your workspace\n'));
|
|
119
|
+
} else if (statusCode === 422) {
|
|
120
|
+
console.log(chalk.yellow('\nPlease check your input parameters and try again\n'));
|
|
121
|
+
} else if (statusCode >= 500) {
|
|
122
|
+
console.log(chalk.yellow('\nThis appears to be a server-side error. Please try again later.\n'));
|
|
123
|
+
console.log(chalk.gray('If the problem persists, contact support.\n'));
|
|
124
|
+
} else {
|
|
125
|
+
console.log('');
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Generic error
|
|
131
|
+
console.log(chalk.red(`\n✗ ${context} failed`));
|
|
132
|
+
console.log(chalk.red(` ${errorMessage}\n`));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Format error for programmatic use (returns error object)
|
|
137
|
+
*/
|
|
138
|
+
function formatError(error) {
|
|
139
|
+
return {
|
|
140
|
+
statusCode: error.statusCode || error.status || (error.response && error.response.statusCode),
|
|
141
|
+
message: extractErrorMessage(error),
|
|
142
|
+
code: error.code,
|
|
143
|
+
response: error.response,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
handleError,
|
|
149
|
+
formatError,
|
|
150
|
+
extractErrorMessage,
|
|
151
|
+
getStatusMessage,
|
|
152
|
+
};
|
|
153
|
+
|