d-drive-cli 1.3.2 → 2.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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Command-line tool for D-Drive cloud storage.
4
4
 
5
+ **Version: 2.0.0 LTS**
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -156,6 +158,69 @@ Upload build artifacts from CI/CD:
156
158
  - `-f, --force` - Force deletion without confirmation
157
159
  - `-r, --recursive` - Delete directory recursively
158
160
 
161
+ ---
162
+
163
+ ## Task Management
164
+
165
+ D-Drive supports automated SFTP backup tasks. Use the CLI to manage them:
166
+
167
+ ### List Tasks
168
+
169
+ ```bash
170
+ d-drive tasks list
171
+ # or
172
+ d-drive tasks ls
173
+ ```
174
+
175
+ ### Run a Task Immediately
176
+
177
+ ```bash
178
+ d-drive tasks run <taskId>
179
+ ```
180
+
181
+ ### Stop a Running Task
182
+
183
+ ```bash
184
+ d-drive tasks stop <taskId>
185
+ ```
186
+
187
+ ### Enable/Disable a Task
188
+
189
+ ```bash
190
+ d-drive tasks enable <taskId>
191
+ d-drive tasks disable <taskId>
192
+ ```
193
+
194
+ ### Delete a Task
195
+
196
+ ```bash
197
+ d-drive tasks delete <taskId>
198
+
199
+ # Force delete without confirmation
200
+ d-drive tasks delete <taskId> -f
201
+ ```
202
+
203
+ ### Task Examples
204
+
205
+ ```bash
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
217
+
218
+ # Stop a running task
219
+ $ d-drive tasks stop abc123
220
+ ```
221
+
222
+ ---
223
+
159
224
  ## License
160
225
 
161
226
  MIT
