rocketride 1.0.6 → 1.1.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/README.md +2 -2
- package/dist/cjs/account.js +284 -0
- package/dist/cjs/account.js.map +1 -0
- package/dist/cjs/billing.js +171 -0
- package/dist/cjs/billing.js.map +1 -0
- package/dist/cjs/client.js +1208 -756
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/constants.js +10 -1
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/core/DAPBase.js +4 -1
- package/dist/cjs/core/DAPBase.js.map +1 -1
- package/dist/cjs/core/DAPClient.js +121 -50
- package/dist/cjs/core/DAPClient.js.map +1 -1
- package/dist/cjs/core/TransportBase.js +0 -10
- package/dist/cjs/core/TransportBase.js.map +1 -1
- package/dist/cjs/core/TransportWebSocket.js +30 -19
- package/dist/cjs/core/TransportWebSocket.js.map +1 -1
- package/dist/cjs/database.js +121 -0
- package/dist/cjs/database.js.map +1 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/schema/Question.js +2 -0
- package/dist/cjs/schema/Question.js.map +1 -1
- package/dist/cjs/types/account.js +26 -0
- package/dist/cjs/types/account.js.map +1 -0
- package/dist/cjs/types/billing.js +26 -0
- package/dist/cjs/types/billing.js.map +1 -0
- package/dist/cjs/types/client.js +14 -0
- package/dist/cjs/types/client.js.map +1 -1
- package/dist/cjs/types/cprofile.js +26 -0
- package/dist/cjs/types/cprofile.js.map +1 -0
- package/dist/cjs/types/dashboard.js +26 -0
- package/dist/cjs/types/dashboard.js.map +1 -0
- package/dist/cjs/types/events.js +5 -1
- package/dist/cjs/types/events.js.map +1 -1
- package/dist/cjs/types/index.js +5 -0
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/service.js +85 -0
- package/dist/cjs/types/service.js.map +1 -0
- package/dist/cli/cli/rocketride.js +335 -113
- package/dist/cli/cli/rocketride.js.map +1 -1
- package/dist/cli/client/account.js +284 -0
- package/dist/cli/client/account.js.map +1 -0
- package/dist/cli/client/billing.js +171 -0
- package/dist/cli/client/billing.js.map +1 -0
- package/dist/cli/client/client.js +1208 -756
- package/dist/cli/client/client.js.map +1 -1
- package/dist/cli/client/constants.js +10 -1
- package/dist/cli/client/constants.js.map +1 -1
- package/dist/cli/client/core/DAPBase.js +4 -1
- package/dist/cli/client/core/DAPBase.js.map +1 -1
- package/dist/cli/client/core/DAPClient.js +121 -50
- package/dist/cli/client/core/DAPClient.js.map +1 -1
- package/dist/cli/client/core/TransportBase.js +0 -10
- package/dist/cli/client/core/TransportBase.js.map +1 -1
- package/dist/cli/client/core/TransportWebSocket.js +30 -19
- package/dist/cli/client/core/TransportWebSocket.js.map +1 -1
- package/dist/cli/client/database.js +121 -0
- package/dist/cli/client/database.js.map +1 -0
- package/dist/cli/client/index.js +4 -0
- package/dist/cli/client/index.js.map +1 -1
- package/dist/cli/client/schema/Question.js +2 -0
- package/dist/cli/client/schema/Question.js.map +1 -1
- package/dist/cli/client/types/account.js +26 -0
- package/dist/cli/client/types/account.js.map +1 -0
- package/dist/cli/client/types/billing.js +26 -0
- package/dist/cli/client/types/billing.js.map +1 -0
- package/dist/cli/client/types/client.js +14 -0
- package/dist/cli/client/types/client.js.map +1 -1
- package/dist/cli/client/types/cprofile.js +26 -0
- package/dist/cli/client/types/cprofile.js.map +1 -0
- package/dist/cli/client/types/dashboard.js +26 -0
- package/dist/cli/client/types/dashboard.js.map +1 -0
- package/dist/cli/client/types/events.js +5 -1
- package/dist/cli/client/types/events.js.map +1 -1
- package/dist/cli/client/types/index.js +5 -0
- package/dist/cli/client/types/index.js.map +1 -1
- package/dist/cli/client/types/service.js +85 -0
- package/dist/cli/client/types/service.js.map +1 -0
- package/dist/esm/account.js +280 -0
- package/dist/esm/account.js.map +1 -0
- package/dist/esm/billing.js +167 -0
- package/dist/esm/billing.js.map +1 -0
- package/dist/esm/client.js +1208 -756
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/constants.js +9 -0
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/core/DAPBase.js +4 -1
- package/dist/esm/core/DAPBase.js.map +1 -1
- package/dist/esm/core/DAPClient.js +121 -50
- package/dist/esm/core/DAPClient.js.map +1 -1
- package/dist/esm/core/TransportBase.js +0 -10
- package/dist/esm/core/TransportBase.js.map +1 -1
- package/dist/esm/core/TransportWebSocket.js +30 -19
- package/dist/esm/core/TransportWebSocket.js.map +1 -1
- package/dist/esm/database.js +117 -0
- package/dist/esm/database.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/schema/Question.js +2 -0
- package/dist/esm/schema/Question.js.map +1 -1
- package/dist/esm/types/account.js +25 -0
- package/dist/esm/types/account.js.map +1 -0
- package/dist/esm/types/billing.js +25 -0
- package/dist/esm/types/billing.js.map +1 -0
- package/dist/esm/types/client.js +13 -1
- package/dist/esm/types/client.js.map +1 -1
- package/dist/esm/types/cprofile.js +25 -0
- package/dist/esm/types/cprofile.js.map +1 -0
- package/dist/esm/types/dashboard.js +25 -0
- package/dist/esm/types/dashboard.js.map +1 -0
- package/dist/esm/types/events.js +5 -1
- package/dist/esm/types/events.js.map +1 -1
- package/dist/esm/types/index.js +5 -0
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/service.js +82 -0
- package/dist/esm/types/service.js.map +1 -0
- package/dist/types/account.d.ts +209 -0
- package/dist/types/account.d.ts.map +1 -0
- package/dist/types/billing.d.ts +135 -0
- package/dist/types/billing.d.ts.map +1 -0
- package/dist/types/client.d.ts +553 -285
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/constants.d.ts +9 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/core/DAPBase.d.ts.map +1 -1
- package/dist/types/core/DAPClient.d.ts +89 -7
- package/dist/types/core/DAPClient.d.ts.map +1 -1
- package/dist/types/core/TransportBase.d.ts +1 -11
- package/dist/types/core/TransportBase.d.ts.map +1 -1
- package/dist/types/core/TransportWebSocket.d.ts +14 -11
- package/dist/types/core/TransportWebSocket.d.ts.map +1 -1
- package/dist/types/database.d.ts +90 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/schema/Question.d.ts +3 -1
- package/dist/types/schema/Question.d.ts.map +1 -1
- package/dist/types/types/account.d.ts +171 -0
- package/dist/types/types/account.d.ts.map +1 -0
- package/dist/types/types/billing.d.ts +115 -0
- package/dist/types/types/billing.d.ts.map +1 -0
- package/dist/types/types/client.d.ts +241 -1
- package/dist/types/types/client.d.ts.map +1 -1
- package/dist/types/types/cprofile.d.ts +67 -0
- package/dist/types/types/cprofile.d.ts.map +1 -0
- package/dist/types/types/dashboard.d.ts +198 -0
- package/dist/types/types/dashboard.d.ts.map +1 -0
- package/dist/types/types/events.d.ts +90 -95
- package/dist/types/types/events.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +5 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/types/pipeline.d.ts +10 -2
- package/dist/types/types/pipeline.d.ts.map +1 -1
- package/dist/types/types/service.d.ts +189 -0
- package/dist/types/types/service.d.ts.map +1 -0
- package/dist/types/types/task.d.ts +5 -1
- package/dist/types/types/task.d.ts.map +1 -1
- package/package.json +12 -7
|
@@ -141,7 +141,7 @@ class Box {
|
|
|
141
141
|
}
|
|
142
142
|
boxTop() {
|
|
143
143
|
const titlePart = ` ${this.title} `;
|
|
144
|
-
const remainingWidth =
|
|
144
|
+
const remainingWidth = this.width - 3 - titlePart.length;
|
|
145
145
|
return CHR_TL + CHR_HORIZ + titlePart + CHR_HORIZ.repeat(Math.max(0, remainingWidth)) + CHR_TR;
|
|
146
146
|
}
|
|
147
147
|
boxMiddle(content) {
|
|
@@ -328,23 +328,16 @@ class StatusMonitor extends BoxMonitor {
|
|
|
328
328
|
3: ['Online', ANSI_GREEN],
|
|
329
329
|
4: ['Stopping', ANSI_YELLOW],
|
|
330
330
|
5: ['Offline', ANSI_GRAY],
|
|
331
|
-
6: ['Offline', ANSI_GRAY]
|
|
331
|
+
6: ['Offline', ANSI_GRAY],
|
|
332
332
|
};
|
|
333
333
|
return stateMap[state] || ['Unknown', ANSI_RESET];
|
|
334
334
|
}
|
|
335
335
|
hasCountData(status) {
|
|
336
|
-
return (
|
|
337
|
-
(Number(status.totalCount) || 0) > 0 ||
|
|
338
|
-
(Number(status.completedSize) || 0) > 0 ||
|
|
339
|
-
(Number(status.completedCount) || 0) > 0 ||
|
|
340
|
-
(Number(status.failedSize) || 0) > 0 ||
|
|
341
|
-
(Number(status.failedCount) || 0) > 0 ||
|
|
342
|
-
(Number(status.rateSize) || 0) > 0 ||
|
|
343
|
-
(Number(status.rateCount) || 0) > 0);
|
|
336
|
+
return (Number(status.totalSize) || 0) > 0 || (Number(status.totalCount) || 0) > 0 || (Number(status.completedSize) || 0) > 0 || (Number(status.completedCount) || 0) > 0 || (Number(status.failedSize) || 0) > 0 || (Number(status.failedCount) || 0) > 0 || (Number(status.rateSize) || 0) > 0 || (Number(status.rateCount) || 0) > 0;
|
|
344
337
|
}
|
|
345
338
|
hasMetricsData(status) {
|
|
346
339
|
const metrics = (status.metrics || {});
|
|
347
|
-
return Object.values(metrics).some(value => typeof value === 'number' && value > 0);
|
|
340
|
+
return Object.values(metrics).some((value) => typeof value === 'number' && value > 0);
|
|
348
341
|
}
|
|
349
342
|
onEvent(message) {
|
|
350
343
|
const eventType = message.event || '';
|
|
@@ -390,7 +383,7 @@ class StatusMonitor extends BoxMonitor {
|
|
|
390
383
|
if (startTime > 0) {
|
|
391
384
|
const startStr = new Date(startTime * 1000).toLocaleString();
|
|
392
385
|
lines.push(`Started: ${startStr}`);
|
|
393
|
-
const endTime = status.completed ?
|
|
386
|
+
const endTime = status.completed ? Number(status.endTime) || 0 : undefined;
|
|
394
387
|
const duration = this.formatDuration(startTime, endTime);
|
|
395
388
|
lines.push(`Elapsed: ${duration}`);
|
|
396
389
|
}
|
|
@@ -399,7 +392,7 @@ class StatusMonitor extends BoxMonitor {
|
|
|
399
392
|
const dataTypes = [
|
|
400
393
|
['total', 'Total'],
|
|
401
394
|
['completed', 'Completed'],
|
|
402
|
-
['failed', 'Failed']
|
|
395
|
+
['failed', 'Failed'],
|
|
403
396
|
];
|
|
404
397
|
for (const [keyBase, label] of dataTypes) {
|
|
405
398
|
const count = Number(status[`${keyBase}Count`]) || 0;
|
|
@@ -424,7 +417,7 @@ class StatusMonitor extends BoxMonitor {
|
|
|
424
417
|
const metrics = (status.metrics || {});
|
|
425
418
|
for (const [key, value] of Object.entries(metrics)) {
|
|
426
419
|
if (typeof value === 'number' && value > 0) {
|
|
427
|
-
const label = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
420
|
+
const label = key.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
|
|
428
421
|
lines.push(`${label}: ${value}`);
|
|
429
422
|
}
|
|
430
423
|
}
|
|
@@ -442,8 +435,7 @@ class StatusMonitor extends BoxMonitor {
|
|
|
442
435
|
const errType = parts[0].trim();
|
|
443
436
|
const message = parts[1].replace(/`/g, '').trim();
|
|
444
437
|
const fileInfo = parts[2].trim();
|
|
445
|
-
const filename = fileInfo.includes('\\') || fileInfo.includes('/') ?
|
|
446
|
-
path.basename(fileInfo) : fileInfo;
|
|
438
|
+
const filename = fileInfo.includes('\\') || fileInfo.includes('/') ? path.basename(fileInfo) : fileInfo;
|
|
447
439
|
lines.push(`${color}${errType}${ANSI_RESET}: ${message}`);
|
|
448
440
|
if (filename) {
|
|
449
441
|
lines.push(` -> ${filename}`);
|
|
@@ -459,7 +451,7 @@ class StatusMonitor extends BoxMonitor {
|
|
|
459
451
|
if (!items || items.length === 0) {
|
|
460
452
|
return [];
|
|
461
453
|
}
|
|
462
|
-
return items.slice(-5).map(item => `• ${item}`);
|
|
454
|
+
return items.slice(-5).map((item) => `• ${item}`);
|
|
463
455
|
}
|
|
464
456
|
displayConnecting(url, attempt) {
|
|
465
457
|
this.clear();
|
|
@@ -480,7 +472,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
480
472
|
this.setCommandStatus('Preparing upload...');
|
|
481
473
|
}
|
|
482
474
|
createProgressBar(percent, width = 30) {
|
|
483
|
-
const filledLength = Math.floor(width * percent / 100);
|
|
475
|
+
const filledLength = Math.floor((width * percent) / 100);
|
|
484
476
|
const bar = CHR_BLOCK.repeat(filledLength) + CHR_LIGHT_BLOCK.repeat(width - filledLength);
|
|
485
477
|
return `[${bar}] ${percent.toFixed(1).padStart(5)}%`;
|
|
486
478
|
}
|
|
@@ -516,7 +508,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
516
508
|
filepath,
|
|
517
509
|
action,
|
|
518
510
|
bytes_sent: bytesSent,
|
|
519
|
-
file_size: fileSize
|
|
511
|
+
file_size: fileSize,
|
|
520
512
|
});
|
|
521
513
|
}
|
|
522
514
|
else if (action === 'close') {
|
|
@@ -526,7 +518,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
526
518
|
...existing,
|
|
527
519
|
action,
|
|
528
520
|
bytes_sent: bytesSent,
|
|
529
|
-
file_size: fileSize
|
|
521
|
+
file_size: fileSize,
|
|
530
522
|
});
|
|
531
523
|
}
|
|
532
524
|
}
|
|
@@ -535,7 +527,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
535
527
|
this.completedUploads.set(filename, {
|
|
536
528
|
filepath,
|
|
537
529
|
action,
|
|
538
|
-
file_size: fileSize
|
|
530
|
+
file_size: fileSize,
|
|
539
531
|
});
|
|
540
532
|
}
|
|
541
533
|
else if (action === 'error') {
|
|
@@ -545,7 +537,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
545
537
|
filepath,
|
|
546
538
|
action,
|
|
547
539
|
file_size: fileSize,
|
|
548
|
-
error: errorMessage
|
|
540
|
+
error: errorMessage,
|
|
549
541
|
});
|
|
550
542
|
}
|
|
551
543
|
this.draw();
|
|
@@ -569,7 +561,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
569
561
|
filepath,
|
|
570
562
|
action,
|
|
571
563
|
bytes_sent: bytesSent,
|
|
572
|
-
file_size: fileSize
|
|
564
|
+
file_size: fileSize,
|
|
573
565
|
});
|
|
574
566
|
}
|
|
575
567
|
else if (action === 'close') {
|
|
@@ -579,7 +571,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
579
571
|
...existing,
|
|
580
572
|
action,
|
|
581
573
|
bytes_sent: bytesSent,
|
|
582
|
-
file_size: fileSize
|
|
574
|
+
file_size: fileSize,
|
|
583
575
|
});
|
|
584
576
|
}
|
|
585
577
|
}
|
|
@@ -588,7 +580,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
588
580
|
this.completedUploads.set(filename, {
|
|
589
581
|
filepath,
|
|
590
582
|
action,
|
|
591
|
-
file_size: fileSize
|
|
583
|
+
file_size: fileSize,
|
|
592
584
|
});
|
|
593
585
|
}
|
|
594
586
|
else if (action === 'error') {
|
|
@@ -598,7 +590,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
598
590
|
filepath,
|
|
599
591
|
action,
|
|
600
592
|
file_size: fileSize,
|
|
601
|
-
error: errorMessage
|
|
593
|
+
error: errorMessage,
|
|
602
594
|
});
|
|
603
595
|
}
|
|
604
596
|
if (this.cli.isCancelled()) {
|
|
@@ -625,7 +617,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
625
617
|
const phase = action === 'write' ? 'Writing ' : action === 'close' ? 'Finalize' : ' ';
|
|
626
618
|
const bytesSent = Number(data.bytes_sent) || 0;
|
|
627
619
|
const fileSize = Number(data.file_size) || 1;
|
|
628
|
-
const percent = fileSize > 0 ? (bytesSent / fileSize * 100
|
|
620
|
+
const percent = fileSize > 0 ? (bytesSent / fileSize) * 100 : 0;
|
|
629
621
|
const progressBar = this.createProgressBar(percent, 12);
|
|
630
622
|
const sizeInfo = `${this.formatSize(bytesSent)}/${this.formatSize(fileSize)}`;
|
|
631
623
|
uploadLines.push(`${displayName} ${phase} ${progressBar} ${sizeInfo}`);
|
|
@@ -645,8 +637,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
645
637
|
if (this.failedUploads.size > 0) {
|
|
646
638
|
summaryLines.push(`Failed: ${this.failedUploads.size} files`);
|
|
647
639
|
}
|
|
648
|
-
const totalBytes = Array.from(this.completedUploads.values())
|
|
649
|
-
.reduce((sum, data) => sum + (Number(data.file_size) || 0), 0);
|
|
640
|
+
const totalBytes = Array.from(this.completedUploads.values()).reduce((sum, data) => sum + (Number(data.file_size) || 0), 0);
|
|
650
641
|
summaryLines.push(`Total size: ${this.formatSize(totalBytes)}`);
|
|
651
642
|
this.addBox('Upload Summary', summaryLines);
|
|
652
643
|
}
|
|
@@ -658,8 +649,7 @@ class UploadProgressMonitor extends BoxMonitor {
|
|
|
658
649
|
for (const [filename, data] of failedEntries.slice(-displayCount)) {
|
|
659
650
|
const displayName = this.truncateFilename(filename, 25);
|
|
660
651
|
const errorStr = String(data.error || '');
|
|
661
|
-
const errorMsg = errorStr.length > 30 ?
|
|
662
|
-
`${errorStr.substring(0, 30)}...` : errorStr;
|
|
652
|
+
const errorMsg = errorStr.length > 30 ? `${errorStr.substring(0, 30)}...` : errorStr;
|
|
663
653
|
failedLines.push(`${ANSI_RED}${CHR_CROSS}${ANSI_RESET} ${displayName} - ${errorMsg}`);
|
|
664
654
|
}
|
|
665
655
|
if (failedEntries.length > 5) {
|
|
@@ -697,7 +687,7 @@ class RocketRideCLI {
|
|
|
697
687
|
total_bytes: 0,
|
|
698
688
|
successful_uploads: 0,
|
|
699
689
|
failed_uploads: 0,
|
|
700
|
-
upload_times: []
|
|
690
|
+
upload_times: [],
|
|
701
691
|
};
|
|
702
692
|
this.uri = '';
|
|
703
693
|
this.connected = false;
|
|
@@ -711,25 +701,52 @@ class RocketRideCLI {
|
|
|
711
701
|
isCancelled() {
|
|
712
702
|
return this.cancelled;
|
|
713
703
|
}
|
|
704
|
+
isShuttingDown() {
|
|
705
|
+
return this.signalShutdownPromise !== undefined;
|
|
706
|
+
}
|
|
707
|
+
// Resolves only when the signal handler calls process.exit, so any
|
|
708
|
+
// command/run/main flow that awaits this will stand down and let the
|
|
709
|
+
// signal handler own the exit code.
|
|
710
|
+
awaitShutdown() {
|
|
711
|
+
return this.signalShutdownPromise ?? new Promise(() => { });
|
|
712
|
+
}
|
|
714
713
|
setupSignalHandlers() {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
714
|
+
const FORCE_EXIT_TIMEOUT_MS = 5000;
|
|
715
|
+
const signalHandler = async (signal) => {
|
|
716
|
+
const exitCode = 128 + (signal === 'SIGINT' ? 2 : 15);
|
|
717
|
+
if (this.signalShutdownPromise) {
|
|
718
|
+
// Second signal: force exit immediately
|
|
719
|
+
process.exit(exitCode);
|
|
720
|
+
}
|
|
721
|
+
// Park a promise that never resolves; other flows await it to
|
|
722
|
+
// stand down while the signal handler drives the exit.
|
|
723
|
+
this.signalShutdownPromise = new Promise(() => { });
|
|
724
|
+
this.cancel();
|
|
725
|
+
// Force exit if cleanup hangs
|
|
726
|
+
const forceExitTimer = setTimeout(() => {
|
|
727
|
+
console.error(`\nCleanup timed out after ${FORCE_EXIT_TIMEOUT_MS}ms, forcing exit`);
|
|
728
|
+
process.exit(exitCode);
|
|
729
|
+
}, FORCE_EXIT_TIMEOUT_MS);
|
|
730
|
+
try {
|
|
731
|
+
await this.cleanupClient();
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
// Ignore cleanup errors during signal handling
|
|
735
|
+
}
|
|
736
|
+
finally {
|
|
737
|
+
clearTimeout(forceExitTimer);
|
|
738
|
+
}
|
|
739
|
+
process.exit(exitCode);
|
|
740
|
+
};
|
|
741
|
+
process.on('SIGINT', () => signalHandler('SIGINT'));
|
|
742
|
+
process.on('SIGTERM', () => signalHandler('SIGTERM'));
|
|
721
743
|
}
|
|
722
744
|
createProgram() {
|
|
723
745
|
const program = new commander_1.Command();
|
|
724
|
-
program
|
|
725
|
-
.name('rocketride')
|
|
726
|
-
.description('RocketRide Unified Pipeline and File Management CLI')
|
|
727
|
-
.version('1.3.0');
|
|
746
|
+
program.name('rocketride').description('RocketRide Unified Pipeline and File Management CLI').version('1.3.0');
|
|
728
747
|
// Common options
|
|
729
748
|
const addCommonOptions = (cmd) => {
|
|
730
|
-
return cmd
|
|
731
|
-
.option('--uri <uri>', 'RocketRide server URI (can use ROCKETRIDE_URI env var)', process.env.ROCKETRIDE_URI || constants_1.CONST_DEFAULT_WEB_LOCAL)
|
|
732
|
-
.option('--apikey <key>', 'API key for RocketRide server authentication (can use ROCKETRIDE_APIKEY in .env or env var)', process.env.ROCKETRIDE_APIKEY);
|
|
749
|
+
return cmd.option('--uri <uri>', 'RocketRide server URI (can use ROCKETRIDE_URI env var)', process.env.ROCKETRIDE_URI || constants_1.CONST_DEFAULT_WEB_LOCAL).option('--apikey <key>', 'API key for RocketRide server authentication (can use ROCKETRIDE_APIKEY in .env or env var)', process.env.ROCKETRIDE_APIKEY);
|
|
733
750
|
};
|
|
734
751
|
// Start command
|
|
735
752
|
const startCmd = program
|
|
@@ -749,16 +766,20 @@ class RocketRideCLI {
|
|
|
749
766
|
command: 'start',
|
|
750
767
|
...options,
|
|
751
768
|
pipeline: options.pipeline,
|
|
752
|
-
threads: parseInt(options.threads)
|
|
769
|
+
threads: parseInt(options.threads),
|
|
753
770
|
};
|
|
754
771
|
this.uri = options.uri;
|
|
755
772
|
try {
|
|
756
773
|
const exitCode = await this.cmdStart();
|
|
757
|
-
|
|
774
|
+
if (!this.isCancelled()) {
|
|
775
|
+
process.exit(exitCode);
|
|
776
|
+
}
|
|
758
777
|
}
|
|
759
778
|
finally {
|
|
760
|
-
this.
|
|
761
|
-
|
|
779
|
+
if (!this.isCancelled()) {
|
|
780
|
+
this.cancel();
|
|
781
|
+
await this.cleanupClient();
|
|
782
|
+
}
|
|
762
783
|
}
|
|
763
784
|
});
|
|
764
785
|
addCommonOptions(startCmd);
|
|
@@ -784,16 +805,20 @@ class RocketRideCLI {
|
|
|
784
805
|
files,
|
|
785
806
|
threads: parseInt(options.threads),
|
|
786
807
|
max_concurrent: parseInt(options.maxConcurrent || '5'),
|
|
787
|
-
pipeline_args: options.args
|
|
808
|
+
pipeline_args: options.args,
|
|
788
809
|
};
|
|
789
810
|
this.uri = options.uri;
|
|
790
811
|
try {
|
|
791
812
|
const exitCode = await this.cmdUpload();
|
|
792
|
-
|
|
813
|
+
if (!this.isCancelled()) {
|
|
814
|
+
process.exit(exitCode);
|
|
815
|
+
}
|
|
793
816
|
}
|
|
794
817
|
finally {
|
|
795
|
-
this.
|
|
796
|
-
|
|
818
|
+
if (!this.isCancelled()) {
|
|
819
|
+
this.cancel();
|
|
820
|
+
await this.cleanupClient();
|
|
821
|
+
}
|
|
797
822
|
}
|
|
798
823
|
});
|
|
799
824
|
addCommonOptions(uploadCmd);
|
|
@@ -815,11 +840,15 @@ class RocketRideCLI {
|
|
|
815
840
|
this.uri = options.uri;
|
|
816
841
|
try {
|
|
817
842
|
const exitCode = await this.cmdStatus();
|
|
818
|
-
|
|
843
|
+
if (!this.isCancelled()) {
|
|
844
|
+
process.exit(exitCode);
|
|
845
|
+
}
|
|
819
846
|
}
|
|
820
847
|
finally {
|
|
821
|
-
this.
|
|
822
|
-
|
|
848
|
+
if (!this.isCancelled()) {
|
|
849
|
+
this.cancel();
|
|
850
|
+
await this.cleanupClient();
|
|
851
|
+
}
|
|
823
852
|
}
|
|
824
853
|
});
|
|
825
854
|
addCommonOptions(statusCmd);
|
|
@@ -841,14 +870,213 @@ class RocketRideCLI {
|
|
|
841
870
|
this.uri = options.uri;
|
|
842
871
|
try {
|
|
843
872
|
const exitCode = await this.cmdStop();
|
|
844
|
-
|
|
873
|
+
if (!this.isCancelled()) {
|
|
874
|
+
process.exit(exitCode);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
finally {
|
|
878
|
+
if (!this.isCancelled()) {
|
|
879
|
+
this.cancel();
|
|
880
|
+
await this.cleanupClient();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
addCommonOptions(stopCmd);
|
|
885
|
+
// Store command with file system subcommands
|
|
886
|
+
const storeCmd = program.command('store').description('File store operations');
|
|
887
|
+
// store dir
|
|
888
|
+
const storeDirCmd = storeCmd
|
|
889
|
+
.command('dir [path]')
|
|
890
|
+
.description('List directory contents')
|
|
891
|
+
.action(async (path, options) => {
|
|
892
|
+
this.args = { command: 'store', subcommand: 'dir', path: path || '', ...options };
|
|
893
|
+
this.uri = options.uri;
|
|
894
|
+
try {
|
|
895
|
+
const client = await this.createAndConnectClient();
|
|
896
|
+
const result = await client.fsListDir(path || '');
|
|
897
|
+
const entries = result.entries || [];
|
|
898
|
+
if (entries.length === 0) {
|
|
899
|
+
const stat = path ? await client.fsStat(path) : { exists: true, type: 'dir' };
|
|
900
|
+
if (stat.exists && stat.type === 'dir') {
|
|
901
|
+
console.log(` ${(0).toLocaleString().padStart(8)} File(s) ${(0).toLocaleString().padStart(14)} bytes`);
|
|
902
|
+
console.log(` ${(0).toLocaleString().padStart(8)} Dir(s)`);
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
console.log('File Not Found');
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
let totalSize = 0;
|
|
910
|
+
let fileCount = 0;
|
|
911
|
+
let dirCount = 0;
|
|
912
|
+
for (const e of entries) {
|
|
913
|
+
let dateStr = ' ';
|
|
914
|
+
if (e.modified) {
|
|
915
|
+
const d = new Date(e.modified * 1000);
|
|
916
|
+
const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
917
|
+
const dd = String(d.getUTCDate()).padStart(2, '0');
|
|
918
|
+
const yyyy = d.getUTCFullYear();
|
|
919
|
+
let hh = d.getUTCHours();
|
|
920
|
+
const min = String(d.getUTCMinutes()).padStart(2, '0');
|
|
921
|
+
const ampm = hh >= 12 ? 'PM' : 'AM';
|
|
922
|
+
hh = hh % 12 || 12;
|
|
923
|
+
dateStr = `${mm}/${dd}/${yyyy} ${String(hh).padStart(2, '0')}:${min} ${ampm}`;
|
|
924
|
+
}
|
|
925
|
+
if (e.type === 'dir') {
|
|
926
|
+
console.log(`${dateStr} <DIR> ${e.name}`);
|
|
927
|
+
dirCount++;
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
const size = e.size ?? 0;
|
|
931
|
+
totalSize += size;
|
|
932
|
+
console.log(`${dateStr} ${size.toLocaleString().padStart(14)} ${e.name}`);
|
|
933
|
+
fileCount++;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
console.log(` ${fileCount.toLocaleString().padStart(8)} File(s) ${totalSize.toLocaleString().padStart(14)} bytes`);
|
|
937
|
+
console.log(` ${dirCount.toLocaleString().padStart(8)} Dir(s)`);
|
|
938
|
+
}
|
|
939
|
+
process.exit(0);
|
|
845
940
|
}
|
|
846
941
|
finally {
|
|
847
942
|
this.cancel();
|
|
848
943
|
await this.cleanupClient();
|
|
849
944
|
}
|
|
850
945
|
});
|
|
851
|
-
addCommonOptions(
|
|
946
|
+
addCommonOptions(storeDirCmd);
|
|
947
|
+
// store type
|
|
948
|
+
const storeTypeCmd = storeCmd
|
|
949
|
+
.command('type <path>')
|
|
950
|
+
.description('Display file contents')
|
|
951
|
+
.action(async (path, options) => {
|
|
952
|
+
this.args = { command: 'store', subcommand: 'type', path, ...options };
|
|
953
|
+
this.uri = options.uri;
|
|
954
|
+
try {
|
|
955
|
+
const client = await this.createAndConnectClient();
|
|
956
|
+
const text = await client.fsReadString(path);
|
|
957
|
+
process.stdout.write(text);
|
|
958
|
+
process.exit(0);
|
|
959
|
+
}
|
|
960
|
+
finally {
|
|
961
|
+
this.cancel();
|
|
962
|
+
await this.cleanupClient();
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
addCommonOptions(storeTypeCmd);
|
|
966
|
+
// store write
|
|
967
|
+
const storeWriteCmd = storeCmd
|
|
968
|
+
.command('write <path>')
|
|
969
|
+
.description('Write a file')
|
|
970
|
+
.option('--file <localFile>', 'Local file to upload')
|
|
971
|
+
.option('--content <text>', 'Inline text content')
|
|
972
|
+
.action(async (path, options) => {
|
|
973
|
+
this.args = { command: 'store', subcommand: 'write', path, ...options };
|
|
974
|
+
this.uri = options.uri;
|
|
975
|
+
try {
|
|
976
|
+
const client = await this.createAndConnectClient();
|
|
977
|
+
if (options.file) {
|
|
978
|
+
const nodeFs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
979
|
+
const { handle } = await client.fsOpen(path, 'w');
|
|
980
|
+
try {
|
|
981
|
+
const stream = nodeFs.createReadStream(options.file);
|
|
982
|
+
for await (const chunk of stream) {
|
|
983
|
+
await client.fsWrite(handle, chunk);
|
|
984
|
+
}
|
|
985
|
+
await client.fsClose(handle, 'w');
|
|
986
|
+
}
|
|
987
|
+
catch (err) {
|
|
988
|
+
try {
|
|
989
|
+
await client.fsClose(handle, 'w');
|
|
990
|
+
}
|
|
991
|
+
catch {
|
|
992
|
+
/* best-effort */
|
|
993
|
+
}
|
|
994
|
+
throw err;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
else if (options.content !== undefined) {
|
|
998
|
+
await client.fsWriteString(path, options.content);
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
console.error('Error: Either --file or --content is required');
|
|
1002
|
+
process.exit(1);
|
|
1003
|
+
}
|
|
1004
|
+
console.log(`Written: ${path}`);
|
|
1005
|
+
process.exit(0);
|
|
1006
|
+
}
|
|
1007
|
+
finally {
|
|
1008
|
+
this.cancel();
|
|
1009
|
+
await this.cleanupClient();
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
addCommonOptions(storeWriteCmd);
|
|
1013
|
+
// store rm
|
|
1014
|
+
const storeRmCmd = storeCmd
|
|
1015
|
+
.command('rm <path>')
|
|
1016
|
+
.description('Delete a file')
|
|
1017
|
+
.action(async (path, options) => {
|
|
1018
|
+
this.args = { command: 'store', subcommand: 'rm', path, ...options };
|
|
1019
|
+
this.uri = options.uri;
|
|
1020
|
+
try {
|
|
1021
|
+
const client = await this.createAndConnectClient();
|
|
1022
|
+
await client.fsDelete(path);
|
|
1023
|
+
console.log(`Deleted: ${path}`);
|
|
1024
|
+
process.exit(0);
|
|
1025
|
+
}
|
|
1026
|
+
finally {
|
|
1027
|
+
this.cancel();
|
|
1028
|
+
await this.cleanupClient();
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
addCommonOptions(storeRmCmd);
|
|
1032
|
+
// store mkdir
|
|
1033
|
+
const storeMkdirCmd = storeCmd
|
|
1034
|
+
.command('mkdir <path>')
|
|
1035
|
+
.description('Create a directory')
|
|
1036
|
+
.action(async (path, options) => {
|
|
1037
|
+
this.args = { command: 'store', subcommand: 'mkdir', path, ...options };
|
|
1038
|
+
this.uri = options.uri;
|
|
1039
|
+
try {
|
|
1040
|
+
const client = await this.createAndConnectClient();
|
|
1041
|
+
await client.fsMkdir(path);
|
|
1042
|
+
console.log(`Created: ${path}/`);
|
|
1043
|
+
process.exit(0);
|
|
1044
|
+
}
|
|
1045
|
+
finally {
|
|
1046
|
+
this.cancel();
|
|
1047
|
+
await this.cleanupClient();
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
addCommonOptions(storeMkdirCmd);
|
|
1051
|
+
// store stat
|
|
1052
|
+
const storeStatCmd = storeCmd
|
|
1053
|
+
.command('stat <path>')
|
|
1054
|
+
.description('Get file/directory metadata')
|
|
1055
|
+
.action(async (path, options) => {
|
|
1056
|
+
this.args = { command: 'store', subcommand: 'stat', path, ...options };
|
|
1057
|
+
this.uri = options.uri;
|
|
1058
|
+
try {
|
|
1059
|
+
const client = await this.createAndConnectClient();
|
|
1060
|
+
const result = await client.fsStat(path);
|
|
1061
|
+
if (!result.exists) {
|
|
1062
|
+
console.log(`${path}: not found`);
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
const details = [];
|
|
1066
|
+
if (result.size !== undefined)
|
|
1067
|
+
details.push(`size: ${result.size.toLocaleString()}`);
|
|
1068
|
+
if (result.modified)
|
|
1069
|
+
details.push(`modified: ${new Date(result.modified * 1000).toISOString()}`);
|
|
1070
|
+
console.log(`${path}: ${result.type}${details.length ? ` (${details.join(', ')})` : ''}`);
|
|
1071
|
+
}
|
|
1072
|
+
process.exit(0);
|
|
1073
|
+
}
|
|
1074
|
+
finally {
|
|
1075
|
+
this.cancel();
|
|
1076
|
+
await this.cleanupClient();
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
addCommonOptions(storeStatCmd);
|
|
852
1080
|
return program;
|
|
853
1081
|
}
|
|
854
1082
|
async handleEvent(message) {
|
|
@@ -862,7 +1090,7 @@ class RocketRideCLI {
|
|
|
862
1090
|
auth: this.args.apikey,
|
|
863
1091
|
onEvent: this.handleEvent.bind(this),
|
|
864
1092
|
onConnected,
|
|
865
|
-
onDisconnected
|
|
1093
|
+
onDisconnected,
|
|
866
1094
|
});
|
|
867
1095
|
await this.client.connect();
|
|
868
1096
|
return this.client;
|
|
@@ -874,7 +1102,7 @@ class RocketRideCLI {
|
|
|
874
1102
|
const arguments_ = { subscribe };
|
|
875
1103
|
const monitorRequest = this.client.buildRequest('rrext_monitor', {
|
|
876
1104
|
token,
|
|
877
|
-
arguments: arguments_
|
|
1105
|
+
arguments: arguments_,
|
|
878
1106
|
});
|
|
879
1107
|
const monitorResponse = await this.client.request(monitorRequest);
|
|
880
1108
|
return !this.client.didFail(monitorResponse);
|
|
@@ -884,17 +1112,17 @@ class RocketRideCLI {
|
|
|
884
1112
|
}
|
|
885
1113
|
}
|
|
886
1114
|
async cleanupClient() {
|
|
887
|
-
if (this.
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
}
|
|
894
|
-
finally {
|
|
895
|
-
this.client = undefined;
|
|
896
|
-
}
|
|
1115
|
+
if (this._cleanupPromise) {
|
|
1116
|
+
return this._cleanupPromise;
|
|
1117
|
+
}
|
|
1118
|
+
const client = this.client;
|
|
1119
|
+
if (!client) {
|
|
1120
|
+
return;
|
|
897
1121
|
}
|
|
1122
|
+
this.client = undefined;
|
|
1123
|
+
this._cleanupPromise = client.disconnect().catch(() => { });
|
|
1124
|
+
await this._cleanupPromise;
|
|
1125
|
+
this._cleanupPromise = undefined;
|
|
898
1126
|
}
|
|
899
1127
|
loadPipelineConfig(pipelineFile) {
|
|
900
1128
|
if (!fs.existsSync(pipelineFile) || !fs.statSync(pipelineFile).isFile()) {
|
|
@@ -933,15 +1161,9 @@ class RocketRideCLI {
|
|
|
933
1161
|
pipeline: pipelineData,
|
|
934
1162
|
threads: this.args.threads,
|
|
935
1163
|
token: this.args.token,
|
|
936
|
-
args: this.args.pipeline_args || []
|
|
1164
|
+
args: this.args.pipeline_args || [],
|
|
937
1165
|
});
|
|
938
|
-
const executionLines = [
|
|
939
|
-
'Pipeline execution started successfully',
|
|
940
|
-
`Task token: ${taskToken}`,
|
|
941
|
-
'',
|
|
942
|
-
'Use the following command to monitor status:',
|
|
943
|
-
`rocketride status --token ${taskToken} --apikey ${this.args.apikey}`
|
|
944
|
-
];
|
|
1166
|
+
const executionLines = ['Pipeline execution started successfully', `Task token: ${taskToken}`, '', 'Use the following command to monitor status:', `rocketride status --token ${taskToken} --apikey ${this.args.apikey}`];
|
|
945
1167
|
this.monitor.setCommandStatus(executionLines);
|
|
946
1168
|
this.monitor.draw();
|
|
947
1169
|
return 0;
|
|
@@ -973,7 +1195,7 @@ class RocketRideCLI {
|
|
|
973
1195
|
total_bytes: 0,
|
|
974
1196
|
successful_uploads: 0,
|
|
975
1197
|
failed_uploads: 0,
|
|
976
|
-
upload_times: []
|
|
1198
|
+
upload_times: [],
|
|
977
1199
|
};
|
|
978
1200
|
let pipelineConfig;
|
|
979
1201
|
let taskToken;
|
|
@@ -1022,7 +1244,7 @@ class RocketRideCLI {
|
|
|
1022
1244
|
this.monitor.addBox('File Validation Errors', validationErrorLines);
|
|
1023
1245
|
this.monitor.draw();
|
|
1024
1246
|
// Wait briefly to show errors
|
|
1025
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1247
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
1026
1248
|
}
|
|
1027
1249
|
if (validFiles.length === 0) {
|
|
1028
1250
|
this.monitor.setCommandStatus('File validation failed');
|
|
@@ -1041,7 +1263,7 @@ class RocketRideCLI {
|
|
|
1041
1263
|
pipeline: pipelineConfig,
|
|
1042
1264
|
threads: this.args.threads,
|
|
1043
1265
|
token: 'UPLOAD_TASK',
|
|
1044
|
-
args: this.args.pipeline_args || []
|
|
1266
|
+
args: this.args.pipeline_args || [],
|
|
1045
1267
|
});
|
|
1046
1268
|
taskToken = result.token;
|
|
1047
1269
|
}
|
|
@@ -1050,18 +1272,18 @@ class RocketRideCLI {
|
|
|
1050
1272
|
this.monitor.draw();
|
|
1051
1273
|
const startTime = Date.now();
|
|
1052
1274
|
// Convert file paths to File objects for sendFiles
|
|
1053
|
-
const fileObjects = validFiles.map(filePath => {
|
|
1275
|
+
const fileObjects = validFiles.map((filePath) => {
|
|
1054
1276
|
const stats = fs.statSync(filePath);
|
|
1055
1277
|
const content = fs.readFileSync(filePath);
|
|
1056
1278
|
return {
|
|
1057
1279
|
file: new File([content], path.basename(filePath), {
|
|
1058
1280
|
type: 'application/octet-stream',
|
|
1059
|
-
lastModified: stats.mtimeMs
|
|
1281
|
+
lastModified: stats.mtimeMs,
|
|
1060
1282
|
}),
|
|
1061
1283
|
objinfo: {
|
|
1062
1284
|
filepath: filePath,
|
|
1063
|
-
size: stats.size
|
|
1064
|
-
}
|
|
1285
|
+
size: stats.size,
|
|
1286
|
+
},
|
|
1065
1287
|
};
|
|
1066
1288
|
});
|
|
1067
1289
|
// Upload files - progress events come through event subscription
|
|
@@ -1073,11 +1295,7 @@ class RocketRideCLI {
|
|
|
1073
1295
|
// Cleanup pipeline if we created it
|
|
1074
1296
|
if (shouldManagePipeline && taskToken) {
|
|
1075
1297
|
try {
|
|
1076
|
-
this.monitor.setCommandStatus([
|
|
1077
|
-
'Upload completed successfully',
|
|
1078
|
-
'Cleaning up...',
|
|
1079
|
-
'Terminating pipeline...'
|
|
1080
|
-
]);
|
|
1298
|
+
this.monitor.setCommandStatus(['Upload completed successfully', 'Cleaning up...', 'Terminating pipeline...']);
|
|
1081
1299
|
this.monitor.draw();
|
|
1082
1300
|
await this.client.terminate(taskToken);
|
|
1083
1301
|
// Re-show results after cleanup
|
|
@@ -1121,13 +1339,13 @@ class RocketRideCLI {
|
|
|
1121
1339
|
}
|
|
1122
1340
|
else if (stat.isDirectory()) {
|
|
1123
1341
|
const dirFiles = glob.sync(path.join(fullPath, '**/*'), { nodir: true });
|
|
1124
|
-
files.push(...dirFiles.map(f => path.resolve(f)));
|
|
1342
|
+
files.push(...dirFiles.map((f) => path.resolve(f)));
|
|
1125
1343
|
}
|
|
1126
1344
|
}
|
|
1127
1345
|
catch {
|
|
1128
1346
|
// Try glob pattern
|
|
1129
1347
|
const matches = glob.sync(pattern, { nodir: true });
|
|
1130
|
-
files.push(...matches.map(f => path.resolve(f)));
|
|
1348
|
+
files.push(...matches.map((f) => path.resolve(f)));
|
|
1131
1349
|
}
|
|
1132
1350
|
}
|
|
1133
1351
|
// Remove duplicates
|
|
@@ -1169,13 +1387,13 @@ class RocketRideCLI {
|
|
|
1169
1387
|
successfulFiles.push({
|
|
1170
1388
|
name: filename,
|
|
1171
1389
|
size: result.file_size || 0,
|
|
1172
|
-
time: result.upload_time || 0
|
|
1390
|
+
time: result.upload_time || 0,
|
|
1173
1391
|
});
|
|
1174
1392
|
}
|
|
1175
1393
|
else {
|
|
1176
1394
|
failedFiles.push({
|
|
1177
1395
|
name: filename,
|
|
1178
|
-
error: result.error || 'Unknown error'
|
|
1396
|
+
error: result.error || 'Unknown error',
|
|
1179
1397
|
});
|
|
1180
1398
|
this.uploadStats.failed_uploads++;
|
|
1181
1399
|
}
|
|
@@ -1185,10 +1403,7 @@ class RocketRideCLI {
|
|
|
1185
1403
|
const successful = this.uploadStats.successful_uploads;
|
|
1186
1404
|
const failed = this.uploadStats.failed_uploads;
|
|
1187
1405
|
const totalBytes = this.uploadStats.total_bytes;
|
|
1188
|
-
const summaryLines = [
|
|
1189
|
-
`Total files processed: ${successful + failed}`,
|
|
1190
|
-
`Successful uploads: ${ANSI_GREEN}${successful}${ANSI_RESET}`
|
|
1191
|
-
];
|
|
1406
|
+
const summaryLines = [`Total files processed: ${successful + failed}`, `Successful uploads: ${ANSI_GREEN}${successful}${ANSI_RESET}`];
|
|
1192
1407
|
if (failed > 0) {
|
|
1193
1408
|
summaryLines.push(`Failed uploads: ${ANSI_RED}${failed}${ANSI_RESET}`);
|
|
1194
1409
|
}
|
|
@@ -1222,10 +1437,8 @@ class RocketRideCLI {
|
|
|
1222
1437
|
if (failedFiles.length > 0) {
|
|
1223
1438
|
const failureLines = [];
|
|
1224
1439
|
for (const failedFile of failedFiles.slice(0, 10)) {
|
|
1225
|
-
const filename = failedFile.name.length > 25 ?
|
|
1226
|
-
|
|
1227
|
-
const errorMsg = failedFile.error.length > 40 ?
|
|
1228
|
-
`${failedFile.error.substring(0, 40)}...` : failedFile.error;
|
|
1440
|
+
const filename = failedFile.name.length > 25 ? `${failedFile.name.substring(0, 25)}...` : failedFile.name;
|
|
1441
|
+
const errorMsg = failedFile.error.length > 40 ? `${failedFile.error.substring(0, 40)}...` : failedFile.error;
|
|
1229
1442
|
failureLines.push(`${ANSI_RED}${CHR_CROSS}${ANSI_RESET} ${filename} - ${errorMsg}`);
|
|
1230
1443
|
}
|
|
1231
1444
|
if (failedFiles.length > 10) {
|
|
@@ -1237,8 +1450,7 @@ class RocketRideCLI {
|
|
|
1237
1450
|
if (successfulFiles.length > 0) {
|
|
1238
1451
|
const successLines = [];
|
|
1239
1452
|
for (const successFile of successfulFiles.slice(-5)) {
|
|
1240
|
-
const truncatedName = successFile.name.length > 35 ?
|
|
1241
|
-
`${successFile.name.substring(0, 35)}...` : successFile.name;
|
|
1453
|
+
const truncatedName = successFile.name.length > 35 ? `${successFile.name.substring(0, 35)}...` : successFile.name;
|
|
1242
1454
|
const sizeStr = this.monitor.formatSize(successFile.size);
|
|
1243
1455
|
const timeStr = `${successFile.time.toFixed(1)}s`;
|
|
1244
1456
|
successLines.push(`${ANSI_GREEN}${CHR_CHECK}${ANSI_RESET} ${truncatedName} (${sizeStr}, ${timeStr})`);
|
|
@@ -1276,11 +1488,11 @@ class RocketRideCLI {
|
|
|
1276
1488
|
}
|
|
1277
1489
|
catch {
|
|
1278
1490
|
this.attempt++;
|
|
1279
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
1491
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
1280
1492
|
continue;
|
|
1281
1493
|
}
|
|
1282
1494
|
}
|
|
1283
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1495
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1284
1496
|
}
|
|
1285
1497
|
return 0;
|
|
1286
1498
|
}
|
|
@@ -1313,11 +1525,7 @@ class RocketRideCLI {
|
|
|
1313
1525
|
this.monitor.setCommandStatus(`Terminating task: ${this.args.token}`);
|
|
1314
1526
|
this.monitor.draw();
|
|
1315
1527
|
await this.client.terminate(this.args.token);
|
|
1316
|
-
const stopLines = [
|
|
1317
|
-
`Task ${this.args.token} terminated successfully`,
|
|
1318
|
-
'',
|
|
1319
|
-
'The task has been stopped and resources cleaned up.'
|
|
1320
|
-
];
|
|
1528
|
+
const stopLines = [`Task ${this.args.token} terminated successfully`, '', 'The task has been stopped and resources cleaned up.'];
|
|
1321
1529
|
this.monitor.setCommandStatus(stopLines);
|
|
1322
1530
|
this.monitor.draw();
|
|
1323
1531
|
return 0;
|
|
@@ -1340,9 +1548,16 @@ class RocketRideCLI {
|
|
|
1340
1548
|
// Parse command line arguments - commander will handle command routing
|
|
1341
1549
|
try {
|
|
1342
1550
|
await program.parseAsync(process.argv);
|
|
1551
|
+
if (this.isShuttingDown()) {
|
|
1552
|
+
// Signal handler owns the exit; park until it calls process.exit.
|
|
1553
|
+
await this.awaitShutdown();
|
|
1554
|
+
}
|
|
1343
1555
|
return 0; // If we get here, a command was executed successfully
|
|
1344
1556
|
}
|
|
1345
1557
|
catch (error) {
|
|
1558
|
+
if (this.isShuttingDown()) {
|
|
1559
|
+
await this.awaitShutdown();
|
|
1560
|
+
}
|
|
1346
1561
|
if (error instanceof Error && error.message.includes('interrupted')) {
|
|
1347
1562
|
console.log('\nOperation interrupted by user');
|
|
1348
1563
|
return 1;
|
|
@@ -1369,12 +1584,19 @@ function formatError(e) {
|
|
|
1369
1584
|
return `${e.constructor.name}: ${e.message}`;
|
|
1370
1585
|
}
|
|
1371
1586
|
async function main() {
|
|
1587
|
+
const cli = new RocketRideCLI();
|
|
1372
1588
|
try {
|
|
1373
|
-
const cli = new RocketRideCLI();
|
|
1374
1589
|
const exitCode = await cli.run();
|
|
1590
|
+
if (cli.isShuttingDown()) {
|
|
1591
|
+
// Signal handler owns the exit code; never race it to process.exit.
|
|
1592
|
+
await cli.awaitShutdown();
|
|
1593
|
+
}
|
|
1375
1594
|
process.exit(exitCode);
|
|
1376
1595
|
}
|
|
1377
1596
|
catch (error) {
|
|
1597
|
+
if (cli.isShuttingDown()) {
|
|
1598
|
+
await cli.awaitShutdown();
|
|
1599
|
+
}
|
|
1378
1600
|
if (error instanceof Error && error.message.includes('interrupted')) {
|
|
1379
1601
|
console.log('\n\nOperation interrupted by user');
|
|
1380
1602
|
}
|
|
@@ -1386,7 +1608,7 @@ async function main() {
|
|
|
1386
1608
|
}
|
|
1387
1609
|
// Entry point when script is run directly
|
|
1388
1610
|
if (require.main === module) {
|
|
1389
|
-
main().catch(error => {
|
|
1611
|
+
main().catch((error) => {
|
|
1390
1612
|
console.error('Fatal error:', error);
|
|
1391
1613
|
process.exit(1);
|
|
1392
1614
|
});
|