@targlobal/mission-control 1.3.3 → 1.4.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/dist/index.js CHANGED
@@ -45,7 +45,7 @@ const inquirer_1 = __importDefault(require("inquirer"));
45
45
  const config_1 = require("./config");
46
46
  const api_1 = require("./api");
47
47
  const child_process_1 = require("child_process");
48
- const VERSION = '1.3.3';
48
+ const VERSION = '1.4.0';
49
49
  const program = new commander_1.Command();
50
50
  // Set terminal title
51
51
  const setTitle = (context) => {
@@ -705,6 +705,7 @@ const PLAN_COLORS = {
705
705
  validator_v2: chalk_1.default.blue,
706
706
  booster: chalk_1.default.green,
707
707
  dumpster: chalk_1.default.gray,
708
+ christmas: chalk_1.default.red,
708
709
  };
709
710
  const PLAN_ICONS = {
710
711
  hermes: '⚡',
@@ -713,6 +714,43 @@ const PLAN_ICONS = {
713
714
  validator_v2: '🔐',
714
715
  booster: '🚀',
715
716
  dumpster: '🗑️',
717
+ christmas: '🎄',
718
+ };
719
+ // Helper to get percentage (preset or custom input)
720
+ const getPercentageWithCustom = async () => {
721
+ const { percentage } = await inquirer_1.default.prompt([
722
+ {
723
+ type: 'list',
724
+ name: 'percentage',
725
+ message: 'Select percentage of remaining amount to pay:',
726
+ choices: [
727
+ { name: '10%', value: 10 },
728
+ { name: '25%', value: 25 },
729
+ { name: '50%', value: 50 },
730
+ { name: '75%', value: 75 },
731
+ { name: '100% (Full)', value: 100 },
732
+ { name: chalk_1.default.cyan('Custom...'), value: -1 },
733
+ ],
734
+ },
735
+ ]);
736
+ if (percentage === -1) {
737
+ const { customPct } = await inquirer_1.default.prompt([
738
+ {
739
+ type: 'input',
740
+ name: 'customPct',
741
+ message: 'Enter custom percentage (1-100):',
742
+ validate: (input) => {
743
+ const num = parseFloat(input);
744
+ if (isNaN(num) || num < 1 || num > 100) {
745
+ return 'Please enter a number between 1 and 100';
746
+ }
747
+ return true;
748
+ },
749
+ },
750
+ ]);
751
+ return parseFloat(customPct);
752
+ }
753
+ return percentage;
716
754
  };
717
755
  const formatAmount = (amount, crypto) => {
718
756
  const formatted = amount.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 4 });
@@ -1182,6 +1220,413 @@ const processPayoutsWithProgress = async (ids, action, percentage) => {
1182
1220
  return { success: false };
1183
1221
  }
1184
1222
  };