@@ -0,0 +1,7 @@
1
+ export declare function tasksListCommand(): Promise<void>;
2
+ export declare function taskRunCommand(taskId: string): Promise<void>;
3
+ export declare function taskStopCommand(taskId: string): Promise<void>;
4
+ export declare function taskDeleteCommand(taskId: string, options: {
5
+ force?: boolean;
6
+ }): Promise<void>;
7
+ export declare function taskEnableCommand(taskId: string, enable: boolean): Promise<void>;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.tasksListCommand = tasksListCommand;
40
+ exports.taskRunCommand = taskRunCommand;
41
+ exports.taskStopCommand = taskStopCommand;
42
+ exports.taskDeleteCommand = taskDeleteCommand;
43
+ exports.taskEnableCommand = taskEnableCommand;
44
+ const chalk_1 = __importDefault(require("chalk"));
45
+ const ora_1 = __importDefault(require("ora"));
46
+ const api_1 = require("../api");
47
+ async function tasksListCommand() {
48
+ const spinner = (0, ora_1.default)('Fetching tasks...').start();
49
+ try {
50
+ const client = (0, api_1.createApiClient)();
51
+ const response = await client.get('/tasks');
52
+ const tasks = response.data;
53
+ spinner.stop();
54
+ if (tasks.length === 0) {
55
+ console.log(chalk_1.default.yellow('No tasks found.'));
56
+ return;
57
+ }
58
+ console.log(chalk_1.default.bold('\nBackup Tasks:\n'));
59
+ console.log(chalk_1.default.gray('─'.repeat(80)));
60
+ for (const task of tasks) {
61
+ const status = task.enabled ? chalk_1.default.green('●') : chalk_1.default.red('○');
62
+ const isRunning = task.lastStarted && (!task.lastRun || new Date(task.lastStarted) > new Date(task.lastRun));
63
+ const runStatus = isRunning ? chalk_1.default.blue(' [RUNNING]') : '';
64
+ console.log(`${status} ${chalk_1.default.bold(task.name)}${runStatus}`);
65
+ console.log(` ${chalk_1.default.gray('ID:')} ${task.id}`);
66
+ console.log(` ${chalk_1.default.gray('Schedule:')} ${task.cron}`);
67
+ console.log(` ${chalk_1.default.gray('SFTP:')} ${task.sftpUser}@${task.sftpHost}:${task.sftpPort}${task.sftpPath}`);
68
+ console.log(` ${chalk_1.default.gray('Destination:')} ${task.destinationPath || '/'}`);
69
+ console.log(` ${chalk_1.default.gray('Compress:')} ${task.compress} | ${chalk_1.default.gray('Max Files:')} ${task.maxFiles || 'unlimited'}`);
70
+ if (task.lastRun) {
71
+ const lastRun = new Date(task.lastRun).toLocaleString();
72
+ const runtime = task.lastRuntime ? ` (${formatRuntime(task.lastRuntime)})` : '';
73
+ console.log(` ${chalk_1.default.gray('Last Run:')} ${lastRun}${runtime}`);
74
+ }
75
+ console.log(chalk_1.default.gray('─'.repeat(80)));
76
+ }
77
+ console.log(chalk_1.default.gray(`\nTotal: ${tasks.length} task(s)`));
78
+ }
79
+ catch (error) {
80
+ spinner.fail('Failed to fetch tasks');
81
+ console.error(chalk_1.default.red(error.response?.data?.error || error.message));
82
+ process.exit(1);
83
+ }
84
+ }
85
+ async function taskRunCommand(taskId) {
86
+ const spinner = (0, ora_1.default)('Starting task...').start();
87
+ try {
88
+ const client = (0, api_1.createApiClient)();
89
+ const response = await client.post(`/tasks/${taskId}/run`);
90
+ spinner.succeed(chalk_1.default.green('Task started successfully!'));
91
+ console.log(chalk_1.default.gray(`Task ID: ${taskId}`));
92
+ }
93
+ catch (error) {
94
+ if (error.response?.status === 409) {
95
+ spinner.fail(chalk_1.default.yellow('Task is already running'));
96
+ }
97
+ else {
98
+ spinner.fail('Failed to start task');
99
+ }
100
+ console.error(chalk_1.default.red(error.response?.data?.error || error.message));
101
+ process.exit(1);
102
+ }
103
+ }
104
+ async function taskStopCommand(taskId) {
105
+ const spinner = (0, ora_1.default)('Stopping task...').start();
106
+ try {
107
+ const client = (0, api_1.createApiClient)();
108
+ await client.post(`/tasks/${taskId}/stop`);
109
+ spinner.succeed(chalk_1.default.green('Task stopped successfully!'));
110
+ }
111
+ catch (error) {
112
+ spinner.fail('Failed to stop task');
113
+ console.error(chalk_1.default.red(error.response?.data?.error || error.message));
114
+ process.exit(1);
115
+ }
116
+ }
117
+ async function taskDeleteCommand(taskId, options) {
118
+ try {
119
+ if (!options.force) {
120
+ const inquirer = await Promise.resolve().then(() => __importStar(require('inquirer')));
121
+ const { confirm } = await inquirer.default.prompt([
122
+ {
123
+ type: 'confirm',
124
+ name: 'confirm',
125
+ message: `Are you sure you want to delete task ${taskId}?`,
126
+ default: false,
127
+ },
128
+ ]);
129
+ if (!confirm) {
130
+ console.log(chalk_1.default.yellow('Cancelled.'));
131
+ return;
132
+ }
133
+ }
134
+ const spinner = (0, ora_1.default)('Deleting task...').start();
135
+ const client = (0, api_1.createApiClient)();
136
+ await client.delete(`/tasks/${taskId}`);
137
+ spinner.succeed(chalk_1.default.green('Task deleted successfully!'));
138
+ }
139
+ catch (error) {
140
+ console.error(chalk_1.default.red(error.response?.data?.error || error.message));
141
+ process.exit(1);
142
+ }
143
+ }
144
+ async function taskEnableCommand(taskId, enable) {
145
+ const action = enable ? 'Enabling' : 'Disabling';
146
+ const spinner = (0, ora_1.default)(`${action} task...`).start();
147
+ try {
148
+ const client = (0, api_1.createApiClient)();
149
+ await client.patch(`/tasks/${taskId}`, { enabled: enable });
150
+ spinner.succeed(chalk_1.default.green(`Task ${enable ? 'enabled' : 'disabled'} successfully!`));
151
+ }
152
+ catch (error) {
153
+ spinner.fail(`Failed to ${enable ? 'enable' : 'disable'} task`);
154
+ console.error(chalk_1.default.red(error.response?.data?.error || error.message));
155
+ process.exit(1);
156
+ }
157
+ }
158
+ function formatRuntime(seconds) {
159
+ if (seconds < 60)
160
+ return `${seconds}s`;
161
+ const mins = Math.round(seconds / 60);
162
+ if (mins < 60)
163
+ return `${mins}m`;
164
+ const hours = Math.floor(mins / 60);
165
+ const remainingMins = mins % 60;
166
+ return `${hours}h ${remainingMins}m`;
167
+ }
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ const download_1 = require("./commands/download");
12
12
  const list_1 = require("./commands/list");
