@targlobal/mission-control 1.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/.gitattributes +2 -0
- package/README.md +142 -0
- package/dist/api.d.ts +119 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +137 -0
- package/dist/api.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +50 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1703 -0
- package/dist/index.js.map +1 -0
- package/install.sh +124 -0
- package/package.json +48 -0
- package/src/api.ts +233 -0
- package/src/config.ts +50 -0
- package/src/index.ts +1793 -0
- package/tsconfig.json +19 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1703 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
44
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
45
|
+
const config_1 = require("./config");
|
|
46
|
+
const api_1 = require("./api");
|
|
47
|
+
const child_process_1 = require("child_process");
|
|
48
|
+
const VERSION = '1.2.0';
|
|
49
|
+
const program = new commander_1.Command();
|
|
50
|
+
// Set terminal title
|
|
51
|
+
const setTitle = (context) => {
|
|
52
|
+
process.stdout.write(`\x1b]0;MC | ${context}\x07`);
|
|
53
|
+
};
|
|
54
|
+
// ASCII Art Logo
|
|
55
|
+
const logo = `
|
|
56
|
+
${chalk_1.default.bold.white('███╗ ███╗ ██╗ ███████╗ ███████╗ ██╗ ██████╗ ███╗ ██╗')}
|
|
57
|
+
${chalk_1.default.bold.white('████╗ ████║ ██║ ██╔════╝ ██╔════╝ ██║ ██╔═══██╗ ████╗ ██║')}
|
|
58
|
+
${chalk_1.default.bold.white('██╔████╔██║ ██║ ███████╗ ███████╗ ██║ ██║ ██║ ██╔██╗ ██║')}
|
|
59
|
+
${chalk_1.default.bold.white('██║╚██╔╝██║ ██║ ╚════██║ ╚════██║ ██║ ██║ ██║ ██║╚██╗██║')}
|
|
60
|
+
${chalk_1.default.bold.white('██║ ╚═╝ ██║ ██║ ███████║ ███████║ ██║ ╚██████╔╝ ██║ ╚████║')}
|
|
61
|
+
${chalk_1.default.bold.white('╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝')}
|
|
62
|
+
|
|
63
|
+
${chalk_1.default.yellow('>>>')} ${chalk_1.default.bold.yellow('CONTROL')} ${chalk_1.default.dim('TAR Global Management')}
|
|
64
|
+
`;
|
|
65
|
+
const miniLogo = chalk_1.default.cyan('>>>') + chalk_1.default.bold.white(' Mission Control') + chalk_1.default.dim(' | TAR Global');
|
|
66
|
+
// Compact mode detection
|
|
67
|
+
const isCompact = () => {
|
|
68
|
+
const config = (0, config_1.getConfig)();
|
|
69
|
+
if (config.compact === 'on')
|
|
70
|
+
return true;
|
|
71
|
+
if (config.compact === 'off')
|
|
72
|
+
return false;
|
|
73
|
+
// Auto-detect based on terminal width
|
|
74
|
+
const width = process.stdout.columns || 80;
|
|
75
|
+
return width < 60;
|
|
76
|
+
};
|
|
77
|
+
const getTermWidth = () => process.stdout.columns || 80;
|
|
78
|
+
// Status bar showing user info
|
|
79
|
+
const showStatusBar = () => {
|
|
80
|
+
const user = (0, config_1.getUser)();
|
|
81
|
+
if (user) {
|
|
82
|
+
console.log(chalk_1.default.dim('─'.repeat(50)));
|
|
83
|
+
console.log(chalk_1.default.bold.white(` ${user.name}`) + chalk_1.default.dim(` | ${user.email}`));
|
|
84
|
+
console.log('');
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
// Fun completion messages (ASCII-safe)
|
|
88
|
+
const celebrations = [
|
|
89
|
+
chalk_1.default.green('*** Task completed! You\'re on fire! ***'),
|
|
90
|
+
chalk_1.default.green('*** Done! Another one bites the dust! ***'),
|
|
91
|
+
chalk_1.default.green('*** Shipped! To infinity and beyond! ***'),
|
|
92
|
+
chalk_1.default.green('*** Crushed it! Keep that momentum! ***'),
|
|
93
|
+
chalk_1.default.green('*** Victory! You\'re unstoppable! ***'),
|
|
94
|
+
chalk_1.default.green('*** Stellar work! Task obliterated! ***'),
|
|
95
|
+
chalk_1.default.green('*** Boom! Task destroyed! ***'),
|
|
96
|
+
chalk_1.default.green('*** Bullseye! Nailed it! ***'),
|
|
97
|
+
];
|
|
98
|
+
const getRandomCelebration = () => celebrations[Math.floor(Math.random() * celebrations.length)];
|
|
99
|
+
// Priority colors and icons (ASCII-safe for terminal compatibility)
|
|
100
|
+
const priorityStyle = {
|
|
101
|
+
critical: { icon: chalk_1.default.red('●'), color: chalk_1.default.red },
|
|
102
|
+
high: { icon: chalk_1.default.yellow('●'), color: chalk_1.default.yellow },
|
|
103
|
+
medium: { icon: chalk_1.default.blue('●'), color: chalk_1.default.blue },
|
|
104
|
+
low: { icon: chalk_1.default.green('●'), color: chalk_1.default.green },
|
|
105
|
+
};
|
|
106
|
+
// Type icons (ASCII-safe)
|
|
107
|
+
const typeIcons = {
|
|
108
|
+
bug: chalk_1.default.red('[BUG]'),
|
|
109
|
+
feature: chalk_1.default.green('[FTR]'),
|
|
110
|
+
improvement: chalk_1.default.cyan('[IMP]'),
|
|
111
|
+
task: chalk_1.default.white('[TSK]'),
|
|
112
|
+
escalation: chalk_1.default.magenta('[ESC]'),
|
|
113
|
+
};
|
|
114
|
+
// Helper to check auth
|
|
115
|
+
const requireAuth = () => {
|
|
116
|
+
if (!(0, config_1.isAuthenticated)()) {
|
|
117
|
+
console.log(chalk_1.default.red('\n[!] Not authenticated. Run `mc login` first.\n'));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
// Helper to parse task number (supports MC-1001 or just 1001)
|
|
122
|
+
const parseTaskNumber = (input) => {
|
|
123
|
+
return input.toUpperCase().replace('MC-', '');
|
|
124
|
+
};
|
|
125
|
+
// Helper to format date
|
|
126
|
+
const formatDate = (dateStr) => {
|
|
127
|
+
const date = new Date(dateStr);
|
|
128
|
+
const now = new Date();
|
|
129
|
+
const diffMs = now.getTime() - date.getTime();
|
|
130
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
131
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
132
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
133
|
+
if (diffMins < 1)
|
|
134
|
+
return 'just now';
|
|
135
|
+
if (diffMins < 60)
|
|
136
|
+
return `${diffMins}m ago`;
|
|
137
|
+
if (diffHours < 24)
|
|
138
|
+
return `${diffHours}h ago`;
|
|
139
|
+
if (diffDays < 7)
|
|
140
|
+
return `${diffDays}d ago`;
|
|
141
|
+
return date.toLocaleDateString();
|
|
142
|
+
};
|
|
143
|
+
// Fetch and store user info
|
|
144
|
+
const fetchUserInfo = async () => {
|
|
145
|
+
try {
|
|
146
|
+
const response = await api_1.api.getCurrentUser();
|
|
147
|
+
if (response.user) {
|
|
148
|
+
(0, config_1.setUser)({
|
|
149
|
+
name: response.user.display_name,
|
|
150
|
+
email: response.user.email,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
// Silently fail - user info is optional
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
program
|
|
159
|
+
.name('mc')
|
|
160
|
+
.description('Mission Control CLI - TAR Global Task Management')
|
|
161
|
+
.version(VERSION)
|
|
162
|
+
.addHelpText('before', logo);
|
|
163
|
+
// ==================== INTERACTIVE SHELL ====================
|
|
164
|
+
program
|
|
165
|
+
.command('shell')
|
|
166
|
+
.alias('i')
|
|
167
|
+
.description('Start interactive dashboard mode')
|
|
168
|
+
.action(async () => {
|
|
169
|
+
requireAuth();
|
|
170
|
+
await runInteractiveShell();
|
|
171
|
+
});
|
|
172
|
+
// Command functions for interactive mode
|
|
173
|
+
const listTasksCmd = async (options) => {
|
|
174
|
+
const spinner = (0, ora_1.default)('Loading tasks...').start();
|
|
175
|
+
try {
|
|
176
|
+
const data = options.all ? await api_1.api.listTasks({}) : await api_1.api.getMyTasks();
|
|
177
|
+
const tasks = data.tasks || [];
|
|
178
|
+
spinner.stop();
|
|
179
|
+
if (tasks.length === 0) {
|
|
180
|
+
console.log(chalk_1.default.dim('\n No tasks found\n'));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const compact = isCompact();
|
|
184
|
+
const table = new cli_table3_1.default({
|
|
185
|
+
head: compact
|
|
186
|
+
? [chalk_1.default.cyan('ID'), chalk_1.default.cyan('Title'), chalk_1.default.cyan('Pri')]
|
|
187
|
+
: [chalk_1.default.cyan('ID'), chalk_1.default.cyan('Title'), chalk_1.default.cyan('Priority'), chalk_1.default.cyan('Status'), chalk_1.default.cyan('Type')],
|
|
188
|
+
style: { head: [], border: [] },
|
|
189
|
+
colWidths: compact ? [8, 24, 6] : [10, 40, 12, 15, 8],
|
|
190
|
+
});
|
|
191
|
+
tasks.forEach((task) => {
|
|
192
|
+
const p = priorityStyle[task.priority] || priorityStyle.medium;
|
|
193
|
+
const titleLen = compact ? 21 : 37;
|
|
194
|
+
const title = task.title.length > titleLen ? task.title.substring(0, titleLen) + '...' : task.title;
|
|
195
|
+
if (compact) {
|
|
196
|
+
table.push([
|
|
197
|
+
chalk_1.default.dim(task.display_id.replace('MC-', '')),
|
|
198
|
+
title,
|
|
199
|
+
p.icon,
|
|
200
|
+
]);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
table.push([
|
|
204
|
+
chalk_1.default.white(task.display_id),
|
|
205
|
+
title,
|
|
206
|
+
p.icon + ' ' + p.color(task.priority),
|
|
207
|
+
task.column_name,
|
|
208
|
+
typeIcons[task.task_type] || chalk_1.default.white('[TSK]'),
|
|
209
|
+
]);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
console.log('\n' + table.toString() + '\n');
|
|
213
|
+
console.log(chalk_1.default.dim(` ${tasks.length} task(s) found\n`));
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
spinner.fail(chalk_1.default.red('Failed to load tasks'));
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const showTaskCmd = async (taskId) => {
|
|
220
|
+
const spinner = (0, ora_1.default)('Loading task...').start();
|
|
221
|
+
const compact = isCompact();
|
|
222
|
+
try {
|
|
223
|
+
const task = await api_1.api.getTask(parseTaskNumber(taskId));
|
|
224
|
+
spinner.stop();
|
|
225
|
+
const p = priorityStyle[task.priority] || priorityStyle.medium;
|
|
226
|
+
const typeIcon = typeIcons[task.task_type] || chalk_1.default.white('[TSK]');
|
|
227
|
+
if (compact) {
|
|
228
|
+
// Compact task view
|
|
229
|
+
console.log('');
|
|
230
|
+
console.log(chalk_1.default.bold.white(`${task.display_id}: ${task.title.substring(0, 28)}`));
|
|
231
|
+
console.log(chalk_1.default.dim('─'.repeat(30)));
|
|
232
|
+
console.log(`${typeIcon} ${p.icon} ${p.color(task.priority)} ${chalk_1.default.dim('|')} ${task.column_name}`);
|
|
233
|
+
console.log(chalk_1.default.dim(`Board: ${task.board_name}`));
|
|
234
|
+
console.log(chalk_1.default.dim(`Created: ${formatDate(task.created_at)}`));
|
|
235
|
+
if (task.assignees.length > 0) {
|
|
236
|
+
const names = task.assignees.map((a) => a.display_name.split(' ')[0]).join(', ');
|
|
237
|
+
console.log(chalk_1.default.dim(`Assigned: ${names.substring(0, 25)}`));
|
|
238
|
+
}
|
|
239
|
+
if (task.is_completed) {
|
|
240
|
+
console.log(chalk_1.default.green(`✓ Done ${formatDate(task.completed_at)}`));
|
|
241
|
+
}
|
|
242
|
+
if (task.description) {
|
|
243
|
+
console.log(chalk_1.default.dim('\n' + task.description.substring(0, 100)));
|
|
244
|
+
if (task.description.length > 100)
|
|
245
|
+
console.log(chalk_1.default.dim('...'));
|
|
246
|
+
}
|
|
247
|
+
console.log('');
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
console.log('\n' + chalk_1.default.bold.white('╭─────────────────────────────────────────────────────╮'));
|
|
251
|
+
console.log(chalk_1.default.bold.white(`│ ${task.display_id}: ${task.title.substring(0, 43).padEnd(43)} │`));
|
|
252
|
+
console.log(chalk_1.default.bold.white('├─────────────────────────────────────────────────────┤'));
|
|
253
|
+
console.log(`│ Type: ${typeIcon} ${task.task_type.padEnd(12)} Priority: ${p.icon} ${p.color(task.priority.padEnd(10))} │`);
|
|
254
|
+
console.log(`│ Board: ${task.board_name.padEnd(12)} Column: ${task.column_name.padEnd(15)} │`);
|
|
255
|
+
console.log(`│ Created: ${formatDate(task.created_at).padEnd(10)} by ${(task.created_by_name || 'Unknown').padEnd(20)} │`);
|
|
256
|
+
if (task.assignees.length > 0) {
|
|
257
|
+
const assigneeNames = task.assignees.map((a) => a.display_name).join(', ');
|
|
258
|
+
console.log(`│ Assignees: ${assigneeNames.substring(0, 40).padEnd(40)} │`);
|
|
259
|
+
}
|
|
260
|
+
if (task.is_completed) {
|
|
261
|
+
console.log(`│ ${chalk_1.default.green('[DONE]')} ${formatDate(task.completed_at).padEnd(41)} │`);
|
|
262
|
+
}
|
|
263
|
+
console.log(chalk_1.default.bold.white('╰─────────────────────────────────────────────────────╯'));
|
|
264
|
+
if (task.description) {
|
|
265
|
+
console.log(chalk_1.default.dim('\nDescription:'));
|
|
266
|
+
console.log(task.description.substring(0, 200));
|
|
267
|
+
if (task.description.length > 200)
|
|
268
|
+
console.log(chalk_1.default.dim('...'));
|
|
269
|
+
}
|
|
270
|
+
console.log('');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
spinner.fail(chalk_1.default.red('Failed to load task'));
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
const createTaskInteractive = async () => {
|
|
278
|
+
const boards = await api_1.api.listBoards();
|
|
279
|
+
const answers = await inquirer_1.default.prompt([
|
|
280
|
+
{ type: 'input', name: 'title', message: 'Task title:' },
|
|
281
|
+
{ type: 'input', name: 'description', message: 'Description (optional):' },
|
|
282
|
+
{
|
|
283
|
+
type: 'list',
|
|
284
|
+
name: 'board',
|
|
285
|
+
message: 'Board:',
|
|
286
|
+
choices: boards.boards.map((b) => ({ name: b.name, value: b.id })),
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
type: 'list',
|
|
290
|
+
name: 'priority',
|
|
291
|
+
message: 'Priority:',
|
|
292
|
+
choices: ['low', 'medium', 'high', 'critical'],
|
|
293
|
+
default: 'medium',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
type: 'list',
|
|
297
|
+
name: 'type',
|
|
298
|
+
message: 'Type:',
|
|
299
|
+
choices: ['task', 'bug', 'feature', 'improvement'],
|
|
300
|
+
default: 'task',
|
|
301
|
+
},
|
|
302
|
+
]);
|
|
303
|
+
const spinner = (0, ora_1.default)('Creating task...').start();
|
|
304
|
+
try {
|
|
305
|
+
const task = await api_1.api.createTask({
|
|
306
|
+
title: answers.title,
|
|
307
|
+
description: answers.description || undefined,
|
|
308
|
+
board: answers.board,
|
|
309
|
+
priority: answers.priority,
|
|
310
|
+
task_type: answers.type,
|
|
311
|
+
});
|
|
312
|
+
spinner.succeed(chalk_1.default.green(`Task created: ${task.display_id}`));
|
|
313
|
+
console.log(chalk_1.default.dim(` ${task.title}\n`));
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
spinner.fail(chalk_1.default.red('Failed to create task'));
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const startTaskCmd = async (taskId) => {
|
|
320
|
+
const spinner = (0, ora_1.default)('Starting task...').start();
|
|
321
|
+
try {
|
|
322
|
+
const task = await api_1.api.getTask(parseTaskNumber(taskId));
|
|
323
|
+
const boardData = await api_1.api.getBoard(task.board_name.toLowerCase().replace(' ', '-'));
|
|
324
|
+
const inProgressCol = boardData.columns.find((c) => c.name.toLowerCase().includes('progress'));
|
|
325
|
+
if (!inProgressCol) {
|
|
326
|
+
spinner.fail(chalk_1.default.red('Could not find In Progress column'));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
await api_1.api.moveTask(parseTaskNumber(taskId), inProgressCol.id);
|
|
330
|
+
spinner.succeed(chalk_1.default.green(`${task.display_id} moved to In Progress`));
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
spinner.fail(chalk_1.default.red('Failed to start task'));
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const completeTaskCmd = async (taskId) => {
|
|
337
|
+
const spinner = (0, ora_1.default)('Completing task...').start();
|
|
338
|
+
try {
|
|
339
|
+
const result = await api_1.api.completeTask(parseTaskNumber(taskId));
|
|
340
|
+
spinner.stop();
|
|
341
|
+
console.log('\n' + getRandomCelebration());
|
|
342
|
+
console.log(chalk_1.default.dim(` ${result.task.display_id}: ${result.task.title}`));
|
|
343
|
+
console.log(chalk_1.default.green('\n ╭────────────────────────────────╮'));
|
|
344
|
+
console.log(chalk_1.default.green(' │') + chalk_1.default.bold.yellow(' [*] TASK COMPLETED! [*] ') + chalk_1.default.green('│'));
|
|
345
|
+
console.log(chalk_1.default.green(' ╰────────────────────────────────╯\n'));
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
spinner.fail(chalk_1.default.red('Failed to complete task'));
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
const listBoardsCmd = async () => {
|
|
352
|
+
const spinner = (0, ora_1.default)('Loading boards...').start();
|
|
353
|
+
const compact = isCompact();
|
|
354
|
+
try {
|
|
355
|
+
const data = await api_1.api.listBoards();
|
|
356
|
+
spinner.stop();
|
|
357
|
+
if (compact) {
|
|
358
|
+
console.log(chalk_1.default.cyan('\n Boards:'));
|
|
359
|
+
data.boards.forEach((board) => {
|
|
360
|
+
console.log(` ${chalk_1.default.white(board.slug.padEnd(10))} ${chalk_1.default.dim(board.task_count + ' tasks')}`);
|
|
361
|
+
});
|
|
362
|
+
console.log('');
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
const table = new cli_table3_1.default({
|
|
366
|
+
head: [chalk_1.default.cyan('Slug'), chalk_1.default.cyan('Name'), chalk_1.default.cyan('Team'), chalk_1.default.cyan('Tasks')],
|
|
367
|
+
style: { head: [], border: [] },
|
|
368
|
+
});
|
|
369
|
+
data.boards.forEach((board) => {
|
|
370
|
+
table.push([board.slug, board.name, board.team, board.task_count.toString()]);
|
|
371
|
+
});
|
|
372
|
+
console.log('\n' + table.toString() + '\n');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
spinner.fail(chalk_1.default.red('Failed to load boards'));
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const showStatsCmd = async () => {
|
|
380
|
+
const spinner = (0, ora_1.default)('Loading stats...').start();
|
|
381
|
+
const compact = isCompact();
|
|
382
|
+
try {
|
|
383
|
+
const stats = await api_1.api.getStats();
|
|
384
|
+
spinner.stop();
|
|
385
|
+
if (compact) {
|
|
386
|
+
console.log(chalk_1.default.cyan('\n YOUR STATS'));
|
|
387
|
+
console.log(chalk_1.default.dim(' ──────────────────'));
|
|
388
|
+
console.log(` Tasks: ${chalk_1.default.bold.white(stats.my_tasks)}`);
|
|
389
|
+
console.log(` Done: ${chalk_1.default.bold.green(stats.my_completed)}`);
|
|
390
|
+
console.log(` Overdue: ${chalk_1.default.bold.red(stats.overdue_tasks)}`);
|
|
391
|
+
console.log(` Critical: ${chalk_1.default.bold.yellow(stats.critical_count)}`);
|
|
392
|
+
console.log(` High: ${chalk_1.default.bold.yellow(stats.high_count)}`);
|
|
393
|
+
console.log('');
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
console.log('\n' + miniLogo);
|
|
397
|
+
console.log(chalk_1.default.cyan('\n ╭──────────────────────────────╮'));
|
|
398
|
+
console.log(chalk_1.default.cyan(' │') + chalk_1.default.bold.white(' [=] YOUR STATS [=] ') + chalk_1.default.cyan('│'));
|
|
399
|
+
console.log(chalk_1.default.cyan(' ├──────────────────────────────┤'));
|
|
400
|
+
console.log(chalk_1.default.cyan(' │') + ` [T] My Tasks: ${chalk_1.default.bold.white(String(stats.my_tasks).padStart(6))} ` + chalk_1.default.cyan('│'));
|
|
401
|
+
console.log(chalk_1.default.cyan(' │') + ` [+] Completed: ${chalk_1.default.bold.green(String(stats.my_completed).padStart(6))} ` + chalk_1.default.cyan('│'));
|
|
402
|
+
console.log(chalk_1.default.cyan(' │') + ` [!] Overdue: ${chalk_1.default.bold.red(String(stats.overdue_tasks).padStart(6))} ` + chalk_1.default.cyan('│'));
|
|
403
|
+
console.log(chalk_1.default.cyan(' │') + ` ${chalk_1.default.red('●')} Critical: ${chalk_1.default.bold.yellow(String(stats.critical_count).padStart(6))} ` + chalk_1.default.cyan('│'));
|
|
404
|
+
console.log(chalk_1.default.cyan(' │') + ` ${chalk_1.default.yellow('●')} High: ${chalk_1.default.bold.yellow(String(stats.high_count).padStart(6))} ` + chalk_1.default.cyan('│'));
|
|
405
|
+
console.log(chalk_1.default.cyan(' ╰──────────────────────────────╯\n'));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
spinner.fail(chalk_1.default.red('Failed to load stats'));
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const showUrgentCmd = async () => {
|
|
413
|
+
const spinner = (0, ora_1.default)('Loading urgent tasks...').start();
|
|
414
|
+
try {
|
|
415
|
+
const data = await api_1.api.listTasks({ priority: 'critical' });
|
|
416
|
+
const tasks = data.tasks || [];
|
|
417
|
+
spinner.stop();
|
|
418
|
+
if (tasks.length === 0) {
|
|
419
|
+
console.log(chalk_1.default.green('\n [OK] No critical tasks! All clear.\n'));
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
console.log(chalk_1.default.red.bold(`\n [!!!] ${tasks.length} critical task(s) need attention:\n`));
|
|
423
|
+
tasks.forEach((task) => {
|
|
424
|
+
console.log(` ${chalk_1.default.red('●')} ${task.display_id}: ${task.title}`);
|
|
425
|
+
console.log(chalk_1.default.dim(` ${task.board_name} → ${task.column_name}`));
|
|
426
|
+
});
|
|
427
|
+
console.log('');
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
spinner.fail(chalk_1.default.red('Failed to load tasks'));
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const showWhoami = () => {
|
|
434
|
+
const config = (0, config_1.getConfig)();
|
|
435
|
+
const user = (0, config_1.getUser)();
|
|
436
|
+
const termWidth = getTermWidth();
|
|
437
|
+
console.log('\n' + chalk_1.default.bold(' Mission Control Config'));
|
|
438
|
+
console.log(chalk_1.default.dim(' ──────────────────────'));
|
|
439
|
+
if (user) {
|
|
440
|
+
console.log(` User: ${chalk_1.default.bold.white(user.name)}`);
|
|
441
|
+
console.log(` Email: ${user.email}`);
|
|
442
|
+
}
|
|
443
|
+
console.log(` API URL: ${config.apiUrl}`);
|
|
444
|
+
console.log(` Token: ${config.token ? chalk_1.default.green('●') + ' configured' : chalk_1.default.red('○') + ' not set'}`);
|
|
445
|
+
console.log(` Compact: ${chalk_1.default.cyan(config.compact)} ${chalk_1.default.dim(`(term: ${termWidth} cols)`)}`);
|
|
446
|
+
console.log(` Version: ${VERSION}`);
|
|
447
|
+
console.log('');
|
|
448
|
+
};
|
|
449
|
+
const showInteractiveHelp = () => {
|
|
450
|
+
const compact = isCompact();
|
|
451
|
+
if (compact) {
|
|
452
|
+
console.log(chalk_1.default.cyan('\n Commands:'));
|
|
453
|
+
console.log(chalk_1.default.dim(' ─────────────'));
|
|
454
|
+
console.log(' tasks/ls List tasks');
|
|
455
|
+
console.log(' show <id> Task details');
|
|
456
|
+
console.log(' new Create task');
|
|
457
|
+
console.log(' done <id> Complete');
|
|
458
|
+
console.log(' stats Statistics');
|
|
459
|
+
console.log(' payouts Payouts');
|
|
460
|
+
console.log(' config Settings');
|
|
461
|
+
console.log(' exit/q Quit');
|
|
462
|
+
console.log('');
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
console.log(chalk_1.default.cyan('\n Available Commands:'));
|
|
466
|
+
console.log(chalk_1.default.dim(' ──────────────────────'));
|
|
467
|
+
console.log(' tasks, ls List your tasks');
|
|
468
|
+
console.log(' tasks -a List all tasks');
|
|
469
|
+
console.log(' show <id> Show task details');
|
|
470
|
+
console.log(' new Create new task (interactive)');
|
|
471
|
+
console.log(' start <id> Move task to In Progress');
|
|
472
|
+
console.log(' done <id> Complete a task');
|
|
473
|
+
console.log(' boards List boards');
|
|
474
|
+
console.log(' stats Show your statistics');
|
|
475
|
+
console.log(' urgent Show critical tasks');
|
|
476
|
+
console.log(chalk_1.default.yellow(' payouts Payout processing dashboard'));
|
|
477
|
+
console.log(' whoami Show current user/config');
|
|
478
|
+
console.log(' config View/set config (compact mode)');
|
|
479
|
+
console.log(' clear Clear screen');
|
|
480
|
+
console.log(' help, ? Show this help');
|
|
481
|
+
console.log(' exit, quit, q Exit shell');
|
|
482
|
+
console.log('');
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
// ==================== INTERACTIVE DASHBOARD SHELL ====================
|
|
486
|
+
const showDashboard = async () => {
|
|
487
|
+
try {
|
|
488
|
+
const stats = await api_1.api.getStats();
|
|
489
|
+
const user = (0, config_1.getUser)();
|
|
490
|
+
const now = new Date();
|
|
491
|
+
const greeting = now.getHours() < 12 ? 'Good morning' : now.getHours() < 18 ? 'Good afternoon' : 'Good evening';
|
|
492
|
+
const compact = isCompact();
|
|
493
|
+
console.clear();
|
|
494
|
+
if (compact) {
|
|
495
|
+
// Compact mobile dashboard
|
|
496
|
+
console.log(miniLogo);
|
|
497
|
+
console.log('');
|
|
498
|
+
const firstName = (user?.name || 'Agent').split(' ')[0];
|
|
499
|
+
console.log(chalk_1.default.dim(`${greeting}, `) + chalk_1.default.bold.yellow(firstName));
|
|
500
|
+
console.log('');
|
|
501
|
+
console.log(chalk_1.default.cyan('┌─────────────────────────────┐'));
|
|
502
|
+
console.log(chalk_1.default.cyan('│') + ` ${chalk_1.default.bgCyan.black('TASKS')} ${chalk_1.default.bold.white(String(stats.my_tasks).padStart(3))} ${chalk_1.default.bgRed.white('CRIT')} ${chalk_1.default.bold.red(String(stats.critical_count).padStart(2))} ${chalk_1.default.bgYellow.black('OVR')} ${chalk_1.default.bold.yellow(String(stats.overdue_tasks).padStart(2))} ` + chalk_1.default.cyan('│'));
|
|
503
|
+
console.log(chalk_1.default.cyan('├─────────────────────────────┤'));
|
|
504
|
+
console.log(chalk_1.default.cyan('│') + ` ${chalk_1.default.cyan('[1]')}Tasks ${chalk_1.default.cyan('[2]')}New ${chalk_1.default.cyan('[3]')}Pay ${chalk_1.default.cyan('[4]')}Urg ` + chalk_1.default.cyan('│'));
|
|
505
|
+
console.log(chalk_1.default.cyan('└─────────────────────────────┘'));
|
|
506
|
+
console.log('');
|
|
507
|
+
// Show urgent count only in compact mode
|
|
508
|
+
if (stats.critical_count > 0 || stats.overdue_tasks > 0) {
|
|
509
|
+
console.log(chalk_1.default.red(`! ${stats.critical_count} critical task(s)`));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
// Full dashboard
|
|
514
|
+
const blinkDots = chalk_1.default.cyan('\x1b[5m●●●\x1b[0m'); // Blinking dots
|
|
515
|
+
console.log(logo);
|
|
516
|
+
console.log('');
|
|
517
|
+
console.log(chalk_1.default.cyan('╔══════════════════════════════════════════════════════════════════════╗'));
|
|
518
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.bold.white(greeting)}, ${chalk_1.default.bold.yellow((user?.name || 'Agent').substring(0, 35))}`.padEnd(60) + ` ${blinkDots} ` + chalk_1.default.cyan('║'));
|
|
519
|
+
console.log(chalk_1.default.cyan('╠══════════════════════════════════════════════════════════════════════╣'));
|
|
520
|
+
console.log(chalk_1.default.cyan('║') + ' ' + chalk_1.default.cyan('║'));
|
|
521
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.bgCyan.black(' MY TASKS ')} ${chalk_1.default.bgRed.white(' CRITICAL ')} ${chalk_1.default.bgYellow.black(' OVERDUE ')} ${chalk_1.default.bgGreen.black(' DONE ')} ` + chalk_1.default.cyan('║'));
|
|
522
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.bold.white(String(stats.my_tasks).padStart(3))} ${chalk_1.default.bold.red(String(stats.critical_count).padStart(3))} ${chalk_1.default.bold.yellow(String(stats.overdue_tasks).padStart(3))} ${chalk_1.default.bold.green(String(stats.my_completed).padStart(3))} ` + chalk_1.default.cyan('║'));
|
|
523
|
+
console.log(chalk_1.default.cyan('║') + ' ' + chalk_1.default.cyan('║'));
|
|
524
|
+
console.log(chalk_1.default.cyan('╠══════════════════════════════════════════════════════════════════════╣'));
|
|
525
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.dim(' Quick Actions: ') + chalk_1.default.cyan('║'));
|
|
526
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.cyan('[1]')} My Tasks ${chalk_1.default.cyan('[2]')} New Task ${chalk_1.default.cyan('[3]')} Payouts ${chalk_1.default.cyan('[4]')} Urgent ` + chalk_1.default.cyan('║'));
|
|
527
|
+
console.log(chalk_1.default.cyan('║') + ' ' + chalk_1.default.cyan('║'));
|
|
528
|
+
console.log(chalk_1.default.cyan('╚══════════════════════════════════════════════════════════════════════╝'));
|
|
529
|
+
console.log('');
|
|
530
|
+
// Show recent urgent tasks if any
|
|
531
|
+
if (stats.critical_count > 0 || stats.overdue_tasks > 0) {
|
|
532
|
+
console.log(chalk_1.default.red.bold(' ⚠️ ATTENTION REQUIRED:'));
|
|
533
|
+
const urgentData = await api_1.api.listTasks({ priority: 'critical' });
|
|
534
|
+
const urgentTasks = (urgentData.tasks || []).slice(0, 3);
|
|
535
|
+
urgentTasks.forEach((t) => {
|
|
536
|
+
console.log(` ${chalk_1.default.red('●')} ${chalk_1.default.dim(t.display_id)} ${t.title.substring(0, 45)}`);
|
|
537
|
+
});
|
|
538
|
+
console.log('');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
catch (e) {
|
|
543
|
+
// Silently fail - show basic prompt
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
const runInteractiveShell = async () => {
|
|
547
|
+
setTitle('Mission Control');
|
|
548
|
+
await showDashboard();
|
|
549
|
+
const runLoop = async () => {
|
|
550
|
+
const user = (0, config_1.getUser)();
|
|
551
|
+
const prompt = chalk_1.default.cyan('mc') + chalk_1.default.dim(` ${user?.name?.split(' ')[0] || ''}`) + chalk_1.default.cyan(' ❯ ');
|
|
552
|
+
const { command } = await inquirer_1.default.prompt([
|
|
553
|
+
{
|
|
554
|
+
type: 'input',
|
|
555
|
+
name: 'command',
|
|
556
|
+
message: prompt,
|
|
557
|
+
prefix: '',
|
|
558
|
+
},
|
|
559
|
+
]);
|
|
560
|
+
const cmd = command.trim().toLowerCase();
|
|
561
|
+
const parts = command.trim().split(/\s+/);
|
|
562
|
+
const mainCmd = parts[0]?.toLowerCase();
|
|
563
|
+
const args = parts.slice(1);
|
|
564
|
+
// Exit commands
|
|
565
|
+
if (cmd === 'exit' || cmd === 'quit' || cmd === 'q') {
|
|
566
|
+
console.log(chalk_1.default.dim('\n 👋 See you later!\n'));
|
|
567
|
+
process.exit(0);
|
|
568
|
+
}
|
|
569
|
+
if (cmd === '') {
|
|
570
|
+
return runLoop();
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
// Quick number shortcuts
|
|
574
|
+
if (cmd === '1') {
|
|
575
|
+
setTitle('My Tasks');
|
|
576
|
+
await listTasksCmd({});
|
|
577
|
+
}
|
|
578
|
+
else if (cmd === '2') {
|
|
579
|
+
setTitle('New Task');
|
|
580
|
+
await createTaskInteractive();
|
|
581
|
+
}
|
|
582
|
+
else if (cmd === '3') {
|
|
583
|
+
await runPayoutsShell();
|
|
584
|
+
await showDashboard();
|
|
585
|
+
}
|
|
586
|
+
else if (cmd === '4') {
|
|
587
|
+
setTitle('Urgent');
|
|
588
|
+
await showUrgentCmd();
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
// Regular commands
|
|
592
|
+
switch (mainCmd) {
|
|
593
|
+
case 'dashboard':
|
|
594
|
+
case 'home':
|
|
595
|
+
case 'h':
|
|
596
|
+
await showDashboard();
|
|
597
|
+
break;
|
|
598
|
+
case 'tasks':
|
|
599
|
+
case 'ls':
|
|
600
|
+
setTitle('Tasks');
|
|
601
|
+
await listTasksCmd({ all: args.includes('-a') || args.includes('--all') });
|
|
602
|
+
break;
|
|
603
|
+
case 'show':
|
|
604
|
+
case 'view':
|
|
605
|
+
if (args[0]) {
|
|
606
|
+
setTitle(`Task ${args[0]}`);
|
|
607
|
+
await showTaskCmd(args[0]);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
console.log(chalk_1.default.red(' Usage: show <taskId>\n'));
|
|
611
|
+
}
|
|
612
|
+
break;
|
|
613
|
+
case 'new':
|
|
614
|
+
case 'create':
|
|
615
|
+
setTitle('New Task');
|
|
616
|
+
await createTaskInteractive();
|
|
617
|
+
break;
|
|
618
|
+
case 'start':
|
|
619
|
+
if (args[0]) {
|
|
620
|
+
await startTaskCmd(args[0]);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
console.log(chalk_1.default.red(' Usage: start <taskId>\n'));
|
|
624
|
+
}
|
|
625
|
+
break;
|
|
626
|
+
case 'done':
|
|
627
|
+
case 'complete':
|
|
628
|
+
if (args[0]) {
|
|
629
|
+
await completeTaskCmd(args[0]);
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
console.log(chalk_1.default.red(' Usage: done <taskId>\n'));
|
|
633
|
+
}
|
|
634
|
+
break;
|
|
635
|
+
case 'boards':
|
|
636
|
+
setTitle('Boards');
|
|
637
|
+
await listBoardsCmd();
|
|
638
|
+
break;
|
|
639
|
+
case 'stats':
|
|
640
|
+
setTitle('Stats');
|
|
641
|
+
await showStatsCmd();
|
|
642
|
+
break;
|
|
643
|
+
case 'urgent':
|
|
644
|
+
setTitle('Urgent');
|
|
645
|
+
await showUrgentCmd();
|
|
646
|
+
break;
|
|
647
|
+
case 'payouts':
|
|
648
|
+
case 'payout':
|
|
649
|
+
case 'pay':
|
|
650
|
+
await runPayoutsShell();
|
|
651
|
+
await showDashboard();
|
|
652
|
+
break;
|
|
653
|
+
case 'whoami':
|
|
654
|
+
showWhoami();
|
|
655
|
+
break;
|
|
656
|
+
case 'help':
|
|
657
|
+
case '?':
|
|
658
|
+
showInteractiveHelp();
|
|
659
|
+
break;
|
|
660
|
+
case 'clear':
|
|
661
|
+
case 'cls':
|
|
662
|
+
await showDashboard();
|
|
663
|
+
break;
|
|
664
|
+
case 'refresh':
|
|
665
|
+
case 'r':
|
|
666
|
+
await showDashboard();
|
|
667
|
+
break;
|
|
668
|
+
case 'config':
|
|
669
|
+
if (args[0] === 'compact' && args[1]) {
|
|
670
|
+
if (['auto', 'on', 'off'].includes(args[1])) {
|
|
671
|
+
(0, config_1.setConfig)('compact', args[1]);
|
|
672
|
+
console.log(chalk_1.default.green(`\n ✓ Compact mode: ${args[1]}\n`));
|
|
673
|
+
await showDashboard();
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
console.log(chalk_1.default.red('\n Use: config compact auto/on/off\n'));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
const cfg = (0, config_1.getConfig)();
|
|
681
|
+
console.log(chalk_1.default.cyan('\n Config:'));
|
|
682
|
+
console.log(` compact: ${chalk_1.default.bold(cfg.compact)} ${chalk_1.default.dim('(auto/on/off)')}`);
|
|
683
|
+
console.log(chalk_1.default.dim('\n Usage: config compact on\n'));
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
default:
|
|
687
|
+
console.log(chalk_1.default.red(` Unknown command: ${mainCmd}`));
|
|
688
|
+
console.log(chalk_1.default.dim(' Type "help" for available commands\n'));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
catch (e) {
|
|
693
|
+
console.log(chalk_1.default.red(` Error: ${e.message}\n`));
|
|
694
|
+
}
|
|
695
|
+
setTitle('Mission Control');
|
|
696
|
+
return runLoop();
|
|
697
|
+
};
|
|
698
|
+
await runLoop();
|
|
699
|
+
};
|
|
700
|
+
// ==================== PAYOUT FUNCTIONS ====================
|
|
701
|
+
const PLAN_COLORS = {
|
|
702
|
+
hermes: chalk_1.default.yellow,
|
|
703
|
+
alpha: chalk_1.default.cyan,
|
|
704
|
+
mematic: chalk_1.default.magenta,
|
|
705
|
+
validator_v2: chalk_1.default.blue,
|
|
706
|
+
booster: chalk_1.default.green,
|
|
707
|
+
dumpster: chalk_1.default.gray,
|
|
708
|
+
};
|
|
709
|
+
const PLAN_ICONS = {
|
|
710
|
+
hermes: '⚡',
|
|
711
|
+
alpha: '🔷',
|
|
712
|
+
mematic: '💎',
|
|
713
|
+
validator_v2: '🔐',
|
|
714
|
+
booster: '🚀',
|
|
715
|
+
dumpster: '🗑️',
|
|
716
|
+
};
|
|
717
|
+
const formatAmount = (amount, crypto) => {
|
|
718
|
+
const formatted = amount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 4 });
|
|
719
|
+
return crypto ? `${formatted} ${crypto}` : `$${formatted}`;
|
|
720
|
+
};
|
|
721
|
+
const formatWallet = (address, length = 16) => {
|
|
722
|
+
if (address.length <= length)
|
|
723
|
+
return address;
|
|
724
|
+
return address.substring(0, 8) + '...' + address.substring(address.length - 6);
|
|
725
|
+
};
|
|
726
|
+
const showPayoutDashboard = async () => {
|
|
727
|
+
const spinner = (0, ora_1.default)('Loading payout dashboard...').start();
|
|
728
|
+
const compact = isCompact();
|
|
729
|
+
try {
|
|
730
|
+
// Fetch metrics for all plans
|
|
731
|
+
const metricsData = await api_1.api.getPayoutMetrics();
|
|
732
|
+
const metrics = metricsData.data;
|
|
733
|
+
// Fetch pending payouts
|
|
734
|
+
const payoutsData = await api_1.api.listPayouts({ status: 'pending', limit: 50 });
|
|
735
|
+
const payouts = payoutsData.data || [];
|
|
736
|
+
spinner.stop();
|
|
737
|
+
if (compact) {
|
|
738
|
+
// Compact mobile payout dashboard
|
|
739
|
+
console.log('\n' + chalk_1.default.yellow('>>> PAYOUTS'));
|
|
740
|
+
console.log('');
|
|
741
|
+
console.log(chalk_1.default.cyan('┌─────────────────────────────┐'));
|
|
742
|
+
console.log(chalk_1.default.cyan('│') + ` ${chalk_1.default.yellow('PEND')} ${String(metrics.pending_payouts).padStart(3)} ${chalk_1.default.green('DONE')} ${String(metrics.completed_payouts).padStart(4)} ${chalk_1.default.red('FAIL')} ${String(metrics.failed_payouts).padStart(2)} ` + chalk_1.default.cyan('│'));
|
|
743
|
+
console.log(chalk_1.default.cyan('├─────────────────────────────┤'));
|
|
744
|
+
console.log(chalk_1.default.cyan('│') + ` Pending: ${chalk_1.default.yellow('$' + metrics.pending_amount.toLocaleString())}`.padEnd(29) + chalk_1.default.cyan('│'));
|
|
745
|
+
console.log(chalk_1.default.cyan('│') + ` Paid: ${chalk_1.default.green('$' + metrics.paid_amount.toLocaleString())}`.padEnd(29) + chalk_1.default.cyan('│'));
|
|
746
|
+
console.log(chalk_1.default.cyan('└─────────────────────────────┘'));
|
|
747
|
+
if (payouts.length === 0) {
|
|
748
|
+
console.log(chalk_1.default.green('\n ✓ No pending payouts\n'));
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
// Group payouts by plan
|
|
752
|
+
const byPlan = {};
|
|
753
|
+
payouts.forEach((p) => {
|
|
754
|
+
if (!byPlan[p.plan])
|
|
755
|
+
byPlan[p.plan] = [];
|
|
756
|
+
byPlan[p.plan].push(p);
|
|
757
|
+
});
|
|
758
|
+
console.log(chalk_1.default.dim('\nPENDING BY PLAN:'));
|
|
759
|
+
Object.entries(byPlan).forEach(([plan, planPayouts]) => {
|
|
760
|
+
const planColor = PLAN_COLORS[plan] || chalk_1.default.white;
|
|
761
|
+
const byCrypto = {};
|
|
762
|
+
planPayouts.forEach((p) => {
|
|
763
|
+
if (!byCrypto[p.crypto_type])
|
|
764
|
+
byCrypto[p.crypto_type] = 0;
|
|
765
|
+
byCrypto[p.crypto_type] += p.amount;
|
|
766
|
+
});
|
|
767
|
+
const cryptoTotals = Object.entries(byCrypto).map(([crypto, amt]) => `${amt.toFixed(2)} ${crypto}`).join(', ');
|
|
768
|
+
console.log(` ${planColor(plan.toUpperCase())} ${chalk_1.default.dim('|')} ${planPayouts.length} ${chalk_1.default.dim('|')} ${chalk_1.default.yellow(cryptoTotals)}`);
|
|
769
|
+
// Show first 2 payouts
|
|
770
|
+
planPayouts.slice(0, 2).forEach((p) => {
|
|
771
|
+
const email = p.user_email.length > 15 ? p.user_email.substring(0, 13) + '..' : p.user_email;
|
|
772
|
+
console.log(chalk_1.default.dim(` #${p.id} ${email} ${chalk_1.default.green(p.amount.toFixed(2))} ${p.crypto_type}`));
|
|
773
|
+
});
|
|
774
|
+
if (planPayouts.length > 2) {
|
|
775
|
+
console.log(chalk_1.default.dim(` ... +${planPayouts.length - 2} more`));
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
console.log(chalk_1.default.dim('\nCmds: list, approve, cancel, back\n'));
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
// Full dashboard
|
|
782
|
+
console.log('\n' + chalk_1.default.cyan('╔════════════════════════════════════════════════════════════════╗'));
|
|
783
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.bold.white(' 💰 PAYOUT CONTROL CENTER 💰 ') + chalk_1.default.cyan('║'));
|
|
784
|
+
console.log(chalk_1.default.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
785
|
+
// Metrics Row
|
|
786
|
+
console.log(chalk_1.default.cyan('║') + ' ' + chalk_1.default.cyan('║'));
|
|
787
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.bold.yellow('PENDING')} ${chalk_1.default.bold.green('COMPLETED')} ${chalk_1.default.bold.red('FAILED')} ${chalk_1.default.bold.white('TOTAL')} ` + chalk_1.default.cyan('║'));
|
|
788
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.yellow(String(metrics.pending_payouts).padStart(4))} ${chalk_1.default.green(String(metrics.completed_payouts).padStart(5))} ${chalk_1.default.red(String(metrics.failed_payouts).padStart(3))} ${chalk_1.default.white(String(metrics.total_payouts).padStart(5))} ` + chalk_1.default.cyan('║'));
|
|
789
|
+
console.log(chalk_1.default.cyan('║') + ' ' + chalk_1.default.cyan('║'));
|
|
790
|
+
console.log(chalk_1.default.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
791
|
+
// Amount Stats
|
|
792
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.dim('Pending Amount:')} ${chalk_1.default.yellow('$' + metrics.pending_amount.toLocaleString())}`.padEnd(64) + chalk_1.default.cyan('║'));
|
|
793
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.dim('Paid Amount:')} ${chalk_1.default.green('$' + metrics.paid_amount.toLocaleString())}`.padEnd(64) + chalk_1.default.cyan('║'));
|
|
794
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.dim('Total Volume:')} ${chalk_1.default.white('$' + metrics.total_amount.toLocaleString())}`.padEnd(64) + chalk_1.default.cyan('║'));
|
|
795
|
+
console.log(chalk_1.default.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
796
|
+
if (payouts.length === 0) {
|
|
797
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.green(' ✓ No pending payouts. All clear! ') + chalk_1.default.cyan('║'));
|
|
798
|
+
console.log(chalk_1.default.cyan('╚════════════════════════════════════════════════════════════════╝\n'));
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
// Group payouts by plan
|
|
802
|
+
const byPlan = {};
|
|
803
|
+
payouts.forEach((p) => {
|
|
804
|
+
if (!byPlan[p.plan])
|
|
805
|
+
byPlan[p.plan] = [];
|
|
806
|
+
byPlan[p.plan].push(p);
|
|
807
|
+
});
|
|
808
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.bold.white(' PENDING PAYOUTS BY PLAN ') + chalk_1.default.cyan('║'));
|
|
809
|
+
console.log(chalk_1.default.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
810
|
+
// Show payouts by plan
|
|
811
|
+
Object.entries(byPlan).forEach(([plan, planPayouts]) => {
|
|
812
|
+
const planColor = PLAN_COLORS[plan] || chalk_1.default.white;
|
|
813
|
+
const planIcon = PLAN_ICONS[plan] || '📋';
|
|
814
|
+
// Group amounts by crypto type
|
|
815
|
+
const byCrypto = {};
|
|
816
|
+
planPayouts.forEach((p) => {
|
|
817
|
+
if (!byCrypto[p.crypto_type])
|
|
818
|
+
byCrypto[p.crypto_type] = 0;
|
|
819
|
+
byCrypto[p.crypto_type] += p.amount;
|
|
820
|
+
});
|
|
821
|
+
const cryptoTotals = Object.entries(byCrypto).map(([crypto, amt]) => `${amt.toFixed(2)} ${crypto}`).join(', ');
|
|
822
|
+
console.log(chalk_1.default.cyan('║') + ` ${planIcon} ${planColor(plan.toUpperCase().padEnd(12))} ${chalk_1.default.dim('|')} ${chalk_1.default.white(planPayouts.length + ' payouts')} ${chalk_1.default.dim('|')} ${chalk_1.default.yellow(cryptoTotals)}`.padEnd(72) + chalk_1.default.cyan('║'));
|
|
823
|
+
// Show first 3 payouts for this plan
|
|
824
|
+
planPayouts.slice(0, 3).forEach((p) => {
|
|
825
|
+
const idStr = chalk_1.default.dim(`#${p.id}`);
|
|
826
|
+
const emailStr = p.user_email.length > 20 ? p.user_email.substring(0, 18) + '..' : p.user_email;
|
|
827
|
+
const amountStr = formatAmount(p.amount, p.crypto_type);
|
|
828
|
+
const walletStr = formatWallet(p.wallet_address);
|
|
829
|
+
console.log(chalk_1.default.cyan('║') + ` ${idStr} ${chalk_1.default.white(emailStr.padEnd(20))} ${chalk_1.default.green(amountStr.padStart(14))} ${chalk_1.default.dim(walletStr)}`.padEnd(72) + chalk_1.default.cyan('║'));
|
|
830
|
+
});
|
|
831
|
+
if (planPayouts.length > 3) {
|
|
832
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.dim(` ... and ${planPayouts.length - 3} more`).padEnd(64) + chalk_1.default.cyan('║'));
|
|
833
|
+
}
|
|
834
|
+
console.log(chalk_1.default.cyan('║') + ' ' + chalk_1.default.cyan('║'));
|
|
835
|
+
});
|
|
836
|
+
console.log(chalk_1.default.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
837
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.dim(' Commands: list, approve <ids>, cancel <ids>, process, back ') + chalk_1.default.cyan('║'));
|
|
838
|
+
console.log(chalk_1.default.cyan('╚════════════════════════════════════════════════════════════════╝\n'));
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
catch (error) {
|
|
842
|
+
spinner.fail(chalk_1.default.red('Failed to load payout dashboard'));
|
|
843
|
+
if (error.response?.status === 403) {
|
|
844
|
+
console.log(chalk_1.default.red('\n ⛔ Access Denied: Only managers and executors can access payouts\n'));
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
console.log(chalk_1.default.dim(` ${error.response?.data?.error || error.message}\n`));
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
const listPayoutsCmd = async (options) => {
|
|
852
|
+
const spinner = (0, ora_1.default)('Loading payouts...').start();
|
|
853
|
+
const compact = isCompact();
|
|
854
|
+
try {
|
|
855
|
+
const data = await api_1.api.listPayouts({
|
|
856
|
+
plan: options.plan,
|
|
857
|
+
status: options.status || 'pending',
|
|
858
|
+
page: options.page || 1,
|
|
859
|
+
limit: compact ? 10 : 20,
|
|
860
|
+
});
|
|
861
|
+
const payouts = data.data || [];
|
|
862
|
+
spinner.stop();
|
|
863
|
+
if (payouts.length === 0) {
|
|
864
|
+
console.log(chalk_1.default.dim('\n No payouts found\n'));
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (compact) {
|
|
868
|
+
// Compact list view
|
|
869
|
+
console.log('');
|
|
870
|
+
payouts.forEach((p) => {
|
|
871
|
+
const planColor = PLAN_COLORS[p.plan] || chalk_1.default.white;
|
|
872
|
+
const statusIcon = p.payed ? chalk_1.default.green('✓') : p.triggered ? chalk_1.default.yellow('~') : chalk_1.default.red('●');
|
|
873
|
+
const email = p.user_email.length > 12 ? p.user_email.substring(0, 10) + '..' : p.user_email;
|
|
874
|
+
console.log(` ${statusIcon} ${chalk_1.default.dim('#' + p.id)} ${planColor(p.plan.substring(0, 6))} ${email} ${chalk_1.default.green(p.amount.toFixed(2))} ${p.crypto_type}`);
|
|
875
|
+
});
|
|
876
|
+
console.log(chalk_1.default.dim(`\n Pg ${data.pagination.page}/${data.pagination.pages} (${data.pagination.total})\n`));
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
const table = new cli_table3_1.default({
|
|
880
|
+
head: [
|
|
881
|
+
chalk_1.default.cyan('ID'),
|
|
882
|
+
chalk_1.default.cyan('User'),
|
|
883
|
+
chalk_1.default.cyan('Plan'),
|
|
884
|
+
chalk_1.default.cyan('Amount'),
|
|
885
|
+
chalk_1.default.cyan('Crypto'),
|
|
886
|
+
chalk_1.default.cyan('Wallet'),
|
|
887
|
+
chalk_1.default.cyan('Status'),
|
|
888
|
+
],
|
|
889
|
+
style: { head: [], border: [] },
|
|
890
|
+
colWidths: [8, 22, 12, 14, 10, 18, 10],
|
|
891
|
+
});
|
|
892
|
+
payouts.forEach((p) => {
|
|
893
|
+
const planColor = PLAN_COLORS[p.plan] || chalk_1.default.white;
|
|
894
|
+
const statusIcon = p.payed ? chalk_1.default.green('✓ Paid') : p.triggered ? chalk_1.default.yellow('⏳ Proc') : chalk_1.default.red('● Pend');
|
|
895
|
+
table.push([
|
|
896
|
+
chalk_1.default.dim(String(p.id)),
|
|
897
|
+
p.user_email.length > 20 ? p.user_email.substring(0, 18) + '..' : p.user_email,
|
|
898
|
+
planColor(p.plan),
|
|
899
|
+
chalk_1.default.green('$' + p.amount.toFixed(2)),
|
|
900
|
+
p.crypto_type,
|
|
901
|
+
formatWallet(p.wallet_address, 16),
|
|
902
|
+
statusIcon,
|
|
903
|
+
]);
|
|
904
|
+
});
|
|
905
|
+
console.log('\n' + table.toString());
|
|
906
|
+
console.log(chalk_1.default.dim(`\n Page ${data.pagination.page}/${data.pagination.pages} (${data.pagination.total} total)\n`));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
catch (error) {
|
|
910
|
+
spinner.fail(chalk_1.default.red('Failed to load payouts'));
|
|
911
|
+
console.log(chalk_1.default.dim(` ${error.response?.data?.error || error.message}\n`));
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const approvePayoutsCmd = async (ids) => {
|
|
915
|
+
if (ids.length === 0) {
|
|
916
|
+
console.log(chalk_1.default.red('\n No payout IDs provided\n'));
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
// Confirmation
|
|
920
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
921
|
+
{
|
|
922
|
+
type: 'confirm',
|
|
923
|
+
name: 'confirm',
|
|
924
|
+
message: chalk_1.default.yellow(`⚠️ Approve and send ${ids.length} payout(s) via TarPay?`),
|
|
925
|
+
default: false,
|
|
926
|
+
},
|
|
927
|
+
]);
|
|
928
|
+
if (!confirm) {
|
|
929
|
+
console.log(chalk_1.default.dim('\n Cancelled\n'));
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const spinner = (0, ora_1.default)(`Processing ${ids.length} payouts...`).start();
|
|
933
|
+
try {
|
|
934
|
+
const result = await api_1.api.batchApprovePayouts(ids);
|
|
935
|
+
spinner.stop();
|
|
936
|
+
if (result.success) {
|
|
937
|
+
console.log('\n' + chalk_1.default.green('╔══════════════════════════════════════════╗'));
|
|
938
|
+
console.log(chalk_1.default.green('║') + chalk_1.default.bold.white(' ✓ PAYOUTS APPROVED & SUBMITTED ') + chalk_1.default.green('║'));
|
|
939
|
+
console.log(chalk_1.default.green('╠══════════════════════════════════════════╣'));
|
|
940
|
+
console.log(chalk_1.default.green('║') + ` Successful: ${chalk_1.default.bold.green(result.success_count)}`.padEnd(42) + chalk_1.default.green('║'));
|
|
941
|
+
console.log(chalk_1.default.green('║') + ` Failed: ${chalk_1.default.bold.red(result.failed_count)}`.padEnd(42) + chalk_1.default.green('║'));
|
|
942
|
+
console.log(chalk_1.default.green('║') + ` Total: ${chalk_1.default.bold.yellow('$' + result.total_amount.toFixed(2))}`.padEnd(42) + chalk_1.default.green('║'));
|
|
943
|
+
console.log(chalk_1.default.green('╚══════════════════════════════════════════╝\n'));
|
|
944
|
+
if (result.errors && result.errors.length > 0) {
|
|
945
|
+
console.log(chalk_1.default.red(' Errors:'));
|
|
946
|
+
result.errors.forEach((err) => {
|
|
947
|
+
console.log(chalk_1.default.dim(` - ${err}`));
|
|
948
|
+
});
|
|
949
|
+
console.log('');
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
console.log(chalk_1.default.red(`\n Failed: ${result.error || 'Unknown error'}\n`));
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
catch (error) {
|
|
957
|
+
spinner.fail(chalk_1.default.red('Failed to approve payouts'));
|
|
958
|
+
console.log(chalk_1.default.dim(` ${error.response?.data?.error || error.message}\n`));
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
const cancelPayoutsCmd = async (ids) => {
|
|
962
|
+
if (ids.length === 0) {
|
|
963
|
+
console.log(chalk_1.default.red('\n No payout IDs provided\n'));
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
// Ask for cooldown options
|
|
967
|
+
const { confirm, addCooldown, cooldownHours, cooldownReason } = await inquirer_1.default.prompt([
|
|
968
|
+
{
|
|
969
|
+
type: 'confirm',
|
|
970
|
+
name: 'confirm',
|
|
971
|
+
message: chalk_1.default.yellow(`⚠️ Cancel ${ids.length} payout(s) and return funds to wallets?`),
|
|
972
|
+
default: false,
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
type: 'confirm',
|
|
976
|
+
name: 'addCooldown',
|
|
977
|
+
message: 'Add withdrawal cooldown for these users?',
|
|
978
|
+
default: false,
|
|
979
|
+
when: (answers) => answers.confirm,
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
type: 'list',
|
|
983
|
+
name: 'cooldownHours',
|
|
984
|
+
message: 'Cooldown duration:',
|
|
985
|
+
choices: [
|
|
986
|
+
{ name: '24 hours', value: 24 },
|
|
987
|
+
{ name: '48 hours', value: 48 },
|
|
988
|
+
{ name: '72 hours', value: 72 },
|
|
989
|
+
{ name: '1 week', value: 168 },
|
|
990
|
+
],
|
|
991
|
+
when: (answers) => answers.addCooldown,
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
type: 'input',
|
|
995
|
+
name: 'cooldownReason',
|
|
996
|
+
message: 'Reason for cancellation:',
|
|
997
|
+
default: 'Payout cancelled by admin',
|
|
998
|
+
when: (answers) => answers.addCooldown,
|
|
999
|
+
},
|
|
1000
|
+
]);
|
|
1001
|
+
if (!confirm) {
|
|
1002
|
+
console.log(chalk_1.default.dim('\n Cancelled\n'));
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
const spinner = (0, ora_1.default)(`Cancelling ${ids.length} payouts...`).start();
|
|
1006
|
+
try {
|
|
1007
|
+
const options = {};
|
|
1008
|
+
if (addCooldown) {
|
|
1009
|
+
options.cooldown_hours = cooldownHours;
|
|
1010
|
+
options.cooldown_reason = cooldownReason;
|
|
1011
|
+
}
|
|
1012
|
+
const result = await api_1.api.batchCancelPayouts(ids, options);
|
|
1013
|
+
spinner.stop();
|
|
1014
|
+
if (result.success) {
|
|
1015
|
+
console.log('\n' + chalk_1.default.yellow('╔══════════════════════════════════════════╗'));
|
|
1016
|
+
console.log(chalk_1.default.yellow('║') + chalk_1.default.bold.white(' ↩ PAYOUTS CANCELLED & RETURNED ') + chalk_1.default.yellow('║'));
|
|
1017
|
+
console.log(chalk_1.default.yellow('╠══════════════════════════════════════════╣'));
|
|
1018
|
+
console.log(chalk_1.default.yellow('║') + ` Cancelled: ${chalk_1.default.bold.green(result.success_count)}`.padEnd(42) + chalk_1.default.yellow('║'));
|
|
1019
|
+
console.log(chalk_1.default.yellow('║') + ` Failed: ${chalk_1.default.bold.red(result.failed_count)}`.padEnd(42) + chalk_1.default.yellow('║'));
|
|
1020
|
+
console.log(chalk_1.default.yellow('║') + ` Returned: ${chalk_1.default.bold.green('$' + result.total_amount.toFixed(2))}`.padEnd(42) + chalk_1.default.yellow('║'));
|
|
1021
|
+
console.log(chalk_1.default.yellow('╚══════════════════════════════════════════╝\n'));
|
|
1022
|
+
if (result.errors && result.errors.length > 0) {
|
|
1023
|
+
console.log(chalk_1.default.red(' Errors:'));
|
|
1024
|
+
result.errors.forEach((err) => {
|
|
1025
|
+
console.log(chalk_1.default.dim(` - Payout ${err.payout_id}: ${err.error}`));
|
|
1026
|
+
});
|
|
1027
|
+
console.log('');
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
else {
|
|
1031
|
+
console.log(chalk_1.default.red(`\n Failed: ${result.error || 'Unknown error'}\n`));
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
catch (error) {
|
|
1035
|
+
spinner.fail(chalk_1.default.red('Failed to cancel payouts'));
|
|
1036
|
+
console.log(chalk_1.default.dim(` ${error.response?.data?.error || error.message}\n`));
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
const selectPayoutsInteractive = async (plan) => {
|
|
1040
|
+
const spinner = (0, ora_1.default)('Loading payouts...').start();
|
|
1041
|
+
try {
|
|
1042
|
+
const data = await api_1.api.listPayouts({ plan, status: 'pending', limit: 100 });
|
|
1043
|
+
const payouts = data.data || [];
|
|
1044
|
+
spinner.stop();
|
|
1045
|
+
if (payouts.length === 0) {
|
|
1046
|
+
console.log(chalk_1.default.dim('\n No pending payouts found\n'));
|
|
1047
|
+
return [];
|
|
1048
|
+
}
|
|
1049
|
+
// Group by plan for better display
|
|
1050
|
+
const choices = payouts.map((p) => {
|
|
1051
|
+
const planColor = PLAN_COLORS[p.plan] || chalk_1.default.white;
|
|
1052
|
+
const planIcon = PLAN_ICONS[p.plan] || '📋';
|
|
1053
|
+
return {
|
|
1054
|
+
name: `${planIcon} ${chalk_1.default.dim('#' + p.id)} ${planColor(p.plan.padEnd(12))} ${p.user_email.substring(0, 25).padEnd(25)} ${chalk_1.default.green('$' + p.amount.toFixed(2).padStart(10))} ${chalk_1.default.dim(p.crypto_type)}`,
|
|
1055
|
+
value: p.id,
|
|
1056
|
+
short: `#${p.id}`,
|
|
1057
|
+
};
|
|
1058
|
+
});
|
|
1059
|
+
const { selected } = await inquirer_1.default.prompt([
|
|
1060
|
+
{
|
|
1061
|
+
type: 'checkbox',
|
|
1062
|
+
name: 'selected',
|
|
1063
|
+
message: 'Select payouts to process:',
|
|
1064
|
+
choices,
|
|
1065
|
+
pageSize: 15,
|
|
1066
|
+
loop: false,
|
|
1067
|
+
},
|
|
1068
|
+
]);
|
|
1069
|
+
return selected;
|
|
1070
|
+
}
|
|
1071
|
+
catch (error) {
|
|
1072
|
+
spinner.fail(chalk_1.default.red('Failed to load payouts'));
|
|
1073
|
+
return [];
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
const processPayoutsWithProgress = async (ids, action) => {
|
|
1077
|
+
console.log('\n' + chalk_1.default.cyan('╔══════════════════════════════════════════════════════════════╗'));
|
|
1078
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.bold.white(` 🔄 PROCESSING ${ids.length} PAYOUT(S)... `) + chalk_1.default.cyan('║'));
|
|
1079
|
+
console.log(chalk_1.default.cyan('╠══════════════════════════════════════════════════════════════╣'));
|
|
1080
|
+
const startTime = Date.now();
|
|
1081
|
+
// Show progress
|
|
1082
|
+
let processed = 0;
|
|
1083
|
+
const progressBar = (current, total) => {
|
|
1084
|
+
const percent = Math.round((current / total) * 100);
|
|
1085
|
+
const filled = Math.round(percent / 2);
|
|
1086
|
+
const empty = 50 - filled;
|
|
1087
|
+
return chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.dim('░'.repeat(empty)) + ` ${percent}%`;
|
|
1088
|
+
};
|
|
1089
|
+
// Simulate progress while waiting for API
|
|
1090
|
+
const progressInterval = setInterval(() => {
|
|
1091
|
+
processed = Math.min(processed + Math.random() * 10, 90);
|
|
1092
|
+
process.stdout.write(`\r${chalk_1.default.cyan('║')} ${progressBar(processed, 100)} ${chalk_1.default.cyan('║')}`);
|
|
1093
|
+
}, 200);
|
|
1094
|
+
try {
|
|
1095
|
+
let result;
|
|
1096
|
+
if (action === 'approve') {
|
|
1097
|
+
result = await api_1.api.batchApprovePayouts(ids);
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
result = await api_1.api.batchCancelPayouts(ids);
|
|
1101
|
+
}
|
|
1102
|
+
clearInterval(progressInterval);
|
|
1103
|
+
processed = 100;
|
|
1104
|
+
process.stdout.write(`\r${chalk_1.default.cyan('║')} ${progressBar(100, 100)} ${chalk_1.default.cyan('║')}\n`);
|
|
1105
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
1106
|
+
console.log(chalk_1.default.cyan('╠══════════════════════════════════════════════════════════════╣'));
|
|
1107
|
+
if (result.success) {
|
|
1108
|
+
const icon = action === 'approve' ? '✓' : '↩';
|
|
1109
|
+
const verb = action === 'approve' ? 'APPROVED & SENT' : 'CANCELLED & RETURNED';
|
|
1110
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.green(` ${icon} ${verb} `) + chalk_1.default.cyan('║'));
|
|
1111
|
+
console.log(chalk_1.default.cyan('║') + ` Successful: ${chalk_1.default.bold.green(String(result.success_count).padEnd(5))} Failed: ${chalk_1.default.bold.red(String(result.failed_count).padEnd(5))} Time: ${chalk_1.default.dim(elapsed + 's')}`.padEnd(62) + chalk_1.default.cyan('║'));
|
|
1112
|
+
console.log(chalk_1.default.cyan('║') + ` Total Amount: ${chalk_1.default.bold.yellow('$' + result.total_amount.toFixed(2))}`.padEnd(62) + chalk_1.default.cyan('║'));
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.red(` ✗ FAILED: ${result.error || 'Unknown error'}`.substring(0, 58).padEnd(60)) + chalk_1.default.cyan('║'));
|
|
1116
|
+
}
|
|
1117
|
+
console.log(chalk_1.default.cyan('╚══════════════════════════════════════════════════════════════╝\n'));
|
|
1118
|
+
// Show errors if any
|
|
1119
|
+
if (result.errors && result.errors.length > 0) {
|
|
1120
|
+
console.log(chalk_1.default.red(' Errors:'));
|
|
1121
|
+
result.errors.slice(0, 5).forEach((err) => {
|
|
1122
|
+
const errMsg = typeof err === 'string' ? err : `Payout ${err.payout_id}: ${err.error}`;
|
|
1123
|
+
console.log(chalk_1.default.dim(` - ${errMsg}`));
|
|
1124
|
+
});
|
|
1125
|
+
if (result.errors.length > 5) {
|
|
1126
|
+
console.log(chalk_1.default.dim(` ... and ${result.errors.length - 5} more errors`));
|
|
1127
|
+
}
|
|
1128
|
+
console.log('');
|
|
1129
|
+
}
|
|
1130
|
+
return result;
|
|
1131
|
+
}
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
clearInterval(progressInterval);
|
|
1134
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.red(' ✗ ERROR: ' + (error.response?.data?.error || error.message).substring(0, 50).padEnd(52)) + chalk_1.default.cyan('║'));
|
|
1135
|
+
console.log(chalk_1.default.cyan('╚══════════════════════════════════════════════════════════════╝\n'));
|
|
1136
|
+
return { success: false };
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
const runPayoutsShell = async () => {
|
|
1140
|
+
setTitle('Payouts');
|
|
1141
|
+
await showPayoutDashboard();
|
|
1142
|
+
const runLoop = async () => {
|
|
1143
|
+
const { command } = await inquirer_1.default.prompt([
|
|
1144
|
+
{
|
|
1145
|
+
type: 'input',
|
|
1146
|
+
name: 'command',
|
|
1147
|
+
message: chalk_1.default.yellow('payouts') + chalk_1.default.cyan(' > '),
|
|
1148
|
+
prefix: '',
|
|
1149
|
+
},
|
|
1150
|
+
]);
|
|
1151
|
+
const parts = command.trim().split(/\s+/);
|
|
1152
|
+
const cmd = parts[0]?.toLowerCase();
|
|
1153
|
+
const args = parts.slice(1);
|
|
1154
|
+
if (cmd === 'back' || cmd === 'exit' || cmd === 'q') {
|
|
1155
|
+
setTitle('Home');
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
if (cmd === '') {
|
|
1159
|
+
return runLoop();
|
|
1160
|
+
}
|
|
1161
|
+
try {
|
|
1162
|
+
switch (cmd) {
|
|
1163
|
+
case 'refresh':
|
|
1164
|
+
case 'r':
|
|
1165
|
+
await showPayoutDashboard();
|
|
1166
|
+
break;
|
|
1167
|
+
case 'list':
|
|
1168
|
+
case 'ls':
|
|
1169
|
+
const listPlan = args.find((a) => !a.startsWith('-'));
|
|
1170
|
+
await listPayoutsCmd({ plan: listPlan });
|
|
1171
|
+
break;
|
|
1172
|
+
case 'select':
|
|
1173
|
+
case 's':
|
|
1174
|
+
// Interactive selection mode
|
|
1175
|
+
const selectPlan = args.find((a) => !a.startsWith('-'));
|
|
1176
|
+
const selectedIds = await selectPayoutsInteractive(selectPlan);
|
|
1177
|
+
if (selectedIds.length > 0) {
|
|
1178
|
+
const { action } = await inquirer_1.default.prompt([
|
|
1179
|
+
{
|
|
1180
|
+
type: 'list',
|
|
1181
|
+
name: 'action',
|
|
1182
|
+
message: `What do you want to do with ${selectedIds.length} selected payout(s)?`,
|
|
1183
|
+
choices: [
|
|
1184
|
+
{ name: chalk_1.default.green('✓ Approve & Send via TarPay'), value: 'approve' },
|
|
1185
|
+
{ name: chalk_1.default.yellow('↩ Cancel & Return to Wallets'), value: 'cancel' },
|
|
1186
|
+
{ name: chalk_1.default.dim('✗ Cancel'), value: 'none' },
|
|
1187
|
+
],
|
|
1188
|
+
},
|
|
1189
|
+
]);
|
|
1190
|
+
if (action === 'approve') {
|
|
1191
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
1192
|
+
{
|
|
1193
|
+
type: 'confirm',
|
|
1194
|
+
name: 'confirm',
|
|
1195
|
+
message: chalk_1.default.yellow(`⚠️ Confirm: Send ${selectedIds.length} payout(s)?`),
|
|
1196
|
+
default: false,
|
|
1197
|
+
},
|
|
1198
|
+
]);
|
|
1199
|
+
if (confirm) {
|
|
1200
|
+
await processPayoutsWithProgress(selectedIds, 'approve');
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
else if (action === 'cancel') {
|
|
1204
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
1205
|
+
{
|
|
1206
|
+
type: 'confirm',
|
|
1207
|
+
name: 'confirm',
|
|
1208
|
+
message: chalk_1.default.yellow(`⚠️ Confirm: Cancel ${selectedIds.length} payout(s)?`),
|
|
1209
|
+
default: false,
|
|
1210
|
+
},
|
|
1211
|
+
]);
|
|
1212
|
+
if (confirm) {
|
|
1213
|
+
await processPayoutsWithProgress(selectedIds, 'cancel');
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
break;
|
|
1218
|
+
case 'approve':
|
|
1219
|
+
case 'a':
|
|
1220
|
+
if (args.length === 0) {
|
|
1221
|
+
// Interactive mode
|
|
1222
|
+
const approveSelected = await selectPayoutsInteractive();
|
|
1223
|
+
if (approveSelected.length > 0) {
|
|
1224
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
1225
|
+
{
|
|
1226
|
+
type: 'confirm',
|
|
1227
|
+
name: 'confirm',
|
|
1228
|
+
message: chalk_1.default.yellow(`⚠️ Approve & send ${approveSelected.length} payout(s)?`),
|
|
1229
|
+
default: false,
|
|
1230
|
+
},
|
|
1231
|
+
]);
|
|
1232
|
+
if (confirm) {
|
|
1233
|
+
await processPayoutsWithProgress(approveSelected, 'approve');
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
const approveIds = args.map(Number).filter((n) => !isNaN(n));
|
|
1239
|
+
await processPayoutsWithProgress(approveIds, 'approve');
|
|
1240
|
+
}
|
|
1241
|
+
break;
|
|
1242
|
+
case 'cancel':
|
|
1243
|
+
case 'c':
|
|
1244
|
+
if (args.length === 0) {
|
|
1245
|
+
// Interactive mode
|
|
1246
|
+
const cancelSelected = await selectPayoutsInteractive();
|
|
1247
|
+
if (cancelSelected.length > 0) {
|
|
1248
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
1249
|
+
{
|
|
1250
|
+
type: 'confirm',
|
|
1251
|
+
name: 'confirm',
|
|
1252
|
+
message: chalk_1.default.yellow(`⚠️ Cancel ${cancelSelected.length} payout(s)?`),
|
|
1253
|
+
default: false,
|
|
1254
|
+
},
|
|
1255
|
+
]);
|
|
1256
|
+
if (confirm) {
|
|
1257
|
+
await processPayoutsWithProgress(cancelSelected, 'cancel');
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
const cancelIds = args.map(Number).filter((n) => !isNaN(n));
|
|
1263
|
+
await processPayoutsWithProgress(cancelIds, 'cancel');
|
|
1264
|
+
}
|
|
1265
|
+
break;
|
|
1266
|
+
case 'hermes':
|
|
1267
|
+
case 'alpha':
|
|
1268
|
+
case 'mematic':
|
|
1269
|
+
case 'booster':
|
|
1270
|
+
case 'validator_v2':
|
|
1271
|
+
await listPayoutsCmd({ plan: cmd });
|
|
1272
|
+
break;
|
|
1273
|
+
case 'process':
|
|
1274
|
+
// Process specific plan with interactive selection
|
|
1275
|
+
const { processPlan } = await inquirer_1.default.prompt([
|
|
1276
|
+
{
|
|
1277
|
+
type: 'list',
|
|
1278
|
+
name: 'processPlan',
|
|
1279
|
+
message: 'Select plan to process:',
|
|
1280
|
+
choices: [
|
|
1281
|
+
{ name: '⚡ Hermes', value: 'hermes' },
|
|
1282
|
+
{ name: '🔷 Alpha', value: 'alpha' },
|
|
1283
|
+
{ name: '💎 Mematic', value: 'mematic' },
|
|
1284
|
+
{ name: '🔐 Validator V2', value: 'validator_v2' },
|
|
1285
|
+
{ name: '🚀 Booster', value: 'booster' },
|
|
1286
|
+
{ name: '📋 All Plans', value: undefined },
|
|
1287
|
+
],
|
|
1288
|
+
},
|
|
1289
|
+
]);
|
|
1290
|
+
const toProcess = await selectPayoutsInteractive(processPlan);
|
|
1291
|
+
if (toProcess.length > 0) {
|
|
1292
|
+
const { confirmProcess } = await inquirer_1.default.prompt([
|
|
1293
|
+
{
|
|
1294
|
+
type: 'confirm',
|
|
1295
|
+
name: 'confirmProcess',
|
|
1296
|
+
message: chalk_1.default.yellow(`⚠️ Approve & send ${toProcess.length} payout(s)?`),
|
|
1297
|
+
default: false,
|
|
1298
|
+
},
|
|
1299
|
+
]);
|
|
1300
|
+
if (confirmProcess) {
|
|
1301
|
+
await processPayoutsWithProgress(toProcess, 'approve');
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
break;
|
|
1305
|
+
case 'all':
|
|
1306
|
+
// Approve ALL pending payouts
|
|
1307
|
+
const allSpinner = (0, ora_1.default)('Loading all pending payouts...').start();
|
|
1308
|
+
try {
|
|
1309
|
+
const allData = await api_1.api.listPayouts({ status: 'pending', limit: 500 });
|
|
1310
|
+
const allPayouts = allData.data || [];
|
|
1311
|
+
allSpinner.stop();
|
|
1312
|
+
if (allPayouts.length === 0) {
|
|
1313
|
+
console.log(chalk_1.default.green('\n ✓ No pending payouts to approve\n'));
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
const totalAmount = allPayouts.reduce((sum, p) => sum + p.amount, 0);
|
|
1317
|
+
console.log(chalk_1.default.yellow(`\n ⚠️ APPROVE ALL: ${allPayouts.length} payouts totaling $${totalAmount.toFixed(2)}\n`));
|
|
1318
|
+
const { confirmAll } = await inquirer_1.default.prompt([
|
|
1319
|
+
{
|
|
1320
|
+
type: 'confirm',
|
|
1321
|
+
name: 'confirmAll',
|
|
1322
|
+
message: chalk_1.default.red.bold(`Are you sure you want to approve ALL ${allPayouts.length} pending payouts?`),
|
|
1323
|
+
default: false,
|
|
1324
|
+
},
|
|
1325
|
+
]);
|
|
1326
|
+
if (confirmAll) {
|
|
1327
|
+
const allIds = allPayouts.map((p) => p.id);
|
|
1328
|
+
await processPayoutsWithProgress(allIds, 'approve');
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
console.log(chalk_1.default.dim('\n Cancelled\n'));
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
catch (e) {
|
|
1335
|
+
allSpinner.fail(chalk_1.default.red('Failed to load payouts'));
|
|
1336
|
+
}
|
|
1337
|
+
break;
|
|
1338
|
+
case 'help':
|
|
1339
|
+
case '?':
|
|
1340
|
+
console.log(chalk_1.default.cyan('\n Payout Commands:'));
|
|
1341
|
+
console.log(chalk_1.default.dim(' ────────────────────'));
|
|
1342
|
+
console.log(' refresh, r Refresh dashboard');
|
|
1343
|
+
console.log(' list [plan] List pending payouts');
|
|
1344
|
+
console.log(' select [plan] Interactive payout selection');
|
|
1345
|
+
console.log(' approve [ids] Approve payouts (interactive if no IDs)');
|
|
1346
|
+
console.log(chalk_1.default.yellow(' all Approve ALL pending payouts'));
|
|
1347
|
+
console.log(' cancel [ids] Cancel payouts (interactive if no IDs)');
|
|
1348
|
+
console.log(' process Process payouts by plan (interactive)');
|
|
1349
|
+
console.log(' hermes/alpha/etc List payouts for specific plan');
|
|
1350
|
+
console.log(' back, q Return to main menu');
|
|
1351
|
+
console.log('');
|
|
1352
|
+
break;
|
|
1353
|
+
default:
|
|
1354
|
+
console.log(chalk_1.default.red(` Unknown command: ${cmd}`));
|
|
1355
|
+
console.log(chalk_1.default.dim(' Type "help" for available commands\n'));
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
catch (e) {
|
|
1359
|
+
console.log(chalk_1.default.red(` Error: ${e.message}\n`));
|
|
1360
|
+
}
|
|
1361
|
+
await runLoop();
|
|
1362
|
+
};
|
|
1363
|
+
await runLoop();
|
|
1364
|
+
};
|
|
1365
|
+
// ==================== LOGIN ====================
|
|
1366
|
+
program
|
|
1367
|
+
.command('login')
|
|
1368
|
+
.description('Authenticate with Mission Control via Google SSO')
|
|
1369
|
+
.option('-t, --token <token>', 'API token (skip browser login)')
|
|
1370
|
+
.option('--type <type>', 'Token type (bearer or token)', 'bearer')
|
|
1371
|
+
.option('--url <url>', 'API URL', 'https://api.targlobal.org')
|
|
1372
|
+
.action(async (options) => {
|
|
1373
|
+
setTitle('Login');
|
|
1374
|
+
let token = options.token;
|
|
1375
|
+
if (!token) {
|
|
1376
|
+
console.log(miniLogo);
|
|
1377
|
+
console.log('');
|
|
1378
|
+
console.log(chalk_1.default.cyan(' Opening browser for Google SSO login...'));
|
|
1379
|
+
console.log('');
|
|
1380
|
+
const authUrl = 'https://agents.targlobal.org/cli-auth';
|
|
1381
|
+
try {
|
|
1382
|
+
const open = (await Promise.resolve().then(() => __importStar(require('open')))).default;
|
|
1383
|
+
await open(authUrl);
|
|
1384
|
+
console.log(chalk_1.default.dim(` If browser doesn't open, visit:`));
|
|
1385
|
+
console.log(chalk_1.default.white(` ${authUrl}`));
|
|
1386
|
+
console.log('');
|
|
1387
|
+
}
|
|
1388
|
+
catch (e) {
|
|
1389
|
+
console.log(chalk_1.default.dim(` Open this URL in your browser:`));
|
|
1390
|
+
console.log(chalk_1.default.white(` ${authUrl}`));
|
|
1391
|
+
console.log('');
|
|
1392
|
+
}
|
|
1393
|
+
const answers = await inquirer_1.default.prompt([
|
|
1394
|
+
{
|
|
1395
|
+
type: 'password',
|
|
1396
|
+
name: 'token',
|
|
1397
|
+
message: 'Paste your token from the browser:',
|
|
1398
|
+
mask: '*',
|
|
1399
|
+
},
|
|
1400
|
+
]);
|
|
1401
|
+
token = answers.token;
|
|
1402
|
+
options.type = 'bearer';
|
|
1403
|
+
}
|
|
1404
|
+
(0, config_1.setConfig)('token', token);
|
|
1405
|
+
(0, config_1.setConfig)('tokenType', options.type);
|
|
1406
|
+
(0, config_1.setConfig)('apiUrl', options.url);
|
|
1407
|
+
(0, api_1.resetApiClient)();
|
|
1408
|
+
const spinner = (0, ora_1.default)('Verifying credentials...').start();
|
|
1409
|
+
try {
|
|
1410
|
+
// Fetch user info and stats
|
|
1411
|
+
await fetchUserInfo();
|
|
1412
|
+
const stats = await api_1.api.getStats();
|
|
1413
|
+
spinner.succeed(chalk_1.default.green('Authenticated successfully!'));
|
|
1414
|
+
const user = (0, config_1.getUser)();
|
|
1415
|
+
// Show welcome dashboard
|
|
1416
|
+
console.log('');
|
|
1417
|
+
console.log(chalk_1.default.cyan('╔════════════════════════════════════════════════════════════════╗'));
|
|
1418
|
+
console.log(chalk_1.default.cyan('║') + chalk_1.default.bold.white(` Welcome back, ${(user?.name || 'Agent').padEnd(45)}`) + chalk_1.default.cyan('║'));
|
|
1419
|
+
console.log(chalk_1.default.cyan('╠════════════════════════════════════════════════════════════════╣'));
|
|
1420
|
+
console.log(chalk_1.default.cyan('║') + ` ${chalk_1.default.yellow('⚡')} My Tasks: ${chalk_1.default.bold.white(String(stats.my_tasks).padEnd(8))} ${chalk_1.default.red('●')} Critical: ${chalk_1.default.bold.red(String(stats.critical_count).padEnd(8))} ${chalk_1.default.yellow('!')} Overdue: ${chalk_1.default.bold.yellow(String(stats.overdue_tasks))}`.padEnd(71) + chalk_1.default.cyan('║'));
|
|
1421
|
+
console.log(chalk_1.default.cyan('╚════════════════════════════════════════════════════════════════╝'));
|
|
1422
|
+
console.log('');
|
|
1423
|
+
// Ask if they want to enter interactive mode
|
|
1424
|
+
const { startShell } = await inquirer_1.default.prompt([
|
|
1425
|
+
{
|
|
1426
|
+
type: 'confirm',
|
|
1427
|
+
name: 'startShell',
|
|
1428
|
+
message: 'Enter Mission Control?',
|
|
1429
|
+
default: true,
|
|
1430
|
+
},
|
|
1431
|
+
]);
|
|
1432
|
+
if (startShell) {
|
|
1433
|
+
// Launch interactive shell
|
|
1434
|
+
await runInteractiveShell();
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
catch (error) {
|
|
1438
|
+
spinner.fail(chalk_1.default.red('Authentication failed'));
|
|
1439
|
+
(0, config_1.clearConfig)();
|
|
1440
|
+
console.log(chalk_1.default.dim(' Check your token and try again\n'));
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
// ==================== LOGOUT ====================
|
|
1444
|
+
program
|
|
1445
|
+
.command('logout')
|
|
1446
|
+
.description('Clear stored credentials')
|
|
1447
|
+
.action(() => {
|
|
1448
|
+
(0, config_1.clearConfig)();
|
|
1449
|
+
console.log(chalk_1.default.green('\n[OK] Logged out successfully\n'));
|
|
1450
|
+
});
|
|
1451
|
+
// ==================== UPDATE ====================
|
|
1452
|
+
program
|
|
1453
|
+
.command('update')
|
|
1454
|
+
.description('Update Mission Control CLI to latest version')
|
|
1455
|
+
.action(async () => {
|
|
1456
|
+
setTitle('Updating');
|
|
1457
|
+
console.log(miniLogo);
|
|
1458
|
+
console.log('');
|
|
1459
|
+
const spinner = (0, ora_1.default)('Checking for updates...').start();
|
|
1460
|
+
try {
|
|
1461
|
+
// Re-run the installer
|
|
1462
|
+
spinner.text = 'Downloading latest version...';
|
|
1463
|
+
(0, child_process_1.execSync)('curl -fsSL https://targlobal.org/download/mc | bash', {
|
|
1464
|
+
stdio: 'inherit',
|
|
1465
|
+
});
|
|
1466
|
+
spinner.succeed(chalk_1.default.green('Updated successfully!'));
|
|
1467
|
+
console.log(chalk_1.default.dim(' Restart your terminal to use the new version\n'));
|
|
1468
|
+
}
|
|
1469
|
+
catch (error) {
|
|
1470
|
+
spinner.fail(chalk_1.default.red('Update failed'));
|
|
1471
|
+
console.log(chalk_1.default.dim(' Try manually: curl -fsSL https://targlobal.org/download/mc | bash\n'));
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
// ==================== TASKS (list) ====================
|
|
1475
|
+
program
|
|
1476
|
+
.command('tasks')
|
|
1477
|
+
.alias('ls')
|
|
1478
|
+
.description('List your tasks')
|
|
1479
|
+
.option('-a, --all', 'Show all tasks, not just mine')
|
|
1480
|
+
.option('-b, --board <board>', 'Filter by board')
|
|
1481
|
+
.option('-p, --priority <priority>', 'Filter by priority')
|
|
1482
|
+
.action(async (options) => {
|
|
1483
|
+
setTitle('Tasks');
|
|
1484
|
+
requireAuth();
|
|
1485
|
+
await listTasksCmd(options);
|
|
1486
|
+
showStatusBar();
|
|
1487
|
+
});
|
|
1488
|
+
// ==================== SHOW (task detail) ====================
|
|
1489
|
+
program
|
|
1490
|
+
.command('show <taskId>')
|
|
1491
|
+
.alias('view')
|
|
1492
|
+
.description('Show task details')
|
|
1493
|
+
.action(async (taskId) => {
|
|
1494
|
+
setTitle(`Task ${taskId}`);
|
|
1495
|
+
requireAuth();
|
|
1496
|
+
await showTaskCmd(taskId);
|
|
1497
|
+
showStatusBar();
|
|
1498
|
+
});
|
|
1499
|
+
// ==================== NEW (create task) ====================
|
|
1500
|
+
program
|
|
1501
|
+
.command('new <title>')
|
|
1502
|
+
.alias('create')
|
|
1503
|
+
.description('Create a new task')
|
|
1504
|
+
.option('-d, --description <desc>', 'Task description')
|
|
1505
|
+
.option('-p, --priority <priority>', 'Priority: critical, high, medium, low', 'medium')
|
|
1506
|
+
.option('-t, --type <type>', 'Type: bug, feature, improvement, task', 'task')
|
|
1507
|
+
.option('-b, --board <board>', 'Board slug', 'backend')
|
|
1508
|
+
.action(async (title, options) => {
|
|
1509
|
+
setTitle('New Task');
|
|
1510
|
+
requireAuth();
|
|
1511
|
+
const spinner = (0, ora_1.default)('Creating task...').start();
|
|
1512
|
+
try {
|
|
1513
|
+
const boards = await api_1.api.listBoards();
|
|
1514
|
+
const board = boards.boards.find((b) => b.slug === options.board);
|
|
1515
|
+
if (!board) {
|
|
1516
|
+
spinner.fail(chalk_1.default.red(`Board '${options.board}' not found`));
|
|
1517
|
+
console.log(chalk_1.default.dim(' Available boards: ' + boards.boards.map((b) => b.slug).join(', ') + '\n'));
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
const task = await api_1.api.createTask({
|
|
1521
|
+
title,
|
|
1522
|
+
description: options.description,
|
|
1523
|
+
board: board.id,
|
|
1524
|
+
priority: options.priority,
|
|
1525
|
+
task_type: options.type,
|
|
1526
|
+
});
|
|
1527
|
+
spinner.succeed(chalk_1.default.green(`Task created: ${task.display_id}`));
|
|
1528
|
+
console.log(chalk_1.default.dim(` ${task.title}\n`));
|
|
1529
|
+
}
|
|
1530
|
+
catch (error) {
|
|
1531
|
+
spinner.fail(chalk_1.default.red('Failed to create task'));
|
|
1532
|
+
console.log(chalk_1.default.dim(` ${error.response?.data?.error || error.message}\n`));
|
|
1533
|
+
}
|
|
1534
|
+
showStatusBar();
|
|
1535
|
+
});
|
|
1536
|
+
// ==================== START (move to In Progress) ====================
|
|
1537
|
+
program
|
|
1538
|
+
.command('start <taskId>')
|
|
1539
|
+
.description('Move task to In Progress')
|
|
1540
|
+
.action(async (taskId) => {
|
|
1541
|
+
setTitle('Starting Task');
|
|
1542
|
+
requireAuth();
|
|
1543
|
+
await startTaskCmd(taskId);
|
|
1544
|
+
showStatusBar();
|
|
1545
|
+
});
|
|
1546
|
+
// ==================== DONE (complete task) ====================
|
|
1547
|
+
program
|
|
1548
|
+
.command('done <taskId>')
|
|
1549
|
+
.alias('complete')
|
|
1550
|
+
.description('Mark task as complete')
|
|
1551
|
+
.action(async (taskId) => {
|
|
1552
|
+
setTitle('Completing Task');
|
|
1553
|
+
requireAuth();
|
|
1554
|
+
await completeTaskCmd(taskId);
|
|
1555
|
+
showStatusBar();
|
|
1556
|
+
});
|
|
1557
|
+
// ==================== COMMENT ====================
|
|
1558
|
+
program
|
|
1559
|
+
.command('comment <taskId> <message>')
|
|
1560
|
+
.alias('note')
|
|
1561
|
+
.description('Add a comment to a task')
|
|
1562
|
+
.action(async (taskId, message) => {
|
|
1563
|
+
setTitle('Adding Comment');
|
|
1564
|
+
requireAuth();
|
|
1565
|
+
const spinner = (0, ora_1.default)('Adding comment...').start();
|
|
1566
|
+
try {
|
|
1567
|
+
await api_1.api.addComment(parseTaskNumber(taskId), message);
|
|
1568
|
+
spinner.succeed(chalk_1.default.green('Comment added'));
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
spinner.fail(chalk_1.default.red('Failed to add comment'));
|
|
1572
|
+
console.log(chalk_1.default.dim(` ${error.response?.data?.error || error.message}\n`));
|
|
1573
|
+
}
|
|
1574
|
+
showStatusBar();
|
|
1575
|
+
});
|
|
1576
|
+
// ==================== BOARDS ====================
|
|
1577
|
+
program
|
|
1578
|
+
.command('boards')
|
|
1579
|
+
.description('List available boards')
|
|
1580
|
+
.action(async () => {
|
|
1581
|
+
setTitle('Boards');
|
|
1582
|
+
requireAuth();
|
|
1583
|
+
await listBoardsCmd();
|
|
1584
|
+
showStatusBar();
|
|
1585
|
+
});
|
|
1586
|
+
// ==================== STATS ====================
|
|
1587
|
+
program
|
|
1588
|
+
.command('stats')
|
|
1589
|
+
.description('Show task statistics')
|
|
1590
|
+
.action(async () => {
|
|
1591
|
+
setTitle('Stats');
|
|
1592
|
+
requireAuth();
|
|
1593
|
+
await showStatsCmd();
|
|
1594
|
+
showStatusBar();
|
|
1595
|
+
});
|
|
1596
|
+
// ==================== URGENT ====================
|
|
1597
|
+
program
|
|
1598
|
+
.command('urgent')
|
|
1599
|
+
.description('Show critical and overdue tasks')
|
|
1600
|
+
.action(async () => {
|
|
1601
|
+
setTitle('Urgent');
|
|
1602
|
+
requireAuth();
|
|
1603
|
+
await showUrgentCmd();
|
|
1604
|
+
showStatusBar();
|
|
1605
|
+
});
|
|
1606
|
+
// ==================== WHOAMI ====================
|
|
1607
|
+
program
|
|
1608
|
+
.command('whoami')
|
|
1609
|
+
.description('Show current user and configuration')
|
|
1610
|
+
.action(() => {
|
|
1611
|
+
setTitle('Who Am I');
|
|
1612
|
+
showWhoami();
|
|
1613
|
+
});
|
|
1614
|
+
// ==================== CONFIG ====================
|
|
1615
|
+
program
|
|
1616
|
+
.command('config [key] [value]')
|
|
1617
|
+
.description('View or set configuration (compact: auto/on/off)')
|
|
1618
|
+
.action((key, value) => {
|
|
1619
|
+
setTitle('Config');
|
|
1620
|
+
const config = (0, config_1.getConfig)();
|
|
1621
|
+
if (!key) {
|
|
1622
|
+
// Show all config
|
|
1623
|
+
console.log('\n' + chalk_1.default.bold(' Mission Control Config'));
|
|
1624
|
+
console.log(chalk_1.default.dim(' ──────────────────────'));
|
|
1625
|
+
console.log(` compact: ${chalk_1.default.cyan(config.compact)} ${chalk_1.default.dim('(auto/on/off)')}`);
|
|
1626
|
+
console.log(` apiUrl: ${config.apiUrl}`);
|
|
1627
|
+
console.log(` token: ${config.token ? chalk_1.default.green('●') + ' set' : chalk_1.default.red('○') + ' not set'}`);
|
|
1628
|
+
console.log('');
|
|
1629
|
+
console.log(chalk_1.default.dim(' Usage: mc config <key> <value>'));
|
|
1630
|
+
console.log(chalk_1.default.dim(' Example: mc config compact on'));
|
|
1631
|
+
console.log('');
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (key === 'compact') {
|
|
1635
|
+
if (!value || !['auto', 'on', 'off'].includes(value)) {
|
|
1636
|
+
console.log(chalk_1.default.red('\n Invalid value. Use: auto, on, or off\n'));
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
(0, config_1.setConfig)('compact', value);
|
|
1640
|
+
console.log(chalk_1.default.green(`\n ✓ Compact mode set to: ${value}\n`));
|
|
1641
|
+
if (value === 'on') {
|
|
1642
|
+
console.log(chalk_1.default.dim(' Mobile-friendly compact UI enabled'));
|
|
1643
|
+
}
|
|
1644
|
+
else if (value === 'off') {
|
|
1645
|
+
console.log(chalk_1.default.dim(' Full desktop UI enabled'));
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
console.log(chalk_1.default.dim(' Will auto-detect based on terminal width (<60 = compact)'));
|
|
1649
|
+
}
|
|
1650
|
+
console.log('');
|
|
1651
|
+
}
|
|
1652
|
+
else {
|
|
1653
|
+
console.log(chalk_1.default.red(`\n Unknown config key: ${key}`));
|
|
1654
|
+
console.log(chalk_1.default.dim(' Available keys: compact\n'));
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
// ==================== PAYOUTS (Admin) ====================
|
|
1658
|
+
program
|
|
1659
|
+
.command('payouts')
|
|
1660
|
+
.alias('pay')
|
|
1661
|
+
.description('Payout processing dashboard (managers/executors only)')
|
|
1662
|
+
.option('-l, --list', 'List pending payouts')
|
|
1663
|
+
.option('-p, --plan <plan>', 'Filter by plan (hermes, alpha, mematic, validator_v2, booster)')
|
|
1664
|
+
.option('-a, --approve <ids...>', 'Approve payout IDs')
|
|
1665
|
+
.option('-c, --cancel <ids...>', 'Cancel payout IDs')
|
|
1666
|
+
.action(async (options) => {
|
|
1667
|
+
setTitle('Payouts');
|
|
1668
|
+
requireAuth();
|
|
1669
|
+
if (options.list) {
|
|
1670
|
+
await listPayoutsCmd({ plan: options.plan });
|
|
1671
|
+
}
|
|
1672
|
+
else if (options.approve) {
|
|
1673
|
+
const ids = options.approve.map(Number).filter((n) => !isNaN(n));
|
|
1674
|
+
if (ids.length > 0) {
|
|
1675
|
+
await processPayoutsWithProgress(ids, 'approve');
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
else if (options.cancel) {
|
|
1679
|
+
const ids = options.cancel.map(Number).filter((n) => !isNaN(n));
|
|
1680
|
+
if (ids.length > 0) {
|
|
1681
|
+
await processPayoutsWithProgress(ids, 'cancel');
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
else {
|
|
1685
|
+
// Interactive dashboard mode
|
|
1686
|
+
await runPayoutsShell();
|
|
1687
|
+
}
|
|
1688
|
+
showStatusBar();
|
|
1689
|
+
});
|
|
1690
|
+
// Default action - show help or run shell if authenticated
|
|
1691
|
+
if (process.argv.length === 2) {
|
|
1692
|
+
if ((0, config_1.isAuthenticated)()) {
|
|
1693
|
+
// Run interactive shell
|
|
1694
|
+
process.argv.push('shell');
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
// Show help
|
|
1698
|
+
program.outputHelp();
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
// Parse and run
|
|
1702
|
+
program.parse();
|
|
1703
|
+
//# sourceMappingURL=index.js.map
|