1223
+ // ==================== CHRISTMAS/TARPAY PAYOUT FUNCTIONS ====================
1224
+ const listChristmasPayoutsCmd = async (options) => {
1225
+ const spinner = (0, ora_1.default)('Loading Christmas payouts...').start();
1226
+ const compact = isCompact();
1227
+ try {
1228
+ const data = await api_1.api.getTarPayPayouts({
1229
+ plan: 'christmas',
1230
+ status: options.status || 'pending_review',
1231
+ limit: compact ? 10 : 20,
1232
+ offset: ((options.page || 1) - 1) * (compact ? 10 : 20),
1233
+ });
1234
+ const payouts = data.results || [];
1235
+ spinner.stop();
1236
+ if (payouts.length === 0) {
1237
+ console.log(chalk_1.default.dim('\n No Christmas payouts found\n'));
1238
+ return;
1239
+ }
1240
+ // Info banner about $50 threshold
1241
+ console.log('');
1242
+ console.log(chalk_1.default.red('🎄 CHRISTMAS PAYOUTS') + chalk_1.default.dim(' (TarPay)'));
1243
+ console.log(chalk_1.default.dim(' Payouts > $50 require manual approval. < $50 are auto-sent.'));
1244
+ console.log('');
1245
+ if (compact) {
1246
+ payouts.forEach((p) => {
1247
+ const isPartial = p.has_partial_payments && (p.paid_percentage || 0) > 0;
1248
+ const statusIcon = p.status === 'completed' ? chalk_1.default.green('✓') :
1249
+ p.status === 'failed' ? chalk_1.default.red('✗') :
1250
+ isPartial ? chalk_1.default.cyan('%') : chalk_1.default.yellow('●');
1251
+ const email = p.user_email.length > 12 ? p.user_email.substring(0, 10) + '..' : p.user_email;
1252
+ const displayAmt = isPartial ? (p.remaining_amount || parseFloat(p.amount)) : parseFloat(p.amount);
1253
+ const amountStr = isPartial ? `${displayAmt.toFixed(2)}(${Math.round(p.paid_percentage || 0)}%)` : displayAmt.toFixed(2);
1254
+ console.log(` ${statusIcon} ${chalk_1.default.dim('#' + p.id)} ${chalk_1.default.red('xmas')} ${email} ${chalk_1.default.green(amountStr)} ${p.currency}`);
1255
+ });
1256
+ console.log(chalk_1.default.dim(`\n ${data.count} total\n`));
1257
+ }
1258
+ else {
1259
+ const table = new cli_table3_1.default({
1260
+ head: [
1261
+ chalk_1.default.cyan('ID'),
1262
+ chalk_1.default.cyan('User'),
1263
+ chalk_1.default.cyan('Remaining'),
1264
+ chalk_1.default.cyan('Crypto'),
1265
+ chalk_1.default.cyan('Progress'),
1266
+ chalk_1.default.cyan('Status'),
1267
+ ],
1268
+ style: { head: [], border: [] },
1269
+ colWidths: [8, 24, 14, 10, 12, 14],
1270
+ });
1271
+ payouts.forEach((p) => {
1272
+ const isPartial = p.has_partial_payments && (p.paid_percentage || 0) > 0;
1273
+ const paidPct = p.paid_percentage || 0;
1274
+ const displayAmount = isPartial ? (p.remaining_amount || parseFloat(p.amount)) : parseFloat(p.amount);
1275
+ let statusIcon;
1276
+ if (p.status === 'completed') {
1277
+ statusIcon = chalk_1.default.green('✓ Done');
1278
+ }
1279
+ else if (p.status === 'failed') {
1280
+ statusIcon = chalk_1.default.red('✗ Failed');
1281
+ }
1282
+ else if (isPartial) {
1283
+ statusIcon = chalk_1.default.cyan(`% ${Math.round(paidPct)}%`);
1284
+ }
1285
+ else if (p.status === 'pending_review') {
1286
+ statusIcon = chalk_1.default.yellow('● Review');
1287
+ }
1288
+ else {
1289
+ statusIcon = chalk_1.default.blue('~ ' + p.status);
1290
+ }
1291
+ // Progress bar for partial payments
1292
+ let progressStr = '-';
1293
+ if (isPartial) {
1294
+ const filled = Math.round(paidPct / 10);
1295
+ const empty = 10 - filled;
1296
+ progressStr = chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.dim('░'.repeat(empty));
1297
+ }
1298
+ table.push([
1299
+ chalk_1.default.dim(String(p.id)),
1300
+ p.user_email.length > 22 ? p.user_email.substring(0, 20) + '..' : p.user_email,
1301
+ chalk_1.default.green('$' + displayAmount.toFixed(2)),
1302
+ p.currency,
1303
+ progressStr,
1304
+ statusIcon,
1305
+ ]);
1306
+ });
1307
+ console.log(table.toString());
1308
+ console.log(chalk_1.default.dim(`\n ${data.count} total\n`));
1309
+ }
1310
+ }
1311
+ catch (error) {
1312
+ spinner.fail(chalk_1.default.red('Failed to load Christmas payouts'));
1313
+ console.log(chalk_1.default.dim(` ${error.response?.data?.detail || error.message}\n`));
1314
+ }
1315
+ };
1316
+ const selectChristmasPayoutsInteractive = async (status) => {
1317
+ const spinner = (0, ora_1.default)('Loading Christmas payouts...').start();
1318
+ try {
1319
+ const data = await api_1.api.getTarPayPayouts({
1320
+ plan: 'christmas',
1321
+ status: status || 'pending_review',
1322
+ limit: 100,
1323
+ });
1324
+ const payouts = data.results || [];
1325
+ spinner.stop();
1326
+ if (payouts.length === 0) {
1327
+ console.log(chalk_1.default.dim('\n No Christmas payouts found\n'));
1328
+ return [];
1329
+ }
1330
+ const choices = payouts.map((p) => {
1331
+ const isPartial = p.has_partial_payments && (p.paid_percentage || 0) > 0;
1332
+ const displayAmount = isPartial ? (p.remaining_amount || parseFloat(p.amount)) : parseFloat(p.amount);
1333
+ const progressStr = isPartial ? chalk_1.default.cyan(` (${Math.round(p.paid_percentage || 0)}% paid)`) : '';
1334
+ return {
1335
+ name: `${chalk_1.default.red('🎄')} ${chalk_1.default.dim('#' + p.id)} ${p.user_email.substring(0, 25).padEnd(25)} ${chalk_1.default.green('$' + displayAmount.toFixed(2).padStart(10))} ${chalk_1.default.dim(p.currency)}${progressStr}`,
1336
+ value: p.id,
1337
+ short: `#${p.id}`,
1338
+ };
1339
+ });
1340
+ const { selected } = await inquirer_1.default.prompt([
1341
+ {
1342
+ type: 'checkbox',
1343
+ name: 'selected',
1344
+ message: 'Select Christmas payouts to process:',
1345
+ choices,
1346
+ pageSize: 15,
1347
+ loop: false,
1348
+ },
1349
+ ]);
1350
+ return selected;
1351
+ }
1352
+ catch (error) {
1353
+ spinner.fail(chalk_1.default.red('Failed to load Christmas payouts'));
1354
+ return [];
1355
+ }
1356
+ };
1357
+ const processChristmasPayoutsWithProgress = async (ids, action, percentage) => {
1358
+ const actionLabel = action === 'partial' ? `PARTIAL ${percentage}%` : action.toUpperCase();
1359
+ console.log('\n' + chalk_1.default.red('╔══════════════════════════════════════════════════════════════╗'));
1360
+ console.log(chalk_1.default.red('║') + chalk_1.default.bold.white(` 🎄 CHRISTMAS ${actionLabel} - ${ids.length} PAYOUT(S)... `.substring(0, 60)) + chalk_1.default.red('║'));
1361
+ console.log(chalk_1.default.red('╠══════════════════════════════════════════════════════════════╣'));
1362
+ const startTime = Date.now();
1363
+ // Show progress
1364
+ let processed = 0;
1365
+ const progressBar = (current, total) => {
1366
+ const percent = Math.round((current / total) * 100);
1367
+ const filled = Math.round(percent / 2);
1368
+ const empty = 50 - filled;
1369
+ return chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.dim('░'.repeat(empty)) + ` ${percent}%`;
1370
+ };
1371
+ const progressInterval = setInterval(() => {
1372
+ processed = Math.min(processed + Math.random() * 10, 90);
1373
+ process.stdout.write(`\r${chalk_1.default.red('║')} ${progressBar(processed, 100)} ${chalk_1.default.red('║')}`);
1374
+ }, 200);
1375
+ try {
1376
+ let result;
1377
+ let successCount = 0;
1378
+ let failedCount = 0;
1379
+ const errors = [];
1380
+ if (action === 'partial') {
1381
+ result = await api_1.api.batchTarPayPartialPayouts(ids, percentage);
1382
+ successCount = result.success_count || 0;
1383
+ failedCount = result.failed_count || 0;
1384
+ }
1385
+ else {
1386
+ // Process approve/reject one by one
1387
+ for (const id of ids) {
1388
+ try {
1389
+ if (action === 'approve') {
1390
+ await api_1.api.approveTarPayPayout(id);
1391
+ }
1392
+ else {
1393
+ await api_1.api.rejectTarPayPayout(id);
1394
+ }
1395
+ successCount++;
1396
+ }
1397
+ catch (e) {
1398
+ failedCount++;
1399
+ errors.push(`#${id}: ${e.response?.data?.detail || e.message}`);
1400
+ }
1401
+ }
1402
+ result = { success: true, success_count: successCount, failed_count: failedCount, errors };
1403
+ }
1404
+ clearInterval(progressInterval);
1405
+ processed = 100;
1406
+ process.stdout.write(`\r${chalk_1.default.red('║')} ${progressBar(100, 100)} ${chalk_1.default.red('║')}\n`);
1407
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
1408
+ console.log(chalk_1.default.red('╠══════════════════════════════════════════════════════════════╣'));
1409
+ let icon, verb;
1410
+ if (action === 'approve') {
1411
+ icon = '✓';
1412
+ verb = 'APPROVED';
1413
+ }
1414
+ else if (action === 'partial') {
1415
+ icon = '%';
1416
+ verb = `PARTIAL ${percentage}% SENT`;
1417
+ }
1418
+ else {
1419
+ icon = '↩';
1420
+ verb = 'REJECTED';
1421
+ }
1422
+ console.log(chalk_1.default.red('║') + chalk_1.default.green(` ${icon} ${verb} `.substring(0, 60)) + chalk_1.default.red('║'));
1423
+ console.log(chalk_1.default.red('║') + ` Successful: ${chalk_1.default.bold.green(String(successCount).padEnd(5))} Failed: ${chalk_1.default.bold.red(String(failedCount).padEnd(5))} Time: ${chalk_1.default.dim(elapsed + 's')}`.padEnd(62) + chalk_1.default.red('║'));
1424
+ console.log(chalk_1.default.red('╚══════════════════════════════════════════════════════════════╝\n'));
1425
+ // Show errors if any
1426
+ const errList = result.errors || errors;
1427
+ if (errList.length > 0) {
1428
+ console.log(chalk_1.default.red(' Errors:'));
1429
+ errList.slice(0, 5).forEach((err) => {
1430
+ const errMsg = typeof err === 'string' ? err : `Payout ${err.payout_id}: ${err.error}`;
1431
+ console.log(chalk_1.default.dim(` - ${errMsg}`));
1432
+ });
1433
+ if (errList.length > 5) {
1434
+ console.log(chalk_1.default.dim(` ... and ${errList.length - 5} more errors`));
1435
+ }
1436
+ console.log('');
1437
+ }
1438
+ return result;
1439
+ }
1440
+ catch (error) {
1441
+ clearInterval(progressInterval);
1442
+ console.log(chalk_1.default.red('║') + chalk_1.default.red(' ✗ ERROR: ' + (error.response?.data?.detail || error.message).substring(0, 50).padEnd(52)) + chalk_1.default.red('║'));
1443
+ console.log(chalk_1.default.red('╚══════════════════════════════════════════════════════════════╝\n'));
1444
+ return { success: false };
1445
+ }
1446
+ };
1447
+ const runChristmasPayoutsShell = async () => {
1448
+ setTitle('Christmas Payouts');
1449
+ // Show initial list
1450
+ await listChristmasPayoutsCmd({ status: 'pending_review' });
1451
+ const runLoop = async () => {
1452
+ const { command } = await inquirer_1.default.prompt([
1453
+ {
1454
+ type: 'input',
1455
+ name: 'command',
1456
+ message: chalk_1.default.red('christmas') + chalk_1.default.cyan(' > '),
1457
+ prefix: '',
1458
+ },
1459
+ ]);
1460
+ const parts = command.trim().split(/\s+/);
1461
+ const cmd = parts[0]?.toLowerCase();
1462
+ if (cmd === 'back' || cmd === 'exit' || cmd === 'q') {
1463
+ setTitle('Payouts');
1464
+ return;
1465
+ }
1466
+ if (cmd === '') {
1467
+ return runLoop();
1468
+ }
1469
+ try {
1470
+ switch (cmd) {
1471
+ case 'refresh':
1472
+ case 'r':
1473
+ case 'list':
1474
+ case 'ls':
1475
+ await listChristmasPayoutsCmd({ status: 'pending_review' });
1476
+ break;
1477
+ case 'all':
1478
+ await listChristmasPayoutsCmd({});
1479
+ break;
1480
+ case 'done':
1481
+ case 'completed':
1482
+ await listChristmasPayoutsCmd({ status: 'completed' });
1483
+ break;
1484
+ case 'failed':
1485
+ await listChristmasPayoutsCmd({ status: 'failed' });
1486
+ break;
1487
+ case 'select':
1488
+ case 's':
1489
+ const selectedIds = await selectChristmasPayoutsInteractive('pending_review');
1490
+ if (selectedIds.length > 0) {
1491
+ const { action } = await inquirer_1.default.prompt([
1492
+ {
1493
+ type: 'list',
1494
+ name: 'action',
1495
+ message: `What do you want to do with ${selectedIds.length} selected payout(s)?`,
1496
+ choices: [
1497
+ { name: chalk_1.default.green('✓ Approve & Send 100%'), value: 'approve' },
1498
+ { name: chalk_1.default.cyan('% Pay Partial % (custom amount)'), value: 'partial' },
1499
+ { name: chalk_1.default.yellow('↩ Reject'), value: 'reject' },
1500
+ { name: chalk_1.default.dim('✗ Cancel'), value: 'none' },
1501
+ ],
1502
+ },
1503
+ ]);
1504
+ if (action === 'approve') {
1505
+ const { confirm } = await inquirer_1.default.prompt([
1506
+ {
1507
+ type: 'confirm',
1508
+ name: 'confirm',
1509
+ message: chalk_1.default.yellow(`⚠️ Confirm: Approve ${selectedIds.length} payout(s)?`),
1510
+ default: false,
1511
+ },
1512
+ ]);
1513
+ if (confirm) {
1514
+ await processChristmasPayoutsWithProgress(selectedIds, 'approve');
1515
+ }
1516
+ }
1517
+ else if (action === 'partial') {
1518
+ const percentage = await getPercentageWithCustom();
1519
+ if (percentage !== null) {
1520
+ const { confirm } = await inquirer_1.default.prompt([
1521
+ {
1522
+ type: 'confirm',
1523
+ name: 'confirm',
1524
+ message: chalk_1.default.yellow(`⚠️ Confirm: Pay ${percentage}% of ${selectedIds.length} payout(s)?`),
1525
+ default: false,
1526
+ },
1527
+ ]);
1528
+ if (confirm) {
1529
+ await processChristmasPayoutsWithProgress(selectedIds, 'partial', percentage);
1530
+ }
1531
+ }
1532
+ }
1533
+ else if (action === 'reject') {
1534
+ const { confirm } = await inquirer_1.default.prompt([
1535
+ {
1536
+ type: 'confirm',
1537
+ name: 'confirm',
1538
+ message: chalk_1.default.yellow(`⚠️ Confirm: Reject ${selectedIds.length} payout(s)?`),
1539
+ default: false,
1540
+ },
1541
+ ]);
1542
+ if (confirm) {
1543
+ await processChristmasPayoutsWithProgress(selectedIds, 'reject');
1544
+ }
1545
+ }
1546
+ }
1547
+ break;
1548
+ case 'approve':
1549
+ case 'a':
1550
+ const approveIds = await selectChristmasPayoutsInteractive('pending_review');
1551
+ if (approveIds.length > 0) {
1552
+ const { confirm } = await inquirer_1.default.prompt([
1553
+ {
1554
+ type: 'confirm',
1555
+ name: 'confirm',
1556
+ message: chalk_1.default.yellow(`⚠️ Approve ${approveIds.length} payout(s)?`),
1557
+ default: false,
1558
+ },
1559
+ ]);
1560
+ if (confirm) {
1561
+ await processChristmasPayoutsWithProgress(approveIds, 'approve');
1562
+ }
1563
+ }
1564
+ break;
1565
+ case 'partial':
1566
+ case 'p':
1567
+ const partialIds = await selectChristmasPayoutsInteractive('pending_review');
1568
+ if (partialIds.length > 0) {
1569
+ const percentage = await getPercentageWithCustom();
1570
+ if (percentage !== null) {
1571
+ const { confirm } = await inquirer_1.default.prompt([
1572
+ {
1573
+ type: 'confirm',
1574
+ name: 'confirm',
1575
+ message: chalk_1.default.yellow(`⚠️ Pay ${percentage}% of ${partialIds.length} payout(s)?`),
1576
+ default: false,
1577
+ },
1578
+ ]);
1579
+ if (confirm) {
1580
+ await processChristmasPayoutsWithProgress(partialIds, 'partial', percentage);
1581
+ }
1582
+ }
1583
+ }
1584
+ break;
1585
+ case 'reject':
1586
+ const rejectIds = await selectChristmasPayoutsInteractive('pending_review');
1587
+ if (rejectIds.length > 0) {
1588
+ const { confirm } = await inquirer_1.default.prompt([
1589
+ {
1590
+ type: 'confirm',
1591
+ name: 'confirm',
1592
+ message: chalk_1.default.yellow(`⚠️ Reject ${rejectIds.length} payout(s)?`),
1593
+ default: false,
1594
+ },
1595
+ ]);
1596
+ if (confirm) {
1597
+ await processChristmasPayoutsWithProgress(rejectIds, 'reject');
1598
+ }
1599
+ }
1600
+ break;
1601
+ case 'help':
1602
+ case '?':
1603
+ console.log(chalk_1.default.red('\n 🎄 Christmas Payout Commands:'));
1604
+ console.log(chalk_1.default.dim(' ────────────────────────────────'));
1605
+ console.log(' refresh, r Refresh pending payouts');
1606
+ console.log(' list, ls List pending_review payouts');
1607
+ console.log(' all List all payouts (any status)');
1608
+ console.log(' done List completed payouts');
1609
+ console.log(' failed List failed payouts');
1610
+ console.log(' select, s Interactive selection mode');
1611
+ console.log(' approve, a Select & approve payouts');
1612
+ console.log(chalk_1.default.cyan(' partial, p Select & pay partial %'));
1613
+ console.log(' reject Select & reject payouts');
1614
+ console.log(' back, q Return to main payouts');
1615
+ console.log(chalk_1.default.dim('\n Note: Payouts > $50 need approval, < $50 auto-sent'));
1616
+ console.log('');
1617
+ break;
1618
+ default:
1619
+ console.log(chalk_1.default.red(` Unknown command: ${cmd}`));
1620
+ console.log(chalk_1.default.dim(' Type "help" for available commands\n'));
1621
+ }
1622
+ }
1623
+ catch (e) {
1624
+ console.log(chalk_1.default.red(` Error: ${e.message}\n`));
1625
+ }
1626
+ await runLoop();
1627
+ };
1628
+ await runLoop();
1629
+ };
1185
1630
  const runPayoutsShell = async () => {
1186
1631
  setTitle('Payouts');
1187
1632
  await showPayoutDashboard();
@@ -1248,30 +1693,19 @@ const runPayoutsShell = async () => {
1248
1693
  }
1249
1694
  }