13
13
  const delete_1 = require("./commands/delete");
14
14
  const copy_1 = require("./commands/copy");
15
+ const tasks_1 = require("./commands/tasks");
15
16
  const pkg = require('../package.json');
16
17
  const program = new commander_1.Command();
17
18
  program
@@ -59,6 +60,37 @@ program
59
60
  .command('copy <path>')
60
61
  .description('Make a copy of a file in-place (auto-numbered)')
61
62
  .action(copy_1.copyCommand);
63
+ // Tasks commands
64
+ const tasks = program
65
+ .command('tasks')
66
+ .description('Manage backup tasks');
67
+ tasks
68
+ .command('list')
69
+ .alias('ls')
70
+ .description('List all backup tasks')
71
+ .action(tasks_1.tasksListCommand);
72
+ tasks
73
+ .command('run <taskId>')
74
+ .description('Run a task immediately')
75
+ .action(tasks_1.taskRunCommand);
76
+ tasks
77
+ .command('stop <taskId>')
78
+ .description('Stop a running task')
79
+ .action(tasks_1.taskStopCommand);
80
+ tasks
81
+ .command('delete <taskId>')
82
+ .alias('rm')
83
+ .description('Delete a task')
84
+ .option('-f, --force', 'Force deletion without confirmation')
85
+ .action(tasks_1.taskDeleteCommand);
86
+ tasks
87
+ .command('enable <taskId>')
88
+ .description('Enable a task')
89
+ .action((taskId) => (0, tasks_1.taskEnableCommand)(taskId, true));
90
+ tasks
91
+ .command('disable <taskId>')
92
+ .description('Disable a task')
93
+ .action((taskId) => (0, tasks_1.taskEnableCommand)(taskId, false));
62
94
  // Help command
