zuppaclaude 1.3.2 → 1.3.3

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.
@@ -4,15 +4,28 @@
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
+ const { execSync, spawn } = require('child_process');
7
8
  const { Logger } = require('../utils/logger');
8
9
  const { Platform } = require('../utils/platform');
10
+ const { Prompts } = require('../utils/prompts');
9
11
 
10
12
  class CloudManager {
11
13
  constructor() {
12
14
  this.platform = new Platform();
13
15
  this.logger = new Logger();
16
+ this.prompts = new Prompts();
14
17
  this.backupDir = path.join(this.platform.zuppaconfigDir, 'backups');
15
18
  this.cloudPath = 'zuppaclaude-backups';
19
+
20
+ // Supported providers
21
+ this.providers = [
22
+ { name: 'Google Drive', type: 'drive', remoteName: 'gdrive' },
23
+ { name: 'Dropbox', type: 'dropbox', remoteName: 'dropbox' },
24
+ { name: 'OneDrive', type: 'onedrive', remoteName: 'onedrive' },
25
+ { name: 'Amazon S3', type: 's3', remoteName: 's3' },
26
+ { name: 'SFTP', type: 'sftp', remoteName: 'sftp' },
27
+ { name: 'Skip (configure later)', type: null, remoteName: null }
28
+ ];
16
29
  }
17
30
 
18
31
  /**
@@ -76,6 +89,115 @@ class CloudManager {
76
89
  }
77
90
  }
78
91
 
92
+ /**
93
+ * Configure a cloud provider
94
+ */
95
+ async configureProvider() {
96
+ if (!this.isRcloneInstalled()) {
97
+ this.logger.error('rclone is not installed');
98
+ return null;
99
+ }
100
+
101
+ console.log('');
102
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
103
+ console.log('\x1b[35m Cloud Provider Setup\x1b[0m');
104
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
105
+ console.log('');
106
+
107
+ // Show provider options
108
+ const providerNames = this.providers.map(p => p.name);
109
+ const selectedIndex = await this.prompts.select('Select a cloud provider:', providerNames);
110
+ const provider = this.providers[selectedIndex];
111
+
112
+ if (!provider.type) {
113
+ this.logger.info('Skipping cloud provider configuration');
114
+ this.logger.info('Run "rclone config" later to set up manually');
115
+ return null;
116
+ }
117
+
118
+ console.log('');
119
+ this.logger.step(`Configuring ${provider.name}...`);
120
+
121
+ // Check if remote already exists
122
+ const existingRemotes = this.getRemotes();
123
+ if (existingRemotes.includes(provider.remoteName)) {
124
+ this.logger.warning(`Remote "${provider.remoteName}" already exists`);
125
+ const overwrite = await this.prompts.confirm('Overwrite existing configuration?', false);
126
+ if (!overwrite) {
127
+ return provider.remoteName;
128
+ }
129
+ // Delete existing remote
130
+ try {
131
+ this.platform.exec(`rclone config delete ${provider.remoteName}`, { silent: true });
132
+ } catch (e) {
133
+ // Ignore
134
+ }
135
+ }
136
+
137
+ // Run rclone config create
138
+ try {
139
+ await this.runProviderAuth(provider);
140
+
141
+ // Verify configuration
142
+ if (this.getRemotes().includes(provider.remoteName)) {
143
+ this.logger.success(`${provider.name} configured successfully`);
144
+ console.log('');
145
+ console.log(' Backup path: \x1b[36m' + provider.remoteName + ':zuppaclaude-backups/\x1b[0m');
146
+ console.log(' ├── sessions/');
147
+ console.log(' └── settings/');
148
+ console.log('');
149
+ return provider.remoteName;
150
+ } else {
151
+ this.logger.error('Configuration failed');
152
+ return null;
153
+ }
154
+ } catch (error) {
155
+ this.logger.error(`Failed to configure ${provider.name}: ${error.message}`);
156
+ return null;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Run provider-specific authentication
162
+ */
163
+ async runProviderAuth(provider) {
164
+ return new Promise((resolve, reject) => {
165
+ console.log('');
166
+
167
+ if (provider.type === 'drive' || provider.type === 'dropbox' || provider.type === 'onedrive') {
168
+ // OAuth providers - need browser auth
169
+ this.logger.info('A browser window will open for authentication...');
170
+ this.logger.info('Please authorize ZuppaClaude to access your ' + provider.name);
171
+ console.log('');
172
+ }
173
+
174
+ // Use spawn for interactive rclone config
175
+ const args = ['config', 'create', provider.remoteName, provider.type];
176
+
177
+ // Add provider-specific defaults
178
+ if (provider.type === 'drive') {
179
+ args.push('scope', 'drive');
180
+ }
181
+
182
+ const rclone = spawn('rclone', args, {
183
+ stdio: 'inherit',
184
+ shell: true
185
+ });
186
+
187
+ rclone.on('close', (code) => {
188
+ if (code === 0) {
189
+ resolve();
190
+ } else {
191
+ reject(new Error(`rclone exited with code ${code}`));
192
+ }
193
+ });
194
+
195
+ rclone.on('error', (err) => {
196
+ reject(err);
197
+ });
198
+ });
199
+ }
200
+
79
201
  /**
80
202
  * Verify rclone installation
81
203
  */
package/lib/installer.js CHANGED
@@ -130,6 +130,7 @@ class Installer {
130
130
  this.logger.step('Step 7/9: Cloud Backup Setup (rclone)');
131
131
  let installRclone = false;
132
132
  let rcloneInstalled = this.cloud.isRcloneInstalled();
133
+ let configuredRemote = null;
133
134
 
134
135
  if (rcloneInstalled) {
135
136
  this.logger.success('rclone is already installed');
@@ -147,6 +148,19 @@ class Installer {
147
148
  }
148
149
  }
149
150
 
151
+ // Configure cloud provider if rclone is installed
152
+ if (rcloneInstalled) {
153
+ const existingRemotes = this.cloud.getRemotes();
154
+ if (existingRemotes.length === 0) {
155
+ const configureNow = await this.prompts.confirm('Configure a cloud provider now?', true);
156
+ if (configureNow) {
157
+ configuredRemote = await this.cloud.configureProvider();
158
+ }
159
+ } else {
160
+ this.logger.info(`Existing remotes: ${existingRemotes.join(', ')}`);
161
+ }
162
+ }
163
+
150
164
  // Step 8: Install ZuppaClaude Commands
151
165
  this.logger.step('Step 8/9: Installing ZuppaClaude Slash Commands');
152
166
  const cmdsInstalled = await this.commands.install();
@@ -170,6 +184,7 @@ class Installer {
170
184
  claudeZ: installClaudeZ,
171
185
  claudeHUD: installClaudeHUD,
172
186
  rclone: rcloneInstalled,
187
+ cloudRemote: configuredRemote,
173
188
  zaiApiKey: zaiApiKey ? this.settings.encodeApiKey(zaiApiKey) : null,
174
189
  installedAt: new Date().toISOString(),
175
190
  version: require('../package.json').version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuppaclaude",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Claude Code power-up installer - SuperClaude + Spec Kit + Claude-Z + Claude HUD",
5
5
  "main": "lib/index.js",
6
6
  "bin": {