d-drive-cli 2.0.0 → 2.2.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 +176 -130
- package/dist/commands/config.js +1 -1
- package/dist/config.js +1 -1
- package/dist/index.js +203 -20
- package/package.json +4 -3
- package/src/commands/config.ts +1 -1
- package/src/config.ts +1 -1
- package/src/index.ts +213 -20
package/README.md
CHANGED
|
@@ -1,225 +1,271 @@
|
|
|
1
|
-
# D-Drive CLI
|
|
1
|
+
# D-Drive CLI v2.2.0
|
|
2
2
|
|
|
3
3
|
Command-line tool for D-Drive cloud storage.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```
|
|
6
|
+
╔═══════════════════════════════════════╗
|
|
7
|
+
║ D-Drive CLI v2.2.0 ║
|
|
8
|
+
║ Discord-based cloud storage ║
|
|
9
|
+
╚═══════════════════════════════════════╝
|
|
10
|
+
```
|
|
6
11
|
|
|
7
12
|
## Installation
|
|
8
13
|
|
|
9
14
|
```bash
|
|
15
|
+
# Install globally
|
|
10
16
|
npm install -g d-drive-cli
|
|
17
|
+
|
|
18
|
+
# Or use npx
|
|
19
|
+
npx d-drive-cli
|
|
11
20
|
```
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
After installation, you can use either command:
|
|
23
|
+
- `d-drive` - Full command name
|
|
24
|
+
- `drive` - Short alias
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
## Quick Start
|
|
16
27
|
|
|
17
28
|
```bash
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
# Interactive configuration (recommended)
|
|
30
|
+
drive config
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
# Or set API key directly
|
|
33
|
+
drive config --key YOUR_API_KEY --url https://your-server/api
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
# Check connection
|
|
36
|
+
drive info
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```
|
|
38
|
+
# Upload a file
|
|
39
|
+
drive upload ./backup.zip
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
# List files
|
|
42
|
+
drive ls
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
# Download a file
|
|
45
|
+
drive download /backup.zip ./local-backup.zip
|
|
33
46
|
```
|
|
34
47
|
|
|
35
|
-
##
|
|
48
|
+
## Commands
|
|
36
49
|
|
|
37
|
-
###
|
|
38
|
-
|
|
39
|
-
Upload a single file:
|
|
50
|
+
### Configuration
|
|
40
51
|
|
|
41
52
|
```bash
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
# Interactive setup
|
|
54
|
+
drive config
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
# Set API key
|
|
57
|
+
drive config --key dd_your_api_key
|
|
46
58
|
|
|
47
|
-
|
|
59
|
+
# Set API URL
|
|
60
|
+
drive config --url https://your-server/api
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
# View current config
|
|
63
|
+
drive config --list
|
|
64
|
+
# or
|
|
65
|
+
drive config -l
|
|
51
66
|
```
|
|
52
67
|
|
|
53
|
-
###
|
|
68
|
+
### File Operations
|
|
54
69
|
|
|
55
|
-
|
|
70
|
+
#### Upload
|
|
56
71
|
|
|
57
72
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
# Upload single file
|
|
74
|
+
drive upload ./file.txt
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
# Upload to specific folder
|
|
77
|
+
drive upload ./file.txt /backups/
|
|
62
78
|
|
|
63
|
-
|
|
79
|
+
# Upload directory recursively
|
|
80
|
+
drive upload ./myproject /backups/ -r
|
|
64
81
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# or
|
|
68
|
-
d-drive ls
|
|
82
|
+
# Upload with encryption
|
|
83
|
+
drive upload ./sensitive.txt -e
|
|
69
84
|
```
|
|
70
85
|
|
|
71
|
-
|
|
86
|
+
#### Download
|
|
72
87
|
|
|
73
88
|
```bash
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
Long format with details:
|
|
89
|
+
# Download file
|
|
90
|
+
drive download /backups/file.txt
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
# Download to specific location
|
|
93
|
+
drive download /backups/file.txt ./local-file.txt
|
|
81
94
|
```
|
|
82
95
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Delete a file:
|
|
96
|
+
#### List
|
|
86
97
|
|
|
87
98
|
```bash
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
# List root directory
|
|
100
|
+
drive ls
|
|
90
101
|
|
|
91
|
-
|
|
102
|
+
# List specific directory
|
|
103
|
+
drive ls /backups
|
|
92
104
|
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
# Long format with details
|
|
106
|
+
drive ls -l
|
|
107
|
+
drive ls /backups -l
|
|
95
108
|
```
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
### Automated Backups
|
|
100
|
-
|
|
101
|
-
Create a backup script:
|
|
110
|
+
#### Delete
|
|
102
111
|
|
|
103
112
|
```bash
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# Backup database
|
|
108
|
-
pg_dump mydb > /tmp/backup.sql
|
|
109
|
-
d-drive upload /tmp/backup.sql /backups/database/backup-$(date +%Y%m%d).sql
|
|
113
|
+
# Delete file (with confirmation)
|
|
114
|
+
drive rm /old-file.txt
|
|
110
115
|
|
|
111
|
-
#
|
|
112
|
-
|
|
116
|
+
# Force delete without confirmation
|
|
117
|
+
drive rm /old-file.txt -f
|
|
113
118
|
|
|
114
|
-
#
|
|
115
|
-
rm /
|
|
119
|
+
# Delete directory recursively
|
|
120
|
+
drive rm /old-folder -r
|
|
116
121
|
```
|
|
117
122
|
|
|
118
|
-
|
|
123
|
+
#### Copy
|
|
119
124
|
|
|
120
125
|
```bash
|
|
121
|
-
|
|
126
|
+
# Create a copy of a file
|
|
127
|
+
drive cp /backups/file.txt
|
|
128
|
+
# Creates: /backups/file (1).txt
|
|
122
129
|
```
|
|
123
130
|
|
|
124
|
-
###
|
|
131
|
+
### Task Management
|
|
125
132
|
|
|
126
|
-
|
|
133
|
+
D-Drive supports SFTP backup tasks that can be managed via CLI.
|
|
127
134
|
|
|
128
|
-
```
|
|
129
|
-
#
|
|
130
|
-
|
|
131
|
-
run: |
|
|
132
|
-
npm run build
|
|
133
|
-
d-drive upload ./dist /releases/${{ github.sha }}/ -r
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Options
|
|
135
|
+
```bash
|
|
136
|
+
# List all tasks
|
|
137
|
+
drive tasks ls
|
|
137
138
|
|
|
138
|
-
|
|
139
|
+
# Run a task immediately
|
|
140
|
+
drive tasks run <task-id>
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
# Stop a running task
|
|
143
|
+
drive tasks stop <task-id>
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
# Enable/disable a task
|
|
146
|
+
drive tasks enable <task-id>
|
|
147
|
+
drive tasks disable <task-id>
|
|
144
148
|
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
# Delete a task
|
|
150
|
+
drive tasks rm <task-id>
|
|
151
|
+
drive tasks rm <task-id> -f # Force delete
|
|
152
|
+
```
|
|
147
153
|
|
|
148
|
-
###
|
|
154
|
+
### Info & Status
|
|
149
155
|
|
|
150
|
-
|
|
156
|
+
```bash
|
|
157
|
+
# Show connection status and user info
|
|
158
|
+
drive info
|
|
159
|
+
```
|
|
151
160
|
|
|
152
|
-
|
|
161
|
+
Output:
|
|
162
|
+
```
|
|
163
|
+
Connection Status:
|
|
164
|
+
─────────────────────────────────
|
|
165
|
+
API URL: https://your-server/api
|
|
166
|
+
API Key: ✓ Configured
|
|
167
|
+
Status: ✓ Connected
|
|
168
|
+
User: YourUsername
|
|
169
|
+
─────────────────────────────────
|
|
170
|
+
```
|
|
153
171
|
|
|
154
|
-
|
|
172
|
+
### Interactive Mode
|
|
155
173
|
|
|
156
|
-
|
|
174
|
+
```bash
|
|
175
|
+
# Start interactive mode
|
|
176
|
+
drive interactive
|
|
177
|
+
# or
|
|
178
|
+
drive i
|
|
179
|
+
```
|
|
157
180
|
|
|
158
|
-
|
|
159
|
-
- `-r, --recursive` - Delete directory recursively
|
|
181
|
+
Interactive mode provides a menu-driven interface for all operations.
|
|
160
182
|
|
|
161
|
-
|
|
183
|
+
## Getting Your API Key
|
|
162
184
|
|
|
163
|
-
|
|
185
|
+
1. Open D-Drive web interface
|
|
186
|
+
2. Go to **Settings** (gear icon)
|
|
187
|
+
3. Scroll to **API Keys** section
|
|
188
|
+
4. Click **Create API Key**
|
|
189
|
+
5. Copy the key (starts with `dd_`)
|
|
190
|
+
6. Use in CLI: `drive config --key dd_your_key`
|
|
164
191
|
|
|
165
|
-
|
|
192
|
+
## Examples
|
|
166
193
|
|
|
167
|
-
###
|
|
194
|
+
### Backup a Project
|
|
168
195
|
|
|
169
196
|
```bash
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
d-drive tasks ls
|
|
173
|
-
```
|
|
197
|
+
# Create a backup of your project
|
|
198
|
+
drive upload ./my-project /backups/my-project/ -r
|
|
174
199
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
d-drive tasks run <taskId>
|
|
200
|
+
# List backups
|
|
201
|
+
drive ls /backups/my-project -l
|
|
179
202
|
```
|
|
180
203
|
|
|
181
|
-
###
|
|
204
|
+
### Automated Backup Script
|
|
182
205
|
|
|
183
206
|
```bash
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
### Enable/Disable a Task
|
|
207
|
+
#!/bin/bash
|
|
208
|
+
# backup.sh
|
|
188
209
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
DATE=$(date +%Y-%m-%d)
|
|
211
|
+
drive upload ./data "/backups/$DATE/"
|
|
212
|
+
echo "Backup completed: $DATE"
|
|
192
213
|
```
|
|
193
214
|
|
|
194
|
-
###
|
|
215
|
+
### Download and Restore
|
|
195
216
|
|
|
196
217
|
```bash
|
|
197
|
-
|
|
218
|
+
# Download latest backup
|
|
219
|
+
drive download /backups/2026-01-24/data.tar.gz ./restore/
|
|
198
220
|
|
|
199
|
-
#
|
|
200
|
-
|
|
221
|
+
# Extract
|
|
222
|
+
tar -xzf ./restore/data.tar.gz
|
|
201
223
|
```
|
|
202
224
|
|
|
203
|
-
|
|
225
|
+
## Environment Variables
|
|
204
226
|
|
|
205
|
-
|
|
206
|
-
# List all tasks with status
|
|
207
|
-
$ d-drive tasks list
|
|
208
|
-
● Daily Server Backup [RUNNING]
|
|
209
|
-
ID: abc123
|
|
210
|
-
Schedule: 0 2 * * *
|
|
211
|
-
SFTP: backupuser@backup.example.com:22/backups
|
|
212
|
-
Destination: /backups/server
|
|
213
|
-
Last Run: 1/20/2026, 2:00:00 AM (2m 15s)
|
|
214
|
-
|
|
215
|
-
# Run a backup task manually
|
|
216
|
-
$ d-drive tasks run abc123
|
|
227
|
+
You can also configure the CLI using environment variables:
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
```bash
|
|
230
|
+
export DDRIVE_API_KEY=dd_your_api_key
|
|
231
|
+
export DDRIVE_API_URL=https://your-server/api
|
|
220
232
|
```
|
|
221
233
|
|
|
222
|
-
|
|
234
|
+
## Troubleshooting
|
|
235
|
+
|
|
236
|
+
### "Cannot connect to server"
|
|
237
|
+
- Check your API URL is correct
|
|
238
|
+
- Ensure the server is running
|
|
239
|
+
- Verify network connectivity
|
|
240
|
+
|
|
241
|
+
### "Invalid API key"
|
|
242
|
+
- Generate a new API key in Settings
|
|
243
|
+
- Ensure the key starts with `dd_`
|
|
244
|
+
- Check for extra spaces or characters
|
|
245
|
+
|
|
246
|
+
### "Permission denied"
|
|
247
|
+
- Verify you're logged in with the correct account
|
|
248
|
+
- Check file/folder permissions in D-Drive
|
|
249
|
+
|
|
250
|
+
## Changelog
|
|
251
|
+
|
|
252
|
+
### v2.2.0
|
|
253
|
+
- Added `drive` command alias for easier use
|
|
254
|
+
- Interactive mode with menu-driven interface
|
|
255
|
+
- `drive info` command for connection status
|
|
256
|
+
- Interactive configuration wizard
|
|
257
|
+
- Better error messages
|
|
258
|
+
- Colorized output
|
|
259
|
+
- Added `cp` alias for copy command
|
|
260
|
+
- Added `up` and `dl` aliases
|
|
261
|
+
|
|
262
|
+
### v2.1.0
|
|
263
|
+
- Task management commands
|
|
264
|
+
- Encryption support
|
|
265
|
+
- Progress bars
|
|
266
|
+
|
|
267
|
+
### v2.0.0
|
|
268
|
+
- Initial release with upload/download/list/delete
|
|
223
269
|
|
|
224
270
|
## License
|
|
225
271
|
|
package/dist/commands/config.js
CHANGED
|
@@ -22,7 +22,7 @@ async function configCommand(options) {
|
|
|
22
22
|
try {
|
|
23
23
|
// Normalize and test the API key by calling the /auth/me endpoint
|
|
24
24
|
const config = (0, config_1.getConfig)();
|
|
25
|
-
const apiUrl = options.url || config.apiUrl || '
|
|
25
|
+
const apiUrl = options.url || config.apiUrl || 'https://localhost/api';
|
|
26
26
|
// Accept keys entered with or without the `dd_` prefix, and strip any accidental "Bearer " prefix
|
|
27
27
|
const rawKey = options.key.replace(/^Bearer\s+/i, '').trim();
|
|
28
28
|
const normalizedKey = rawKey.startsWith('dd_') ? rawKey : `dd_${rawKey}`;
|
package/dist/config.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
10
|
const config_1 = require("./commands/config");
|
|
10
11
|
const upload_1 = require("./commands/upload");
|
|
11
12
|
const download_1 = require("./commands/download");
|
|
@@ -13,30 +14,64 @@ const list_1 = require("./commands/list");
|
|
|
13
14
|
const delete_1 = require("./commands/delete");
|
|
14
15
|
const copy_1 = require("./commands/copy");
|
|
15
16
|
const tasks_1 = require("./commands/tasks");
|
|
17
|
+
const config_2 = require("./config");
|
|
16
18
|
const pkg = require('../package.json');
|
|
17
19
|
const program = new commander_1.Command();
|
|
20
|
+
// ASCII art banner
|
|
21
|
+
const banner = `
|
|
22
|
+
${chalk_1.default.cyan('╔═══════════════════════════════════════╗')}
|
|
23
|
+
${chalk_1.default.cyan('║')} ${chalk_1.default.bold.white('D-Drive CLI')} ${chalk_1.default.gray('v' + (pkg.version || '2.2.0'))} ${chalk_1.default.cyan('║')}
|
|
24
|
+
${chalk_1.default.cyan('║')} ${chalk_1.default.gray('Discord-based cloud storage')} ${chalk_1.default.cyan('║')}
|
|
25
|
+
${chalk_1.default.cyan('╚═══════════════════════════════════════╝')}
|
|
26
|
+
`;
|
|
18
27
|
program
|
|
19
28
|
.name('d-drive')
|
|
20
|
-
.description('D-Drive CLI - Discord-based cloud storage
|
|
21
|
-
.version(pkg.version || '
|
|
22
|
-
|
|
29
|
+
.description('D-Drive CLI - Discord-based cloud storage')
|
|
30
|
+
.version(pkg.version || '2.2.0')
|
|
31
|
+
.hook('preAction', (thisCommand) => {
|
|
32
|
+
// Show banner only for main commands, not subcommands
|
|
33
|
+
if (thisCommand.name() === 'd-drive' && !process.argv.includes('--help') && !process.argv.includes('-h')) {
|
|
34
|
+
// Don't show banner for quick commands
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
// Interactive mode when no command is provided
|
|
38
|
+
program
|
|
39
|
+
.command('interactive', { isDefault: false })
|
|
40
|
+
.alias('i')
|
|
41
|
+
.description('Start interactive mode')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
console.log(banner);
|
|
44
|
+
await interactiveMode();
|
|
45
|
+
});
|
|
46
|
+
// Config command with interactive setup
|
|
23
47
|
program
|
|
24
48
|
.command('config')
|
|
25
49
|
.description('Configure D-Drive CLI')
|
|
26
50
|
.option('-k, --key <apiKey>', 'Set API key')
|
|
27
51
|
.option('-u, --url <url>', 'Set API URL')
|
|
28
52
|
.option('-l, --list', 'List current configuration')
|
|
29
|
-
.
|
|
53
|
+
.option('-i, --interactive', 'Interactive configuration')
|
|
54
|
+
.action(async (options) => {
|
|
55
|
+
if (options.interactive || (!options.key && !options.url && !options.list)) {
|
|
56
|
+
await interactiveConfig();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
await (0, config_1.configCommand)(options);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
30
62
|
// Upload command
|
|
31
63
|
program
|
|
32
64
|
.command('upload <source> [destination]')
|
|
65
|
+
.alias('up')
|
|
33
66
|
.description('Upload a file or directory to D-Drive')
|
|
34
67
|
.option('-r, --recursive', 'Upload directory recursively')
|
|
68
|
+
.option('-e, --encrypt', 'Encrypt the file')
|
|
35
69
|
.option('--no-progress', 'Disable progress bar')
|
|
36
70
|
.action(upload_1.uploadCommand);
|
|
37
71
|
// Download command
|
|
38
72
|
program
|
|
39
73
|
.command('download <source> [destination]')
|
|
74
|
+
.alias('dl')
|
|
40
75
|
.description('Download a file from D-Drive')
|
|
41
76
|
.option('--no-progress', 'Disable progress bar')
|
|
42
77
|
.action(download_1.downloadCommand);
|
|
@@ -46,6 +81,7 @@ program
|
|
|
46
81
|
.alias('ls')
|
|
47
82
|
.description('List files in D-Drive')
|
|
48
83
|
.option('-l, --long', 'Use long listing format')
|
|
84
|
+
.option('-a, --all', 'Include hidden files')
|
|
49
85
|
.action(list_1.listCommand);
|
|
50
86
|
// Delete command
|
|
51
87
|
program
|
|
@@ -58,12 +94,49 @@ program
|
|
|
58
94
|
// Copy command
|
|
59
95
|
program
|
|
60
96
|
.command('copy <path>')
|
|
97
|
+
.alias('cp')
|
|
61
98
|
.description('Make a copy of a file in-place (auto-numbered)')
|
|
62
99
|
.action(copy_1.copyCommand);
|
|
100
|
+
// Info command
|
|
101
|
+
program
|
|
102
|
+
.command('info')
|
|
103
|
+
.description('Show D-Drive connection info and status')
|
|
104
|
+
.action(async () => {
|
|
105
|
+
const config = (0, config_2.getConfig)();
|
|
106
|
+
console.log(banner);
|
|
107
|
+
console.log(chalk_1.default.bold('Connection Status:'));
|
|
108
|
+
console.log(chalk_1.default.gray('─────────────────────────────────'));
|
|
109
|
+
console.log(`${chalk_1.default.gray('API URL:')} ${config.apiUrl ? chalk_1.default.cyan(config.apiUrl) : chalk_1.default.red('Not configured')}`);
|
|
110
|
+
console.log(`${chalk_1.default.gray('API Key:')} ${config.apiKey ? chalk_1.default.green('✓ Configured') : chalk_1.default.red('✗ Not configured')}`);
|
|
111
|
+
if (config.apiKey && config.apiUrl) {
|
|
112
|
+
const axios = require('axios');
|
|
113
|
+
try {
|
|
114
|
+
const response = await axios.get(`${config.apiUrl}/auth/me`, {
|
|
115
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
116
|
+
timeout: 5000,
|
|
117
|
+
});
|
|
118
|
+
console.log(`${chalk_1.default.gray('Status:')} ${chalk_1.default.green('✓ Connected')}`);
|
|
119
|
+
console.log(`${chalk_1.default.gray('User:')} ${chalk_1.default.white(response.data.username || response.data.id)}`);
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
if (e.code === 'ECONNREFUSED') {
|
|
123
|
+
console.log(`${chalk_1.default.gray('Status:')} ${chalk_1.default.red('✗ Cannot connect to server')}`);
|
|
124
|
+
}
|
|
125
|
+
else if (e.response?.status === 401) {
|
|
126
|
+
console.log(`${chalk_1.default.gray('Status:')} ${chalk_1.default.red('✗ Invalid API key')}`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.log(`${chalk_1.default.gray('Status:')} ${chalk_1.default.yellow('? Unable to verify')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
console.log(chalk_1.default.gray('─────────────────────────────────'));
|
|
134
|
+
console.log(chalk_1.default.gray(`Run ${chalk_1.default.white('drive config')} to configure`));
|
|
135
|
+
});
|
|
63
136
|
// Tasks commands
|
|
64
137
|
const tasks = program
|
|
65
138
|
.command('tasks')
|
|
66
|
-
.description('Manage backup tasks');
|
|
139
|
+
.description('Manage SFTP backup tasks');
|
|
67
140
|
tasks
|
|
68
141
|
.command('list')
|
|
69
142
|
.alias('ls')
|
|
@@ -91,23 +164,133 @@ tasks
|
|
|
91
164
|
.command('disable <taskId>')
|
|
92
165
|
.description('Disable a task')
|
|
93
166
|
.action((taskId) => (0, tasks_1.taskEnableCommand)(taskId, false));
|
|
94
|
-
// Help
|
|
167
|
+
// Help with examples
|
|
95
168
|
program.on('--help', () => {
|
|
96
169
|
console.log('');
|
|
97
|
-
console.log(chalk_1.default.bold('
|
|
98
|
-
console.log('
|
|
99
|
-
console.log(
|
|
100
|
-
console.log(
|
|
101
|
-
console.log('
|
|
102
|
-
console.log('
|
|
103
|
-
console.log(
|
|
170
|
+
console.log(chalk_1.default.bold('Quick Start:'));
|
|
171
|
+
console.log(chalk_1.default.gray('────────────────────────────────────────'));
|
|
172
|
+
console.log(` ${chalk_1.default.cyan('$')} drive config ${chalk_1.default.gray('# Interactive setup')}`);
|
|
173
|
+
console.log(` ${chalk_1.default.cyan('$')} drive config --key <KEY> ${chalk_1.default.gray('# Set API key')}`);
|
|
174
|
+
console.log('');
|
|
175
|
+
console.log(chalk_1.default.bold('File Operations:'));
|
|
176
|
+
console.log(chalk_1.default.gray('────────────────────────────────────────'));
|
|
177
|
+
console.log(` ${chalk_1.default.cyan('$')} drive upload ./file.txt`);
|
|
178
|
+
console.log(` ${chalk_1.default.cyan('$')} drive upload ./folder -r ${chalk_1.default.gray('# Recursive')}`);
|
|
179
|
+
console.log(` ${chalk_1.default.cyan('$')} drive download /file.txt`);
|
|
180
|
+
console.log(` ${chalk_1.default.cyan('$')} drive ls /backups`);
|
|
181
|
+
console.log(` ${chalk_1.default.cyan('$')} drive rm /old-file.txt`);
|
|
182
|
+
console.log('');
|
|
183
|
+
console.log(chalk_1.default.bold('Task Management:'));
|
|
184
|
+
console.log(chalk_1.default.gray('────────────────────────────────────────'));
|
|
185
|
+
console.log(` ${chalk_1.default.cyan('$')} drive tasks ls ${chalk_1.default.gray('# List tasks')}`);
|
|
186
|
+
console.log(` ${chalk_1.default.cyan('$')} drive tasks run <id> ${chalk_1.default.gray('# Run task')}`);
|
|
187
|
+
console.log(` ${chalk_1.default.cyan('$')} drive tasks stop <id> ${chalk_1.default.gray('# Stop task')}`);
|
|
104
188
|
console.log('');
|
|
105
|
-
console.log(chalk_1.default.
|
|
106
|
-
console.log(' $ d-drive tasks list List all backup tasks');
|
|
107
|
-
console.log(' $ d-drive tasks run <taskId> Run a task immediately');
|
|
108
|
-
console.log(' $ d-drive tasks stop <taskId> Stop a running task');
|
|
109
|
-
console.log(' $ d-drive tasks enable <taskId> Enable a task');
|
|
110
|
-
console.log(' $ d-drive tasks disable <taskId> Disable a task');
|
|
111
|
-
console.log(' $ d-drive tasks delete <taskId> Delete a task');
|
|
189
|
+
console.log(chalk_1.default.gray('Documentation: https://github.com/jasonzli-DEV/D-Drive'));
|
|
112
190
|
});
|
|
191
|
+
// Interactive mode function
|
|
192
|
+
async function interactiveMode() {
|
|
193
|
+
const config = (0, config_2.getConfig)();
|
|
194
|
+
if (!config.apiKey) {
|
|
195
|
+
console.log(chalk_1.default.yellow('No API key configured. Let\'s set one up!\n'));
|
|
196
|
+
await interactiveConfig();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const choices = [
|
|
200
|
+
{ name: '📂 List files', value: 'list' },
|
|
201
|
+
{ name: '⬆️ Upload file', value: 'upload' },
|
|
202
|
+
{ name: '⬇️ Download file', value: 'download' },
|
|
203
|
+
{ name: '🗑️ Delete file', value: 'delete' },
|
|
204
|
+
{ name: '📋 List tasks', value: 'tasks' },
|
|
205
|
+
{ name: '⚙️ Configuration', value: 'config' },
|
|
206
|
+
{ name: '❌ Exit', value: 'exit' },
|
|
207
|
+
];
|
|
208
|
+
const { action } = await inquirer_1.default.prompt([
|
|
209
|
+
{
|
|
210
|
+
type: 'list',
|
|
211
|
+
name: 'action',
|
|
212
|
+
message: 'What would you like to do?',
|
|
213
|
+
choices,
|
|
214
|
+
},
|
|
215
|
+
]);
|
|
216
|
+
switch (action) {
|
|
217
|
+
case 'list':
|
|
218
|
+
const { path } = await inquirer_1.default.prompt([
|
|
219
|
+
{ type: 'input', name: 'path', message: 'Path (leave empty for root):', default: '/' },
|
|
220
|
+
]);
|
|
221
|
+
await (0, list_1.listCommand)(path === '/' ? undefined : path, { long: true });
|
|
222
|
+
break;
|
|
223
|
+
case 'upload':
|
|
224
|
+
const { source, dest } = await inquirer_1.default.prompt([
|
|
225
|
+
{ type: 'input', name: 'source', message: 'Local file path:' },
|
|
226
|
+
{ type: 'input', name: 'dest', message: 'Remote destination (optional):' },
|
|
227
|
+
]);
|
|
228
|
+
await (0, upload_1.uploadCommand)(source, dest || undefined, { recursive: false, progress: true });
|
|
229
|
+
break;
|
|
230
|
+
case 'download':
|
|
231
|
+
const { remotePath, localPath } = await inquirer_1.default.prompt([
|
|
232
|
+
{ type: 'input', name: 'remotePath', message: 'Remote file path:' },
|
|
233
|
+
{ type: 'input', name: 'localPath', message: 'Local destination (optional):' },
|
|
234
|
+
]);
|
|
235
|
+
await (0, download_1.downloadCommand)(remotePath, localPath || undefined, { progress: true });
|
|
236
|
+
break;
|
|
237
|
+
case 'delete':
|
|
238
|
+
const { deletePath, confirm } = await inquirer_1.default.prompt([
|
|
239
|
+
{ type: 'input', name: 'deletePath', message: 'Path to delete:' },
|
|
240
|
+
{ type: 'confirm', name: 'confirm', message: 'Are you sure?', default: false },
|
|
241
|
+
]);
|
|
242
|
+
if (confirm) {
|
|
243
|
+
await (0, delete_1.deleteCommand)(deletePath, { recursive: false, force: true });
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
case 'tasks':
|
|
247
|
+
await (0, tasks_1.tasksListCommand)();
|
|
248
|
+
break;
|
|
249
|
+
case 'config':
|
|
250
|
+
await interactiveConfig();
|
|
251
|
+
break;
|
|
252
|
+
case 'exit':
|
|
253
|
+
console.log(chalk_1.default.gray('Goodbye! 👋'));
|
|
254
|
+
process.exit(0);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Interactive configuration
|
|
258
|
+
async function interactiveConfig() {
|
|
259
|
+
const config = (0, config_2.getConfig)();
|
|
260
|
+
console.log(chalk_1.default.bold('\n📋 D-Drive Configuration\n'));
|
|
261
|
+
const answers = await inquirer_1.default.prompt([
|
|
262
|
+
{
|
|
263
|
+
type: 'input',
|
|
264
|
+
name: 'apiUrl',
|
|
265
|
+
message: 'API URL:',
|
|
266
|
+
default: config.apiUrl || 'https://your-server/api',
|
|
267
|
+
validate: (input) => {
|
|
268
|
+
if (!input.startsWith('http://') && !input.startsWith('https://')) {
|
|
269
|
+
return 'URL must start with http:// or https://';
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
type: 'input',
|
|
276
|
+
name: 'apiKey',
|
|
277
|
+
message: 'API Key (from Settings → API Keys):',
|
|
278
|
+
validate: (input) => {
|
|
279
|
+
if (!input || input.trim().length === 0) {
|
|
280
|
+
return 'API key is required';
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
]);
|
|
286
|
+
await (0, config_1.configCommand)({
|
|
287
|
+
key: answers.apiKey,
|
|
288
|
+
url: answers.apiUrl,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
// Handle no arguments - show help (but not for --version or --help)
|
|
292
|
+
if (process.argv.length === 2 && !process.argv.includes('-v') && !process.argv.includes('--version') && !process.argv.includes('-h') && !process.argv.includes('--help')) {
|
|
293
|
+
console.log(banner);
|
|
294
|
+
program.outputHelp();
|
|
295
|
+
}
|
|
113
296
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "d-drive-cli",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "D-Drive CLI tool for developers
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "D-Drive CLI tool for developers - Discord cloud storage",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"d-drive": "./dist/index.js"
|
|
7
|
+
"d-drive": "./dist/index.js",
|
|
8
|
+
"drive": "./dist/index.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsc",
|
package/src/commands/config.ts
CHANGED
|
@@ -25,7 +25,7 @@ export async function configCommand(options: ConfigOptions) {
|
|
|
25
25
|
try {
|
|
26
26
|
// Normalize and test the API key by calling the /auth/me endpoint
|
|
27
27
|
const config = getConfig();
|
|
28
|
-
const apiUrl = options.url || config.apiUrl || '
|
|
28
|
+
const apiUrl = options.url || config.apiUrl || 'https://localhost/api';
|
|
29
29
|
|
|
30
30
|
// Accept keys entered with or without the `dd_` prefix, and strip any accidental "Bearer " prefix
|
|
31
31
|
const rawKey = options.key.replace(/^Bearer\s+/i, '').trim();
|
package/src/config.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
+
import inquirer from 'inquirer';
|
|
5
6
|
import { configCommand } from './commands/config';
|
|
6
7
|
import { uploadCommand } from './commands/upload';
|
|
7
8
|
import { downloadCommand } from './commands/download';
|
|
@@ -9,35 +10,70 @@ import { listCommand } from './commands/list';
|
|
|
9
10
|
import { deleteCommand } from './commands/delete';
|
|
10
11
|
import { copyCommand } from './commands/copy';
|
|
11
12
|
import { tasksListCommand, taskRunCommand, taskStopCommand, taskDeleteCommand, taskEnableCommand } from './commands/tasks';
|
|
13
|
+
import { getConfig } from './config';
|
|
12
14
|
const pkg = require('../package.json');
|
|
13
15
|
|
|
14
16
|
const program = new Command();
|
|
15
17
|
|
|
18
|
+
// ASCII art banner
|
|
19
|
+
const banner = `
|
|
20
|
+
${chalk.cyan('╔═══════════════════════════════════════╗')}
|
|
21
|
+
${chalk.cyan('║')} ${chalk.bold.white('D-Drive CLI')} ${chalk.gray('v' + (pkg.version || '2.2.0'))} ${chalk.cyan('║')}
|
|
22
|
+
${chalk.cyan('║')} ${chalk.gray('Discord-based cloud storage')} ${chalk.cyan('║')}
|
|
23
|
+
${chalk.cyan('╚═══════════════════════════════════════╝')}
|
|
24
|
+
`;
|
|
25
|
+
|
|
16
26
|
program
|
|
17
27
|
.name('d-drive')
|
|
18
|
-
.description('D-Drive CLI - Discord-based cloud storage
|
|
19
|
-
.version(pkg.version || '
|
|
28
|
+
.description('D-Drive CLI - Discord-based cloud storage')
|
|
29
|
+
.version(pkg.version || '2.2.0')
|
|
30
|
+
.hook('preAction', (thisCommand) => {
|
|
31
|
+
// Show banner only for main commands, not subcommands
|
|
32
|
+
if (thisCommand.name() === 'd-drive' && !process.argv.includes('--help') && !process.argv.includes('-h')) {
|
|
33
|
+
// Don't show banner for quick commands
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Interactive mode when no command is provided
|
|
38
|
+
program
|
|
39
|
+
.command('interactive', { isDefault: false })
|
|
40
|
+
.alias('i')
|
|
41
|
+
.description('Start interactive mode')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
console.log(banner);
|
|
44
|
+
await interactiveMode();
|
|
45
|
+
});
|
|
20
46
|
|
|
21
|
-
// Config command
|
|
47
|
+
// Config command with interactive setup
|
|
22
48
|
program
|
|
23
49
|
.command('config')
|
|
24
50
|
.description('Configure D-Drive CLI')
|
|
25
51
|
.option('-k, --key <apiKey>', 'Set API key')
|
|
26
52
|
.option('-u, --url <url>', 'Set API URL')
|
|
27
53
|
.option('-l, --list', 'List current configuration')
|
|
28
|
-
.
|
|
54
|
+
.option('-i, --interactive', 'Interactive configuration')
|
|
55
|
+
.action(async (options) => {
|
|
56
|
+
if (options.interactive || (!options.key && !options.url && !options.list)) {
|
|
57
|
+
await interactiveConfig();
|
|
58
|
+
} else {
|
|
59
|
+
await configCommand(options);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
29
62
|
|
|
30
63
|
// Upload command
|
|
31
64
|
program
|
|
32
65
|
.command('upload <source> [destination]')
|
|
66
|
+
.alias('up')
|
|
33
67
|
.description('Upload a file or directory to D-Drive')
|
|
34
68
|
.option('-r, --recursive', 'Upload directory recursively')
|
|
69
|
+
.option('-e, --encrypt', 'Encrypt the file')
|
|
35
70
|
.option('--no-progress', 'Disable progress bar')
|
|
36
71
|
.action(uploadCommand);
|
|
37
72
|
|
|
38
73
|
// Download command
|
|
39
74
|
program
|
|
40
75
|
.command('download <source> [destination]')
|
|
76
|
+
.alias('dl')
|
|
41
77
|
.description('Download a file from D-Drive')
|
|
42
78
|
.option('--no-progress', 'Disable progress bar')
|
|
43
79
|
.action(downloadCommand);
|
|
@@ -48,6 +84,7 @@ program
|
|
|
48
84
|
.alias('ls')
|
|
49
85
|
.description('List files in D-Drive')
|
|
50
86
|
.option('-l, --long', 'Use long listing format')
|
|
87
|
+
.option('-a, --all', 'Include hidden files')
|
|
51
88
|
.action(listCommand);
|
|
52
89
|
|
|
53
90
|
// Delete command
|
|
@@ -62,13 +99,49 @@ program
|
|
|
62
99
|
// Copy command
|
|
63
100
|
program
|
|
64
101
|
.command('copy <path>')
|
|
102
|
+
.alias('cp')
|
|
65
103
|
.description('Make a copy of a file in-place (auto-numbered)')
|
|
66
104
|
.action(copyCommand);
|
|
67
105
|
|
|
106
|
+
// Info command
|
|
107
|
+
program
|
|
108
|
+
.command('info')
|
|
109
|
+
.description('Show D-Drive connection info and status')
|
|
110
|
+
.action(async () => {
|
|
111
|
+
const config = getConfig();
|
|
112
|
+
console.log(banner);
|
|
113
|
+
console.log(chalk.bold('Connection Status:'));
|
|
114
|
+
console.log(chalk.gray('─────────────────────────────────'));
|
|
115
|
+
console.log(`${chalk.gray('API URL:')} ${config.apiUrl ? chalk.cyan(config.apiUrl) : chalk.red('Not configured')}`);
|
|
116
|
+
console.log(`${chalk.gray('API Key:')} ${config.apiKey ? chalk.green('✓ Configured') : chalk.red('✗ Not configured')}`);
|
|
117
|
+
|
|
118
|
+
if (config.apiKey && config.apiUrl) {
|
|
119
|
+
const axios = require('axios');
|
|
120
|
+
try {
|
|
121
|
+
const response = await axios.get(`${config.apiUrl}/auth/me`, {
|
|
122
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
123
|
+
timeout: 5000,
|
|
124
|
+
});
|
|
125
|
+
console.log(`${chalk.gray('Status:')} ${chalk.green('✓ Connected')}`);
|
|
126
|
+
console.log(`${chalk.gray('User:')} ${chalk.white(response.data.username || response.data.id)}`);
|
|
127
|
+
} catch (e: any) {
|
|
128
|
+
if (e.code === 'ECONNREFUSED') {
|
|
129
|
+
console.log(`${chalk.gray('Status:')} ${chalk.red('✗ Cannot connect to server')}`);
|
|
130
|
+
} else if (e.response?.status === 401) {
|
|
131
|
+
console.log(`${chalk.gray('Status:')} ${chalk.red('✗ Invalid API key')}`);
|
|
132
|
+
} else {
|
|
133
|
+
console.log(`${chalk.gray('Status:')} ${chalk.yellow('? Unable to verify')}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
console.log(chalk.gray('─────────────────────────────────'));
|
|
138
|
+
console.log(chalk.gray(`Run ${chalk.white('drive config')} to configure`));
|
|
139
|
+
});
|
|
140
|
+
|
|
68
141
|
// Tasks commands
|
|
69
142
|
const tasks = program
|
|
70
143
|
.command('tasks')
|
|
71
|
-
.description('Manage backup tasks');
|
|
144
|
+
.description('Manage SFTP backup tasks');
|
|
72
145
|
|
|
73
146
|
tasks
|
|
74
147
|
.command('list')
|
|
@@ -103,24 +176,144 @@ tasks
|
|
|
103
176
|
.description('Disable a task')
|
|
104
177
|
.action((taskId: string) => taskEnableCommand(taskId, false));
|
|
105
178
|
|
|
106
|
-
// Help
|
|
179
|
+
// Help with examples
|
|
107
180
|
program.on('--help', () => {
|
|
108
181
|
console.log('');
|
|
109
|
-
console.log(chalk.bold('
|
|
110
|
-
console.log('
|
|
111
|
-
console.log(
|
|
112
|
-
console.log(
|
|
113
|
-
console.log('
|
|
114
|
-
console.log('
|
|
115
|
-
console.log(
|
|
182
|
+
console.log(chalk.bold('Quick Start:'));
|
|
183
|
+
console.log(chalk.gray('────────────────────────────────────────'));
|
|
184
|
+
console.log(` ${chalk.cyan('$')} drive config ${chalk.gray('# Interactive setup')}`);
|
|
185
|
+
console.log(` ${chalk.cyan('$')} drive config --key <KEY> ${chalk.gray('# Set API key')}`);
|
|
186
|
+
console.log('');
|
|
187
|
+
console.log(chalk.bold('File Operations:'));
|
|
188
|
+
console.log(chalk.gray('────────────────────────────────────────'));
|
|
189
|
+
console.log(` ${chalk.cyan('$')} drive upload ./file.txt`);
|
|
190
|
+
console.log(` ${chalk.cyan('$')} drive upload ./folder -r ${chalk.gray('# Recursive')}`);
|
|
191
|
+
console.log(` ${chalk.cyan('$')} drive download /file.txt`);
|
|
192
|
+
console.log(` ${chalk.cyan('$')} drive ls /backups`);
|
|
193
|
+
console.log(` ${chalk.cyan('$')} drive rm /old-file.txt`);
|
|
194
|
+
console.log('');
|
|
195
|
+
console.log(chalk.bold('Task Management:'));
|
|
196
|
+
console.log(chalk.gray('────────────────────────────────────────'));
|
|
197
|
+
console.log(` ${chalk.cyan('$')} drive tasks ls ${chalk.gray('# List tasks')}`);
|
|
198
|
+
console.log(` ${chalk.cyan('$')} drive tasks run <id> ${chalk.gray('# Run task')}`);
|
|
199
|
+
console.log(` ${chalk.cyan('$')} drive tasks stop <id> ${chalk.gray('# Stop task')}`);
|
|
116
200
|
console.log('');
|
|
117
|
-
console.log(chalk.
|
|
118
|
-
console.log(' $ d-drive tasks list List all backup tasks');
|
|
119
|
-
console.log(' $ d-drive tasks run <taskId> Run a task immediately');
|
|
120
|
-
console.log(' $ d-drive tasks stop <taskId> Stop a running task');
|
|
121
|
-
console.log(' $ d-drive tasks enable <taskId> Enable a task');
|
|
122
|
-
console.log(' $ d-drive tasks disable <taskId> Disable a task');
|
|
123
|
-
console.log(' $ d-drive tasks delete <taskId> Delete a task');
|
|
201
|
+
console.log(chalk.gray('Documentation: https://github.com/jasonzli-DEV/D-Drive'));
|
|
124
202
|
});
|
|
125
203
|
|
|
204
|
+
// Interactive mode function
|
|
205
|
+
async function interactiveMode() {
|
|
206
|
+
const config = getConfig();
|
|
207
|
+
|
|
208
|
+
if (!config.apiKey) {
|
|
209
|
+
console.log(chalk.yellow('No API key configured. Let\'s set one up!\n'));
|
|
210
|
+
await interactiveConfig();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const choices = [
|
|
215
|
+
{ name: '📂 List files', value: 'list' },
|
|
216
|
+
{ name: '⬆️ Upload file', value: 'upload' },
|
|
217
|
+
{ name: '⬇️ Download file', value: 'download' },
|
|
218
|
+
{ name: '🗑️ Delete file', value: 'delete' },
|
|
219
|
+
{ name: '📋 List tasks', value: 'tasks' },
|
|
220
|
+
{ name: '⚙️ Configuration', value: 'config' },
|
|
221
|
+
{ name: '❌ Exit', value: 'exit' },
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const { action } = await inquirer.prompt([
|
|
225
|
+
{
|
|
226
|
+
type: 'list',
|
|
227
|
+
name: 'action',
|
|
228
|
+
message: 'What would you like to do?',
|
|
229
|
+
choices,
|
|
230
|
+
},
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
switch (action) {
|
|
234
|
+
case 'list':
|
|
235
|
+
const { path } = await inquirer.prompt([
|
|
236
|
+
{ type: 'input', name: 'path', message: 'Path (leave empty for root):', default: '/' },
|
|
237
|
+
]);
|
|
238
|
+
await listCommand(path === '/' ? undefined : path, { long: true });
|
|
239
|
+
break;
|
|
240
|
+
case 'upload':
|
|
241
|
+
const { source, dest } = await inquirer.prompt([
|
|
242
|
+
{ type: 'input', name: 'source', message: 'Local file path:' },
|
|
243
|
+
{ type: 'input', name: 'dest', message: 'Remote destination (optional):' },
|
|
244
|
+
]);
|
|
245
|
+
await uploadCommand(source, dest || undefined, { recursive: false, progress: true });
|
|
246
|
+
break;
|
|
247
|
+
case 'download':
|
|
248
|
+
const { remotePath, localPath } = await inquirer.prompt([
|
|
249
|
+
{ type: 'input', name: 'remotePath', message: 'Remote file path:' },
|
|
250
|
+
{ type: 'input', name: 'localPath', message: 'Local destination (optional):' },
|
|
251
|
+
]);
|
|
252
|
+
await downloadCommand(remotePath, localPath || undefined, { progress: true });
|
|
253
|
+
break;
|
|
254
|
+
case 'delete':
|
|
255
|
+
const { deletePath, confirm } = await inquirer.prompt([
|
|
256
|
+
{ type: 'input', name: 'deletePath', message: 'Path to delete:' },
|
|
257
|
+
{ type: 'confirm', name: 'confirm', message: 'Are you sure?', default: false },
|
|
258
|
+
]);
|
|
259
|
+
if (confirm) {
|
|
260
|
+
await deleteCommand(deletePath, { recursive: false, force: true });
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case 'tasks':
|
|
264
|
+
await tasksListCommand();
|
|
265
|
+
break;
|
|
266
|
+
case 'config':
|
|
267
|
+
await interactiveConfig();
|
|
268
|
+
break;
|
|
269
|
+
case 'exit':
|
|
270
|
+
console.log(chalk.gray('Goodbye! 👋'));
|
|
271
|
+
process.exit(0);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Interactive configuration
|
|
276
|
+
async function interactiveConfig() {
|
|
277
|
+
const config = getConfig();
|
|
278
|
+
|
|
279
|
+
console.log(chalk.bold('\n📋 D-Drive Configuration\n'));
|
|
280
|
+
|
|
281
|
+
const answers = await inquirer.prompt([
|
|
282
|
+
{
|
|
283
|
+
type: 'input',
|
|
284
|
+
name: 'apiUrl',
|
|
285
|
+
message: 'API URL:',
|
|
286
|
+
default: config.apiUrl || 'https://your-server/api',
|
|
287
|
+
validate: (input: string) => {
|
|
288
|
+
if (!input.startsWith('http://') && !input.startsWith('https://')) {
|
|
289
|
+
return 'URL must start with http:// or https://';
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
type: 'input',
|
|
296
|
+
name: 'apiKey',
|
|
297
|
+
message: 'API Key (from Settings → API Keys):',
|
|
298
|
+
validate: (input: string) => {
|
|
299
|
+
if (!input || input.trim().length === 0) {
|
|
300
|
+
return 'API key is required';
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
await configCommand({
|
|
308
|
+
key: answers.apiKey,
|
|
309
|
+
url: answers.apiUrl,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Handle no arguments - show help (but not for --version or --help)
|
|
314
|
+
if (process.argv.length === 2 && !process.argv.includes('-v') && !process.argv.includes('--version') && !process.argv.includes('-h') && !process.argv.includes('--help')) {
|
|
315
|
+
console.log(banner);
|
|
316
|
+
program.outputHelp();
|
|
317
|
+
}
|
|
318
|
+
|
|
126
319
|
program.parse();
|