63
95
  program.on('--help', () => {
64
96
  console.log('');
@@ -69,5 +101,13 @@ program.on('--help', () => {
69
101
  console.log(' $ d-drive download /backups/file.txt ./restored.txt');
70
102
  console.log(' $ d-drive list /backups');
71
103
  console.log(' $ d-drive delete /backups/old-file.txt');
104
+ console.log('');
105
+ console.log(chalk_1.default.bold('Task Commands:'));
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');
72
112
  });
73
113
  program.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "d-drive-cli",
3
- "version": "1.3.2",
4
- "description": "D-Drive CLI tool for developers",
3
+ "version": "2.1.0",
4
+ "description": "D-Drive CLI tool for developers (LTS)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "d-drive": "./dist/index.js"
@@ -0,0 +1,162 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { createApiClient } from '../api';
4
+
5
+ interface Task {
6
+ id: string;
7
+ name: string;
8
+ enabled: boolean;
9
+ cron: string;
10
+ sftpHost: string;
11
+ sftpPort: number;
12
+ sftpUser: string;
13
+ sftpPath: string;
14
+ destinationPath?: string;
15
+ compress: string;
16
+ maxFiles: number;
17
+ lastRun?: string;
18
+ lastStarted?: string;
19
+ lastRuntime?: number;
20
+ }
21
+
22
+ export async function tasksListCommand() {
23
+ const spinner = ora('Fetching tasks...').start();
24
+
25
+ try {
26
+ const client = createApiClient();
27
+ const response = await client.get('/tasks');
28
+ const tasks: Task[] = response.data;
29
+
30
+ spinner.stop();
31
+
32
+ if (tasks.length === 0) {
33
+ console.log(chalk.yellow('No tasks found.'));
34
+ return;
35
+ }
36
+
37
+ console.log(chalk.bold('\nBackup Tasks:\n'));
38
+ console.log(chalk.gray('─'.repeat(80)));
39
+
40
+ for (const task of tasks) {
41
+ const status = task.enabled ? chalk.green('●') : chalk.red('○');
42
+ const isRunning = task.lastStarted && (!task.lastRun || new Date(task.lastStarted) > new Date(task.lastRun));
43
+ const runStatus = isRunning ? chalk.blue(' [RUNNING]') : '';
44
+
45
+ console.log(`${status} ${chalk.bold(task.name)}${runStatus}`);
46
+ console.log(` ${chalk.gray('ID:')} ${task.id}`);
47
+ console.log(` ${chalk.gray('Schedule:')} ${task.cron}`);
48
+ console.log(` ${chalk.gray('SFTP:')} ${task.sftpUser}@${task.sftpHost}:${task.sftpPort}${task.sftpPath}`);
49
+ console.log(` ${chalk.gray('Destination:')} ${task.destinationPath || '/'}`);
50
+ console.log(` ${chalk.gray('Compress:')} ${task.compress} | ${chalk.gray('Max Files:')} ${task.maxFiles || 'unlimited'}`);
51
+
52
+ if (task.lastRun) {
53
+ const lastRun = new Date(task.lastRun).toLocaleString();
54
+ const runtime = task.lastRuntime ? ` (${formatRuntime(task.lastRuntime)})` : '';
55
+ console.log(` ${chalk.gray('Last Run:')} ${lastRun}${runtime}`);
56
+ }
57
+
58
+ console.log(chalk.gray('─'.repeat(80)));
59
+ }
60
+
61
+ console.log(chalk.gray(`\nTotal: ${tasks.length} task(s)`));
62
+
63
+ } catch (error: any) {
64
+ spinner.fail('Failed to fetch tasks');
65
+ console.error(chalk.red(error.response?.data?.error || error.message));
66
+ process.exit(1);
67
+ }
68
+ }
69
+
70
+ export async function taskRunCommand(taskId: string) {
71
+ const spinner = ora('Starting task...').start();
72
+
73
+ try {
74
+ const client = createApiClient();
75
+ const response = await client.post(`/tasks/${taskId}/run`);
76
+
77
+ spinner.succeed(chalk.green('Task started successfully!'));
78
+ console.log(chalk.gray(`Task ID: ${taskId}`));
79
+
80
+ } catch (error: any) {
81
+ if (error.response?.status === 409) {
82
+ spinner.fail(chalk.yellow('Task is already running'));
83
+ } else {
84
+ spinner.fail('Failed to start task');
85
+ }
86
+ console.error(chalk.red(error.response?.data?.error || error.message));
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ export async function taskStopCommand(taskId: string) {
92
+ const spinner = ora('Stopping task...').start();
93
+
94
+ try {
95
+ const client = createApiClient();
96
+ await client.post(`/tasks/${taskId}/stop`);
97
+
98
+ spinner.succeed(chalk.green('Task stopped successfully!'));
99
+
100
+ } catch (error: any) {
101
+ spinner.fail('Failed to stop task');
102
+ console.error(chalk.red(error.response?.data?.error || error.message));
103
+ process.exit(1);
104
+ }
105
+ }
106
+
107
+ export async function taskDeleteCommand(taskId: string, options: { force?: boolean }) {
108
+ try {
109
+ if (!options.force) {
110
+ const inquirer = await import('inquirer');
111
+ const { confirm } = await inquirer.default.prompt([
112
+ {
113
+ type: 'confirm',
114
+ name: 'confirm',
115
+ message: `Are you sure you want to delete task ${taskId}?`,
116
+ default: false,
117
+ },
118
+ ]);
119
+
120
+ if (!confirm) {
121
+ console.log(chalk.yellow('Cancelled.'));
122
+ return;
123
+ }
124
+ }
125
+
126
+ const spinner = ora('Deleting task...').start();
127
+ const client = createApiClient();
128
+ await client.delete(`/tasks/${taskId}`);
129
+
130
+ spinner.succeed(chalk.green('Task deleted successfully!'));
131
+
132
+ } catch (error: any) {
133
+ console.error(chalk.red(error.response?.data?.error || error.message));
134
+ process.exit(1);
135
+ }
136
+ }
137
+
138
+ export async function taskEnableCommand(taskId: string, enable: boolean) {
139
+ const action = enable ? 'Enabling' : 'Disabling';
140
+ const spinner = ora(`${action} task...`).start();
141
+
142
+ try {
143
+ const client = createApiClient();
144
+ await client.patch(`/tasks/${taskId}`, { enabled: enable });
145
+
146
+ spinner.succeed(chalk.green(`Task ${enable ? 'enabled' : 'disabled'} successfully!`));
147
+
148
+ } catch (error: any) {
149
+ spinner.fail(`Failed to ${enable ? 'enable' : 'disable'} task`);
150
+ console.error(chalk.red(error.response?.data?.error || error.message));
151
+ process.exit(1);
152
+ }
153
+ }
154
+
155
+ function formatRuntime(seconds: number): string {
156
+ if (seconds < 60) return `${seconds}s`;
157
+ const mins = Math.round(seconds / 60);
158
+ if (mins < 60) return `${mins}m`;
159
+ const hours = Math.floor(mins / 60);
160
+ const remainingMins = mins % 60;
161
+ return `${hours}h ${remainingMins}m`;
162
+ }
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import { downloadCommand } from './commands/download';
8
8
  import { listCommand } from './commands/list';
9
9
  import { deleteCommand } from './commands/delete';
10
10
  import { copyCommand } from './commands/copy';
11
+ import { tasksListCommand, taskRunCommand, taskStopCommand, taskDeleteCommand, taskEnableCommand } from './commands/tasks';
11
12
  const pkg = require('../package.json');
12
13
 
13
14
  const program = new Command();
@@ -64,6 +65,44 @@ program
64
65
  .description('Make a copy of a file in-place (auto-numbered)')
65
66
  .action(copyCommand);
66
67
 
68
+ // Tasks commands
69
+ const tasks = program
70
+ .command('tasks')
71
+ .description('Manage backup tasks');
72
+
73
+ tasks
74
+ .command('list')
75
+ .alias('ls')
76
+ .description('List all backup tasks')
77
+ .action(tasksListCommand);
78
+
79
+ tasks
80
+ .command('run <taskId>')
81
+ .description('Run a task immediately')
82
+ .action(taskRunCommand);
83
+
84
+ tasks
85
+ .command('stop <taskId>')
86
+ .description('Stop a running task')
87
+ .action(taskStopCommand);
88
+
89
+ tasks
90
+ .command('delete <taskId>')
91
+ .alias('rm')
92
+ .description('Delete a task')
93
+ .option('-f, --force', 'Force deletion without confirmation')
94
+ .action(taskDeleteCommand);
95
+
96
+ tasks
97
+ .command('enable <taskId>')
98
+ .description('Enable a task')
99
+ .action((taskId: string) => taskEnableCommand(taskId, true));
100
+
101
+ tasks
102
+ .command('disable <taskId>')
103
+ .description('Disable a task')
104
+ .action((taskId: string) => taskEnableCommand(taskId, false));
105
+
67
106
  // Help command
68
107
  program.on('--help', () => {
69
108
  console.log('');
@@ -74,6 +113,14 @@ program.on('--help', () => {
74
113
  console.log(' $ d-drive download /backups/file.txt ./restored.txt');
75
114
  console.log(' $ d-drive list /backups');
76
115
  console.log(' $ d-drive delete /backups/old-file.txt');
116
+ console.log('');
117
+ console.log(chalk.bold('Task Commands:'));
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');
77
124
  });
78
125
 
79
126
  program.parse();