1250
1695
  else if (action === 'partial') {
1251
- const { percentage } = await inquirer_1.default.prompt([
1252
- {
1253
- type: 'list',
1254
- name: 'percentage',
1255
- message: 'Select percentage of remaining amount to pay:',
1256
- choices: [
1257
- { name: '10%', value: 10 },
1258
- { name: '25%', value: 25 },
1259
- { name: '50%', value: 50 },
1260
- { name: '75%', value: 75 },
1261
- { name: '100% (Full)', value: 100 },
1262
- ],
1263
- },
1264
- ]);
1265
- const { confirm } = await inquirer_1.default.prompt([
1266
- {
1267
- type: 'confirm',
1268
- name: 'confirm',
1269
- message: chalk_1.default.yellow(`⚠️ Confirm: Pay ${percentage}% of ${selectedIds.length} payout(s)?`),
1270
- default: false,
1271
- },
1272
- ]);
1273
- if (confirm) {
1274
- await processPayoutsWithProgress(selectedIds, 'partial', percentage);
1696
+ const percentage = await getPercentageWithCustom();
1697
+ if (percentage !== null) {
1698
+ const { confirm } = await inquirer_1.default.prompt([
1699
+ {
1700
+ type: 'confirm',
1701
+ name: 'confirm',
1702
+ message: chalk_1.default.yellow(`⚠️ Confirm: Pay ${percentage}% of ${selectedIds.length} payout(s)?`),
1703
+ default: false,
1704
+ },
1705
+ ]);
1706
+ if (confirm) {
1707
+ await processPayoutsWithProgress(selectedIds, 'partial', percentage);
1708
+ }
1275
1709
  }
1276
1710
  }
1277
1711
  else if (action === 'cancel') {
@@ -1343,30 +1777,19 @@ const runPayoutsShell = async () => {
1343
1777
  const partialPlan = args.find((a) => !a.startsWith('-') && isNaN(Number(a)));
1344
1778
  const partialSelected = await selectPayoutsInteractive(partialPlan);
1345
1779
  if (partialSelected.length > 0) {
1346
- const { partialPercentage } = await inquirer_1.default.prompt([
1347
- {
1348
- type: 'list',
1349
- name: 'partialPercentage',
1350
- message: 'Select percentage of remaining amount to pay:',
1351
- choices: [
1352
- { name: '10%', value: 10 },
1353
- { name: '25%', value: 25 },
1354
- { name: '50%', value: 50 },
1355
- { name: '75%', value: 75 },
1356
- { name: '100% (Full)', value: 100 },
1357
- ],
1358
- },
1359
- ]);
1360
- const { confirmPartial } = await inquirer_1.default.prompt([
1361
- {
1362
- type: 'confirm',
1363
- name: 'confirmPartial',
1364
- message: chalk_1.default.yellow(`⚠️ Pay ${partialPercentage}% of ${partialSelected.length} payout(s)?`),
1365
- default: false,
1366
- },
1367
- ]);
1368
- if (confirmPartial) {
1369
- await processPayoutsWithProgress(partialSelected, 'partial', partialPercentage);
1780
+ const partialPercentage = await getPercentageWithCustom();
1781
+ if (partialPercentage !== null) {
1782
+ const { confirmPartial } = await inquirer_1.default.prompt([
1783
+ {
1784
+ type: 'confirm',
1785
+ name: 'confirmPartial',
1786
+ message: chalk_1.default.yellow(`⚠️ Pay ${partialPercentage}% of ${partialSelected.length} payout(s)?`),
1787
+ default: false,
1788
+ },
1789
+ ]);
1790
+ if (confirmPartial) {
1791
+ await processPayoutsWithProgress(partialSelected, 'partial', partialPercentage);
1792
+ }
1370
1793
  }
1371
1794
  }
1372
1795
  break;
@@ -1377,6 +1800,11 @@ const runPayoutsShell = async () => {
1377
1800
  case 'validator_v2':
1378
1801
  await listPayoutsCmd({ plan: cmd });
1379
1802
  break;
1803
+ case 'christmas':
1804
+ case 'xmas':
1805
+ // Christmas payouts subshell
1806
+ await runChristmasPayoutsShell();
1807
+ break;
1380
1808
  case 'process':
1381
1809
  // Process specific plan with interactive selection
1382
1810
  const { processPlan } = await inquirer_1.default.prompt([
@@ -1455,6 +1883,7 @@ const runPayoutsShell = async () => {
1455
1883
  console.log(' cancel [ids] Cancel payouts (interactive if no IDs)');
1456
1884
  console.log(' process Process payouts by plan (interactive)');
1457
1885
  console.log(' hermes/alpha/etc List payouts for specific plan');
1886
+ console.log(chalk_1.default.red(' christmas, xmas 🎄 Christmas payouts (TarPay)'));
1458
1887
  console.log(' back, q Return to main menu');
1459
1888
  console.log('');
1460
1889
  break;