zuppaclaude 1.3.5 → 1.3.6
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/zuppaclaude.js +19 -0
- package/lib/components/cloud.js +115 -0
- package/lib/utils/prompts.js +18 -0
- package/package.json +1 -1
package/bin/zuppaclaude.js
CHANGED
|
@@ -202,6 +202,21 @@ async function main() {
|
|
|
202
202
|
}
|
|
203
203
|
await cloudMgr.listCloudBackups(cloudRemote);
|
|
204
204
|
break;
|
|
205
|
+
case 'delete':
|
|
206
|
+
case 'rm':
|
|
207
|
+
if (!cloudRemote) {
|
|
208
|
+
logger.error('Please specify remote');
|
|
209
|
+
logger.info('Usage: zuppaclaude cloud delete <remote> [backup-id]');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
if (args[3]) {
|
|
213
|
+
// Direct delete with backup ID
|
|
214
|
+
await cloudMgr.deleteCloudBackup(cloudRemote, args[3]);
|
|
215
|
+
} else {
|
|
216
|
+
// Interactive delete menu
|
|
217
|
+
await cloudMgr.deleteCloudBackupInteractive(cloudRemote);
|
|
218
|
+
}
|
|
219
|
+
break;
|
|
205
220
|
default:
|
|
206
221
|
logger.error(`Unknown cloud command: ${cloudCmd}`);
|
|
207
222
|
showCloudHelp();
|
|
@@ -296,6 +311,7 @@ Cloud Commands:
|
|
|
296
311
|
cloud upload <r> Upload backups to remote
|
|
297
312
|
cloud download <r> Download backups from remote
|
|
298
313
|
cloud backups <r> List cloud backups
|
|
314
|
+
cloud delete <r> Delete backup from cloud (interactive)
|
|
299
315
|
|
|
300
316
|
Update Commands:
|
|
301
317
|
update Check for updates
|
|
@@ -363,6 +379,7 @@ Cloud Commands (requires rclone):
|
|
|
363
379
|
upload Upload backups to a cloud remote
|
|
364
380
|
download Download backups from a cloud remote
|
|
365
381
|
backups List backups stored on a cloud remote
|
|
382
|
+
delete Delete backup from cloud (interactive menu)
|
|
366
383
|
|
|
367
384
|
Examples:
|
|
368
385
|
zuppaclaude cloud setup # Setup instructions
|
|
@@ -370,6 +387,8 @@ Examples:
|
|
|
370
387
|
zuppaclaude cloud upload gdrive # Upload all backups
|
|
371
388
|
zuppaclaude cloud download gdrive # Download all backups
|
|
372
389
|
zuppaclaude cloud backups gdrive # List cloud backups
|
|
390
|
+
zuppaclaude cloud delete gdrive # Interactive delete menu
|
|
391
|
+
zuppaclaude cloud delete gdrive Jan-05-2026-14.09 # Direct delete
|
|
373
392
|
|
|
374
393
|
Supported providers (via rclone):
|
|
375
394
|
Google Drive, Dropbox, OneDrive, S3, SFTP, FTP, and 40+ more
|
package/lib/components/cloud.js
CHANGED
|
@@ -722,6 +722,121 @@ class CloudManager {
|
|
|
722
722
|
}
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
+
/**
|
|
726
|
+
* Delete a backup from cloud
|
|
727
|
+
*/
|
|
728
|
+
async deleteCloudBackup(remote, backupId) {
|
|
729
|
+
if (!this.isRcloneInstalled()) {
|
|
730
|
+
this.logger.error('rclone is not installed');
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (!this.remoteExists(remote)) {
|
|
735
|
+
this.logger.error(`Remote not found: ${remote}`);
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const zipFileName = `${backupId}.zip`;
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
// Check if backup exists
|
|
743
|
+
const cmd = `rclone lsf "${remote}:${this.cloudPath}/${zipFileName}" 2>/dev/null`;
|
|
744
|
+
const output = this.platform.exec(cmd, { silent: true });
|
|
745
|
+
|
|
746
|
+
if (!output || !output.trim()) {
|
|
747
|
+
this.logger.error(`Backup not found on cloud: ${backupId}`);
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
this.logger.step(`Deleting ${zipFileName} from ${remote}...`);
|
|
752
|
+
|
|
753
|
+
// Delete the backup
|
|
754
|
+
const deleteCmd = `rclone delete "${remote}:${this.cloudPath}/${zipFileName}"`;
|
|
755
|
+
this.platform.exec(deleteCmd, { silent: true });
|
|
756
|
+
|
|
757
|
+
this.logger.success(`Deleted ${backupId} from ${remote}`);
|
|
758
|
+
return true;
|
|
759
|
+
} catch (error) {
|
|
760
|
+
this.logger.error(`Failed to delete: ${error.message}`);
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Interactive delete - show list and let user choose
|
|
767
|
+
*/
|
|
768
|
+
async deleteCloudBackupInteractive(remote) {
|
|
769
|
+
if (!this.isRcloneInstalled()) {
|
|
770
|
+
this.logger.error('rclone is not installed');
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (!this.remoteExists(remote)) {
|
|
775
|
+
this.logger.error(`Remote not found: ${remote}`);
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Get list of backups
|
|
780
|
+
const backups = await this.getCloudBackupList(remote);
|
|
781
|
+
|
|
782
|
+
if (backups.length === 0) {
|
|
783
|
+
this.logger.warning('No backups found on cloud');
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
console.log('');
|
|
788
|
+
console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
|
|
789
|
+
console.log(`\x1b[35m Delete Cloud Backup (${remote})\x1b[0m`);
|
|
790
|
+
console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
|
|
791
|
+
console.log('');
|
|
792
|
+
|
|
793
|
+
// Show numbered list
|
|
794
|
+
for (let i = 0; i < backups.length; i++) {
|
|
795
|
+
console.log(` ${i + 1}. 📦 ${backups[i]}`);
|
|
796
|
+
}
|
|
797
|
+
console.log(` ${backups.length + 1}. ❌ Cancel`);
|
|
798
|
+
console.log('');
|
|
799
|
+
|
|
800
|
+
// Get user choice
|
|
801
|
+
const choice = await this.prompts.number(`Select backup to delete (1-${backups.length + 1}):`, 1, backups.length + 1);
|
|
802
|
+
|
|
803
|
+
if (choice === backups.length + 1 || choice === null) {
|
|
804
|
+
this.logger.info('Cancelled');
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const selectedBackup = backups[choice - 1];
|
|
809
|
+
|
|
810
|
+
// Confirm deletion
|
|
811
|
+
const confirm = await this.prompts.confirm(`Delete "${selectedBackup}" from ${remote}?`, false);
|
|
812
|
+
|
|
813
|
+
if (!confirm) {
|
|
814
|
+
this.logger.info('Cancelled');
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return await this.deleteCloudBackup(remote, selectedBackup);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Get list of cloud backups (without printing)
|
|
823
|
+
*/
|
|
824
|
+
async getCloudBackupList(remote) {
|
|
825
|
+
try {
|
|
826
|
+
const cmd = `rclone lsf "${remote}:${this.cloudPath}/" --files-only 2>/dev/null`;
|
|
827
|
+
const output = this.platform.exec(cmd, { silent: true });
|
|
828
|
+
|
|
829
|
+
if (!output) return [];
|
|
830
|
+
|
|
831
|
+
return output
|
|
832
|
+
.split('\n')
|
|
833
|
+
.filter(f => f.endsWith('.zip'))
|
|
834
|
+
.map(f => f.replace('.zip', ''));
|
|
835
|
+
} catch (error) {
|
|
836
|
+
return [];
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
725
840
|
/**
|
|
726
841
|
* List cloud backups (zip files)
|
|
727
842
|
*/
|
package/lib/utils/prompts.js
CHANGED
|
@@ -129,6 +129,24 @@ class Prompts {
|
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Ask for a number within a range
|
|
135
|
+
*/
|
|
136
|
+
async number(question, min = 1, max = 10) {
|
|
137
|
+
const rl = this.createInterface();
|
|
138
|
+
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
rl.question(`${question} `, (answer) => {
|
|
141
|
+
const num = parseInt(answer, 10);
|
|
142
|
+
if (!isNaN(num) && num >= min && num <= max) {
|
|
143
|
+
resolve(num);
|
|
144
|
+
} else {
|
|
145
|
+
resolve(null);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
132
150
|
}
|
|
133
151
|
|
|
134
152
|
module.exports = { Prompts };
|