dankgrinder 5.281.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/grinder.js +156 -83
- package/package.json +1 -1
package/lib/grinder.js
CHANGED
|
@@ -219,6 +219,23 @@ function gradientLine(text, from, to) {
|
|
|
219
219
|
return out + c.reset;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// ── Sparkline graph for earnings trend ───────────────────────
|
|
223
|
+
const SPARK_CHARS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
224
|
+
function drawSparkline(data, width = 12) {
|
|
225
|
+
if (!data || data.length === 0) return c.dim + '──────' + c.reset;
|
|
226
|
+
const recent = data.slice(-width);
|
|
227
|
+
const min = Math.min(...recent);
|
|
228
|
+
const max = Math.max(...recent);
|
|
229
|
+
const range = max - min || 1;
|
|
230
|
+
return recent.map(v => {
|
|
231
|
+
const idx = Math.min(7, Math.floor(((v - min) / range) * 8));
|
|
232
|
+
const char = SPARK_CHARS[idx] || '▁';
|
|
233
|
+
return v >= max * 0.9 ? rgb(52, 211, 153) + char + c.reset :
|
|
234
|
+
v <= min * 1.1 ? rgb(239, 68, 68) + char + c.reset :
|
|
235
|
+
rgb(139, 92, 246) + char + c.reset;
|
|
236
|
+
}).join('');
|
|
237
|
+
}
|
|
238
|
+
|
|
222
239
|
const BANNER_RAW = [
|
|
223
240
|
' ██████╗ █████╗ ███╗ ██╗██╗ ██╗',
|
|
224
241
|
' ██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝',
|
|
@@ -264,10 +281,15 @@ let totalCoins = 0;
|
|
|
264
281
|
let totalCommands = 0;
|
|
265
282
|
let startTime = Date.now();
|
|
266
283
|
let shutdownCalled = false;
|
|
284
|
+
let sessionPeakCoins = 0;
|
|
285
|
+
let isNewHigh = false;
|
|
267
286
|
// RingBuffer: O(1) push, bounded memory, no array shifting or GC pressure
|
|
268
287
|
const recentLogs = new RingBuffer(6);
|
|
269
288
|
const MAX_LOGS = 6;
|
|
270
289
|
const RENDER_THROTTLE_MS = 250;
|
|
290
|
+
// Earnings history for sparkline (sample every 10 seconds)
|
|
291
|
+
const earningsHistory = new RingBuffer(20);
|
|
292
|
+
let lastEarningsSample = 0;
|
|
271
293
|
|
|
272
294
|
function formatUptime() {
|
|
273
295
|
const s = Math.floor((Date.now() - startTime) / 1000);
|
|
@@ -311,70 +333,109 @@ function renderDashboard() {
|
|
|
311
333
|
totalErrors += w.stats.errors || 0;
|
|
312
334
|
}
|
|
313
335
|
const successRate = totalCommands > 0 ? Math.round(((totalCommands - totalErrors) / totalCommands) * 100) : 100;
|
|
336
|
+
// Track session peak and new high
|
|
337
|
+
if (totalCoins > sessionPeakCoins) {
|
|
338
|
+
sessionPeakCoins = totalCoins;
|
|
339
|
+
isNewHigh = true;
|
|
340
|
+
setTimeout(() => { isNewHigh = false; }, 3000);
|
|
341
|
+
}
|
|
314
342
|
|
|
315
343
|
const lines = [];
|
|
316
344
|
const tw = Math.min(process.stdout.columns || 80, 78);
|
|
317
345
|
const thinBar = c.dim + '─'.repeat(tw) + c.reset;
|
|
318
346
|
const bar = rgb(139, 92, 246) + c.bold + '━'.repeat(tw) + c.reset;
|
|
319
347
|
const doubleBar = rgb(139, 92, 246) + '╔' + '═'.repeat(tw - 2) + '╗' + c.reset;
|
|
348
|
+
const doubleBarMid = rgb(139, 92, 246) + '╠' + '═'.repeat(tw - 2) + '╣' + c.reset;
|
|
320
349
|
const doubleBarBot = rgb(139, 92, 246) + '╚' + '═'.repeat(tw - 2) + '╝' + c.reset;
|
|
321
350
|
|
|
322
|
-
// Header with
|
|
351
|
+
// Header with title, stats, and mode
|
|
323
352
|
lines.push(doubleBar);
|
|
324
353
|
const cmdCount = AccountWorker.COMMAND_MAP.length;
|
|
325
354
|
const activeCount = workers.filter(w => w.running && !w.paused && !w.dashboardPaused).length;
|
|
326
355
|
const mode = CLUSTER_ENABLED ? `${rgb(34, 211, 238)}Cluster${c.reset}` : `${c.dim}Standalone${c.reset}`;
|
|
327
356
|
|
|
328
|
-
// Animated spinner
|
|
357
|
+
// Animated spinner and gradient title
|
|
329
358
|
const spinners = ['◐', '◓', '◑', '◒'];
|
|
330
359
|
const spinner = spinners[Math.floor(Date.now() / 250) % 4];
|
|
331
360
|
const animatedSpinner = `${rgb(52, 211, 153)}${spinner}${c.reset}`;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
|
|
361
|
+
const gradientTitle = gradientLine('DankGrinder', [192, 132, 252], [34, 211, 238]);
|
|
362
|
+
|
|
363
|
+
// Network quality indicator (based on recent latency/errors)
|
|
364
|
+
const netQuality = workers.length > 0
|
|
365
|
+
? workers.reduce((s, w) => s + (w._lastPing < 200 ? 1 : w._lastPing < 500 ? 0.5 : 0), 0) / workers.length
|
|
366
|
+
: 1;
|
|
367
|
+
const netIcon = netQuality > 0.8 ? `${rgb(52, 211, 153)}◉${c.reset}` :
|
|
368
|
+
netQuality > 0.5 ? `${rgb(251, 191, 36)}◉${c.reset}` :
|
|
369
|
+
`${rgb(239, 68, 68)}◉${c.reset}`;
|
|
370
|
+
const netLabel = netQuality > 0.8 ? `${c.dim}good${c.reset}` :
|
|
371
|
+
netQuality > 0.5 ? `${c.dim}fair${c.reset}` :
|
|
372
|
+
`${c.dim}poor${c.reset}`;
|
|
373
|
+
|
|
374
|
+
lines.push(` ${c.bold}${gradientTitle}${c.reset} ${c.dim}v${PKG_VERSION}${c.reset}`);
|
|
375
|
+
lines.push(` ${mode} ${c.dim}·${c.reset} ${netIcon} ${netLabel} ${c.dim}·${c.reset} ${c.white}${cmdCount} commands${c.reset} ${c.dim}·${c.reset} ${animatedSpinner} ${rgb(52, 211, 153)}${activeCount}${c.reset}${c.dim}/${c.reset}${c.white}${workers.length}${c.reset} ${c.dim}live${c.reset}`);
|
|
376
|
+
|
|
377
|
+
// Stats row 1: Balance & Earnings
|
|
378
|
+
lines.push(doubleBarMid);
|
|
341
379
|
const liveIcon = rgb(52, 211, 153) + '●' + c.reset;
|
|
342
380
|
const balStr = `${rgb(192, 132, 252)}${c.bold}⏣ ${formatCoins(totalBalance)}${c.reset}`;
|
|
343
381
|
const earnStr = `${rgb(52, 211, 153)}▲ ${formatCoins(totalCoins)}${c.reset}`;
|
|
344
|
-
// Coins/hour rate
|
|
345
382
|
const elapsedHrs = (Date.now() - startTime) / 3_600_000;
|
|
346
383
|
const coinsPerHr = elapsedHrs > 0.01 ? Math.round(totalCoins / elapsedHrs) : 0;
|
|
347
384
|
const rateLabel = `${rgb(52, 211, 153)}${formatCoins(coinsPerHr)}/h${c.reset}`;
|
|
385
|
+
|
|
386
|
+
// Stats row 2: Commands & Performance
|
|
348
387
|
const cmdStr = `${rgb(96, 165, 250)}${totalCommands}${c.reset}${c.dim} cmds${c.reset}`;
|
|
349
388
|
const rateStr = successRate >= 95
|
|
350
389
|
? `${rgb(52, 211, 153)}● ${successRate}%${c.reset}`
|
|
351
390
|
: successRate >= 80 ? `${rgb(251, 191, 36)}● ${successRate}%${c.reset}`
|
|
352
391
|
: `${rgb(239, 68, 68)}● ${successRate}%${c.reset}`;
|
|
392
|
+
const cpmVal = globalCmdRate.getRate().toFixed(1);
|
|
393
|
+
const cpmStr = `${rgb(34, 211, 238)}${cpmVal}${c.reset}${c.dim}/m${c.reset}`;
|
|
394
|
+
|
|
395
|
+
// Stats row 3: Uptime & Memory
|
|
353
396
|
const upStr = `${rgb(251, 191, 36)}◷ ${formatUptime()}${c.reset}`;
|
|
354
|
-
// Memory usage (RSS in MB) with bar indicator
|
|
355
397
|
const memMB = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
|
|
356
398
|
const memPct = Math.min(100, (memMB / 1024) * 100);
|
|
357
399
|
const memBarWidth = Math.floor((memPct / 100) * 10);
|
|
358
400
|
const memBar = rgb(52, 211, 153) + '▅'.repeat(memBarWidth) + c.dim + '▅'.repeat(10 - memBarWidth) + c.reset;
|
|
359
401
|
const memColor = memMB > 900 ? rgb(239, 68, 68) : memMB > 600 ? rgb(251, 191, 36) : rgb(52, 211, 153);
|
|
360
402
|
const memStr = `${memColor}${memMB}MB${c.reset} ${memBar}`;
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
403
|
+
|
|
404
|
+
// Sample earnings for sparkline every 10 seconds
|
|
405
|
+
const now = Date.now();
|
|
406
|
+
if (now - lastEarningsSample > 10000) {
|
|
407
|
+
earningsHistory.push(totalCoins);
|
|
408
|
+
lastEarningsSample = now;
|
|
409
|
+
}
|
|
410
|
+
const sparkline = drawSparkline(earningsHistory.toArray(), 16);
|
|
411
|
+
const peakIndicator = isNewHigh ? `${rgb(255, 100, 100)} 🚀 NEW HIGH!${c.reset}` : '';
|
|
412
|
+
|
|
413
|
+
lines.push(` ${liveIcon} ${balStr} ${c.dim}│${c.reset} ${earnStr} ${c.dim}(${c.reset}${rateLabel}${c.dim})${c.reset}${peakIndicator}`);
|
|
414
|
+
lines.push(` ${cmdStr} ${c.dim}(${c.reset}${rateStr}${c.dim})${c.reset} ${c.dim}│${c.reset} ${cpmStr} ${c.dim}│${c.reset} ${upStr}`);
|
|
415
|
+
lines.push(` ${c.dim}Peak:${c.reset} ${rgb(255, 215, 0)}⏣ ${formatCoins(sessionPeakCoins)}${c.reset} ${c.dim}│${c.reset} Trend: ${sparkline} ${c.dim}│${c.reset} ${memStr}`);
|
|
367
416
|
lines.push(thinBar);
|
|
368
417
|
|
|
369
418
|
// Worker rows — paginated for 10K+ accounts
|
|
370
419
|
// Renders up to MAX_VISIBLE_WORKERS rows individually, then shows
|
|
371
420
|
// a compact summary for the rest. This keeps terminal responsive.
|
|
372
|
-
const MAX_VISIBLE_WORKERS =
|
|
373
|
-
const nameWidth = Math.min(
|
|
374
|
-
const statusWidth = Math.max(
|
|
421
|
+
const MAX_VISIBLE_WORKERS = 30;
|
|
422
|
+
const nameWidth = Math.min(18, tw > 70 ? 18 : 12);
|
|
423
|
+
const statusWidth = Math.max(18, tw - nameWidth - 42);
|
|
375
424
|
const RE_ANSI_STRIP = /\x1b\[[0-9;]*m/g;
|
|
376
425
|
|
|
377
|
-
|
|
426
|
+
// Find top 3 earners for highlighting
|
|
427
|
+
const topEarners = [...workers]
|
|
428
|
+
.filter(w => w.running && (w.stats.coins || 0) > 0)
|
|
429
|
+
.sort((a, b) => (b.stats.coins || 0) - (a.stats.coins || 0))
|
|
430
|
+
.slice(0, 3);
|
|
431
|
+
const topEarnerIds = new Set(topEarners.map(w => w.account.id));
|
|
432
|
+
|
|
433
|
+
// Column headers
|
|
434
|
+
lines.push(` ${c.dim}#${c.reset} ${c.dim}Account${' '.repeat(nameWidth - 5)}${c.reset} ${c.dim}Balance${c.reset} ${c.dim}Earned${c.reset} ${c.dim}Status${c.reset}`);
|
|
435
|
+
lines.push(` ${c.dim}${'─'.repeat(tw - 4)}${c.reset}`);
|
|
436
|
+
|
|
437
|
+
const renderWorkerRow = (wk, index) => {
|
|
438
|
+
const pos = `${c.dim}#${c.reset}${c.bold}${(index + 1).toString().padStart(2, ' ')}${c.reset}`;
|
|
378
439
|
const rawStatus = (wk.lastStatus || 'idle').replace(RE_ANSI_STRIP, '');
|
|
379
440
|
const last = rawStatus.substring(0, statusWidth);
|
|
380
441
|
|
|
@@ -401,12 +462,19 @@ function renderDashboard() {
|
|
|
401
462
|
stateLabel = `${c.dim}${last}${c.reset}`;
|
|
402
463
|
}
|
|
403
464
|
|
|
465
|
+
// Top earner medal
|
|
466
|
+
let medal = ' ';
|
|
467
|
+
if (topEarnerIds.has(wk.account.id)) {
|
|
468
|
+
const rank = topEarners.findIndex(e => e.account.id === wk.account.id);
|
|
469
|
+
medal = rank === 0 ? `${rgb(255, 215, 0)}🥇${c.reset}` : rank === 1 ? `${rgb(192, 192, 192)}🥈${c.reset}` : `${rgb(205, 127, 50)}🥉${c.reset}`;
|
|
470
|
+
}
|
|
471
|
+
|
|
404
472
|
const name = `${wk.color}${c.bold}${(wk.username || '?').substring(0, nameWidth)}${c.reset}`;
|
|
405
473
|
const bal = wk.stats.balance > 0
|
|
406
|
-
? `${rgb(251, 191, 36)}⏣${c.reset} ${c.white}${formatCoins(wk.stats.balance).padStart(
|
|
407
|
-
: `${c.dim}⏣
|
|
474
|
+
? `${rgb(251, 191, 36)}⏣${c.reset} ${c.white}${formatCoins(wk.stats.balance).padStart(8)}${c.reset}`
|
|
475
|
+
: `${c.dim}⏣ -${c.reset}`;
|
|
408
476
|
|
|
409
|
-
//
|
|
477
|
+
// Enhanced progress bar for earned coins
|
|
410
478
|
const earnedNum = wk.stats.coins || 0;
|
|
411
479
|
const earnedBarWidth = earnedNum > 0 ? Math.min(5, Math.max(1, Math.floor(Math.log10(earnedNum + 1)))) : 0;
|
|
412
480
|
const earnedBar = earnedNum > 0
|
|
@@ -416,14 +484,14 @@ function renderDashboard() {
|
|
|
416
484
|
? `${rgb(52, 211, 153)}+${formatCoins(earnedNum)}${c.reset} ${earnedBar}`
|
|
417
485
|
: `${c.dim}+0${c.reset} ${earnedBar}`;
|
|
418
486
|
|
|
419
|
-
lines.push(` ${dot} ${name.padEnd(nameWidth + wk.color.length + c.bold.length + c.reset.length)} ${bal} ${earned} ${stateLabel}`);
|
|
487
|
+
lines.push(` ${pos} ${medal}${dot} ${name.padEnd(nameWidth + wk.color.length + c.bold.length + c.reset.length + 2)} ${bal} ${earned} ${stateLabel}`);
|
|
420
488
|
};
|
|
421
489
|
|
|
422
490
|
if (workers.length <= MAX_VISIBLE_WORKERS) {
|
|
423
|
-
for (let i = 0; i < workers.length; i++) renderWorkerRow(workers[i]);
|
|
491
|
+
for (let i = 0; i < workers.length; i++) renderWorkerRow(workers[i], i);
|
|
424
492
|
} else {
|
|
425
493
|
// Pagination: show first MAX_VISIBLE_WORKERS, summarize rest
|
|
426
|
-
for (let i = 0; i < MAX_VISIBLE_WORKERS; i++) renderWorkerRow(workers[i]);
|
|
494
|
+
for (let i = 0; i < MAX_VISIBLE_WORKERS; i++) renderWorkerRow(workers[i], i);
|
|
427
495
|
const remaining = workers.length - MAX_VISIBLE_WORKERS;
|
|
428
496
|
let hiddenActive = 0, hiddenPaused = 0, hiddenRecovering = 0, hiddenOffline = 0;
|
|
429
497
|
for (let i = MAX_VISIBLE_WORKERS; i < workers.length; i++) {
|
|
@@ -433,31 +501,33 @@ function renderDashboard() {
|
|
|
433
501
|
else if (w._recoveryAttempts > 0 && w._errorCooldownUntil > Date.now()) hiddenRecovering++;
|
|
434
502
|
else hiddenActive++;
|
|
435
503
|
}
|
|
436
|
-
const parts = [`${c.dim}... +${remaining} more${c.reset}`];
|
|
437
|
-
if (hiddenActive > 0) parts.push(`${rgb(52, 211, 153)}${hiddenActive} active${c.reset}`);
|
|
438
|
-
if (hiddenPaused > 0) parts.push(`${rgb(251, 191, 36)}${hiddenPaused} paused${c.reset}`);
|
|
439
|
-
if (hiddenRecovering > 0) parts.push(`${rgb(251, 191, 36)}${hiddenRecovering} recovering${c.reset}`);
|
|
440
|
-
if (hiddenOffline > 0) parts.push(`${c.dim}${hiddenOffline} offline${c.reset}`);
|
|
504
|
+
const parts = [`${c.dim}┃${c.reset} ${c.dim}... +${remaining} more${c.reset}`];
|
|
505
|
+
if (hiddenActive > 0) parts.push(`${rgb(52, 211, 153)}● ${hiddenActive} active${c.reset}`);
|
|
506
|
+
if (hiddenPaused > 0) parts.push(`${rgb(251, 191, 36)}⏸ ${hiddenPaused} paused${c.reset}`);
|
|
507
|
+
if (hiddenRecovering > 0) parts.push(`${rgb(251, 191, 36)}↻ ${hiddenRecovering} recovering${c.reset}`);
|
|
508
|
+
if (hiddenOffline > 0) parts.push(`${c.dim}○ ${hiddenOffline} offline${c.reset}`);
|
|
441
509
|
lines.push(` ${parts.join(` ${c.dim}·${c.reset} `)}`);
|
|
442
510
|
}
|
|
443
511
|
|
|
444
|
-
// Recovery summary line
|
|
512
|
+
// Recovery & status summary line with enhanced styling
|
|
445
513
|
const recoveringCount = workers.filter(w => w._recoveryAttempts > 0 && w._errorCooldownUntil > Date.now()).length;
|
|
446
514
|
const pausedCount = workers.filter(w => w.paused).length;
|
|
447
515
|
const totalRecoveries = workers.reduce((s, w) => s + (w._totalRecoveries || 0), 0);
|
|
448
|
-
|
|
516
|
+
const totalDisconnects = workers.reduce((s, w) => s + (w._disconnectCount || 0), 0);
|
|
517
|
+
if (recoveringCount > 0 || pausedCount > 0 || totalRecoveries > 0 || totalDisconnects > 0) {
|
|
449
518
|
const parts = [];
|
|
450
519
|
if (recoveringCount > 0) parts.push(`${rgb(251, 191, 36)}↻ ${recoveringCount} recovering${c.reset}`);
|
|
451
520
|
if (pausedCount > 0) parts.push(`${rgb(239, 68, 68)}⏸ ${pausedCount} paused${c.reset}`);
|
|
452
521
|
if (totalRecoveries > 0) parts.push(`${c.dim}${totalRecoveries} auto-recovered${c.reset}`);
|
|
453
|
-
|
|
522
|
+
if (totalDisconnects > 0) parts.push(`${c.dim}${totalDisconnects} reconnects${c.reset}`);
|
|
523
|
+
lines.push(` ${c.dim}┃${c.reset} ${parts.join(` ${c.dim}·${c.reset} `)}`);
|
|
454
524
|
}
|
|
455
525
|
|
|
456
|
-
// Cluster info line
|
|
526
|
+
// Cluster info line with enhanced styling
|
|
457
527
|
if (CLUSTER_ENABLED) {
|
|
458
528
|
const nodeShort = NODE_ID.substring(0, 12);
|
|
459
529
|
const claimedCount = workers.filter(w => w.running).length;
|
|
460
|
-
lines.push(` ${rgb(34, 211, 238)}
|
|
530
|
+
lines.push(` ${rgb(34, 211, 238)}╔${c.reset}${c.dim} Cluster: ${nodeShort} · ${claimedCount} claimed ${c.reset}${rgb(34, 211, 238)}╗${c.reset}`);
|
|
461
531
|
}
|
|
462
532
|
|
|
463
533
|
// Log section
|
|
@@ -1170,11 +1240,10 @@ class AccountWorker {
|
|
|
1170
1240
|
const tries = Math.max(1, Number.isFinite(maxAttempts) ? maxAttempts : 1);
|
|
1171
1241
|
let lastErr = null;
|
|
1172
1242
|
for (let attempt = 1; attempt <= tries; attempt++) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1243
|
+
const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
|
|
1244
|
+
const attemptLabel = tries > 1 ? ` [try ${attempt}/${tries}]` : '';
|
|
1245
|
+
const progressLine = `${baseLabel}${c.bold} ${this.username}${c.reset}${attemptLabel}`;
|
|
1246
|
+
process.stdout.write(`\x1b[2K\r${progressLine}`);
|
|
1178
1247
|
|
|
1179
1248
|
try {
|
|
1180
1249
|
const result = await commands.runInventory({
|
|
@@ -1184,9 +1253,9 @@ class AccountWorker {
|
|
|
1184
1253
|
accountId: this.account.id,
|
|
1185
1254
|
redis,
|
|
1186
1255
|
onPageProgress: ({ page, total }) => {
|
|
1187
|
-
//
|
|
1256
|
+
// Update progress on same line
|
|
1188
1257
|
const erase = '\x1b[2K\r';
|
|
1189
|
-
process.stdout.write(`${erase}${this.
|
|
1258
|
+
process.stdout.write(`${erase}${baseLabel} ${c.bold}${this.username}${c.reset} · page ${page}/${total}${attemptLabel}`);
|
|
1190
1259
|
},
|
|
1191
1260
|
});
|
|
1192
1261
|
|
|
@@ -1194,9 +1263,9 @@ class AccountWorker {
|
|
|
1194
1263
|
throw new Error(`incomplete pages (${result.pagesVisited || 0}/${result.pagesTotal || 0})`);
|
|
1195
1264
|
}
|
|
1196
1265
|
|
|
1197
|
-
//
|
|
1198
|
-
|
|
1199
|
-
|
|
1266
|
+
// Final result on same line
|
|
1267
|
+
const resultLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.green}${result.items?.length || 0} items${c.reset}, ⏣ ${c.green}${(result.totalValue || 0).toLocaleString()}${c.reset} net${attemptLabel}`;
|
|
1268
|
+
process.stdout.write(`\x1b[2K\r${resultLine}\n`);
|
|
1200
1269
|
try {
|
|
1201
1270
|
await fetch(`${API_URL}/api/grinder/inventory`, {
|
|
1202
1271
|
method: 'POST',
|
|
@@ -1220,7 +1289,9 @@ class AccountWorker {
|
|
|
1220
1289
|
} catch (e) {
|
|
1221
1290
|
lastErr = e;
|
|
1222
1291
|
if (attempt < tries) {
|
|
1223
|
-
|
|
1292
|
+
const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
|
|
1293
|
+
const retryLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.yellow}attempt ${attempt}/${tries} failed${c.reset} — retrying...`;
|
|
1294
|
+
process.stdout.write(`\x1b[2K\r${retryLine}\n`);
|
|
1224
1295
|
await new Promise((r) => setTimeout(r, 1500 + Math.floor(Math.random() * 1500)));
|
|
1225
1296
|
continue;
|
|
1226
1297
|
}
|
|
@@ -1229,7 +1300,9 @@ class AccountWorker {
|
|
|
1229
1300
|
|
|
1230
1301
|
throw lastErr || new Error('inventory check failed');
|
|
1231
1302
|
} catch (e) {
|
|
1232
|
-
|
|
1303
|
+
const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
|
|
1304
|
+
const failLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.red}failed${c.reset} — ${e.message}`;
|
|
1305
|
+
process.stdout.write(`\x1b[2K\r${failLine}\n`);
|
|
1233
1306
|
return { ok: false, error: e.message };
|
|
1234
1307
|
} finally {
|
|
1235
1308
|
this._invRunning = false;
|
|
@@ -2114,7 +2187,7 @@ class AccountWorker {
|
|
|
2114
2187
|
}
|
|
2115
2188
|
|
|
2116
2189
|
const prefix = this.account.use_slash ? '/' : 'pls';
|
|
2117
|
-
this.setStatus(
|
|
2190
|
+
this.setStatus(formatCommandName(item.cmd));
|
|
2118
2191
|
|
|
2119
2192
|
// Report "running" to dashboard
|
|
2120
2193
|
const nextItemRun = this.commandQueue?.peek?.();
|
|
@@ -2226,7 +2299,7 @@ class AccountWorker {
|
|
|
2226
2299
|
if (text.includes('alert') || text.includes('notification') ||
|
|
2227
2300
|
text.includes('you have a pending') || text.includes('check your alerts')) {
|
|
2228
2301
|
if (!this.busy) {
|
|
2229
|
-
this.log('info', 'Alert detected → running
|
|
2302
|
+
this.log('info', 'Alert detected → running alert');
|
|
2230
2303
|
this.busy = true;
|
|
2231
2304
|
const prefix = this.account.use_slash ? '/' : 'pls';
|
|
2232
2305
|
this.runCommand('alert', prefix).finally(() => { this.busy = false; });
|
|
@@ -2569,12 +2642,6 @@ async function start(apiKey, apiUrl) {
|
|
|
2569
2642
|
const total = workers.length;
|
|
2570
2643
|
|
|
2571
2644
|
await Promise.all(workers.map(async (w, i) => {
|
|
2572
|
-
const label = w?.username || w?.account?.label || 'account';
|
|
2573
|
-
|
|
2574
|
-
// Update progress on same line
|
|
2575
|
-
const progress = `[inv] ${invDone + invFailed + 1}/${total}: ${label}`;
|
|
2576
|
-
process.stdout.write(`\x1b[2K\r${c.dim}${progress}${c.reset}`);
|
|
2577
|
-
|
|
2578
2645
|
try {
|
|
2579
2646
|
const invRes = await w.checkInventory({
|
|
2580
2647
|
force: true,
|
|
@@ -2589,8 +2656,7 @@ async function start(apiKey, apiUrl) {
|
|
|
2589
2656
|
}
|
|
2590
2657
|
}));
|
|
2591
2658
|
|
|
2592
|
-
// Final
|
|
2593
|
-
process.stdout.write('\n');
|
|
2659
|
+
// Final summary
|
|
2594
2660
|
log('success', `Inventory: ${invDone}/${total} done${invFailed > 0 ? `, ${c.yellow}${invFailed} failed${c.reset}` : ''}`);
|
|
2595
2661
|
|
|
2596
2662
|
if (invFailed > 0) {
|
|
@@ -2764,7 +2830,10 @@ function setupKeyboardShortcuts() {
|
|
|
2764
2830
|
process.stdin.resume();
|
|
2765
2831
|
process.stdin.setEncoding('utf8');
|
|
2766
2832
|
|
|
2767
|
-
|
|
2833
|
+
// Modern styled keyboard shortcuts with box drawing
|
|
2834
|
+
console.log(`\n ${rgb(139, 92, 246)}╭${c.reset}${c.dim}${'─'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╮${c.reset}`);
|
|
2835
|
+
console.log(` ${rgb(139, 92, 246)}│${c.reset} ${c.bold}Shortcuts:${c.reset} ${c.dim}p${c.reset}=pause ${c.dim}r${c.reset}=resume ${c.dim}s${c.reset}=status ${c.dim}q${c.reset}=quit ${c.dim}?${c.reset}=help ${rgb(139, 92, 246)}│${c.reset}`);
|
|
2836
|
+
console.log(` ${rgb(139, 92, 246)}╰${c.reset}${c.dim}${'─'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╯${c.reset}`);
|
|
2768
2837
|
|
|
2769
2838
|
process.stdin.on('data', (key) => {
|
|
2770
2839
|
const k = key.toString().toLowerCase();
|
|
@@ -2780,7 +2849,9 @@ function setupKeyboardShortcuts() {
|
|
|
2780
2849
|
if (k === 'p') {
|
|
2781
2850
|
let paused = 0;
|
|
2782
2851
|
workers.forEach(w => { if (w.running && !w.paused) { w.paused = true; paused++; } });
|
|
2783
|
-
console.log(`\n ${c.
|
|
2852
|
+
console.log(`\n ${rgb(139, 92, 246)}╭${c.reset}${c.dim}${'─'.repeat(40)}${c.reset}${rgb(139, 92, 246)}╮${c.reset}`);
|
|
2853
|
+
console.log(` ${rgb(139, 92, 246)}│${c.reset} ${c.yellow}⏸ Paused ${paused} accounts${c.reset} ${rgb(139, 92, 246)}│${c.reset}`);
|
|
2854
|
+
console.log(` ${rgb(139, 92, 246)}╰${c.reset}${c.dim}${'─'.repeat(40)}${c.reset}${rgb(139, 92, 246)}╯${c.reset}`);
|
|
2784
2855
|
return;
|
|
2785
2856
|
}
|
|
2786
2857
|
|
|
@@ -2788,42 +2859,44 @@ function setupKeyboardShortcuts() {
|
|
|
2788
2859
|
if (k === 'r') {
|
|
2789
2860
|
let resumed = 0;
|
|
2790
2861
|
workers.forEach(w => { if (w.paused) { w.paused = false; resumed++; } });
|
|
2791
|
-
console.log(`\n ${c.
|
|
2862
|
+
console.log(`\n ${rgb(139, 92, 246)}╭${c.reset}${c.dim}${'─'.repeat(40)}${c.reset}${rgb(139, 92, 246)}╮${c.reset}`);
|
|
2863
|
+
console.log(` ${rgb(139, 92, 246)}│${c.reset} ${c.green}● Resumed ${resumed} accounts${c.reset} ${rgb(139, 92, 246)}│${c.reset}`);
|
|
2864
|
+
console.log(` ${rgb(139, 92, 246)}╰${c.reset}${c.dim}${'─'.repeat(40)}${c.reset}${rgb(139, 92, 246)}╯${c.reset}`);
|
|
2792
2865
|
return;
|
|
2793
2866
|
}
|
|
2794
2867
|
|
|
2795
2868
|
// s = show status summary
|
|
2796
2869
|
if (k === 's') {
|
|
2797
|
-
console.log(`\n ${c.bold}Status Summary:${c.reset}`);
|
|
2798
2870
|
const active = workers.filter(w => w.running && !w.paused).length;
|
|
2799
2871
|
const paused = workers.filter(w => w.paused).length;
|
|
2800
2872
|
const offline = workers.filter(w => !w.running).length;
|
|
2801
2873
|
const recovering = workers.filter(w => w._recoveryAttempts > 0 && w._errorCooldownUntil > Date.now()).length;
|
|
2802
|
-
|
|
2803
|
-
console.log(
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
if (num >= 1 && num <= 9 && workers[num - 1]) {
|
|
2810
|
-
const w = workers[num - 1];
|
|
2811
|
-
w.paused = !w.paused;
|
|
2812
|
-
console.log(`\n ${w.color}${w.username}${c.reset} ${w.paused ? c.yellow + 'paused' : c.green + 'resumed'}${c.reset}`);
|
|
2874
|
+
const totalEarn = workers.reduce((s, w) => s + (w.stats.coins || 0), 0);
|
|
2875
|
+
console.log(`\n ${rgb(139, 92, 246)}╔${c.reset}${c.bold}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╗${c.reset}`);
|
|
2876
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.bold}Status Summary${c.reset} ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2877
|
+
console.log(` ${rgb(139, 92, 246)}╠${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╣${c.reset}`);
|
|
2878
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.green}● ${active} active${c.reset} ${c.yellow}⏸ ${paused} paused${c.reset} ${c.red}○ ${offline} offline${c.reset} ${c.yellow}↻ ${recovering} recovering${c.reset} ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2879
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.dim}Total earnings:${c.reset} ${rgb(52, 211, 153)}⏣ ${totalEarn.toLocaleString()}${c.reset} ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2880
|
+
console.log(` ${rgb(139, 92, 246)}╚${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╝${c.reset}`);
|
|
2813
2881
|
return;
|
|
2814
2882
|
}
|
|
2815
2883
|
|
|
2816
2884
|
// ? = show help
|
|
2817
2885
|
if (k === '?' || k === 'h') {
|
|
2818
|
-
console.log(`\n ${c.bold}
|
|
2819
|
-
console.log(` ${c.
|
|
2820
|
-
console.log(` ${c.
|
|
2821
|
-
console.log(` ${c.white}
|
|
2822
|
-
console.log(` ${c.white}
|
|
2823
|
-
console.log(` ${c.white}
|
|
2824
|
-
console.log(` ${c.white}
|
|
2886
|
+
console.log(`\n ${rgb(139, 92, 246)}╔${c.reset}${c.bold}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╗${c.reset}`);
|
|
2887
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.bold}Keyboard Shortcuts${c.reset} ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2888
|
+
console.log(` ${rgb(139, 92, 246)}╠${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╣${c.reset}`);
|
|
2889
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}p${c.reset} Pause all accounts ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2890
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}r${c.reset} Resume all accounts ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2891
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}s${c.reset} Show status summary ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2892
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}q${c.reset} Quit gracefully ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2893
|
+
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}?${c.reset} Show this help ${rgb(139, 92, 246)}║${c.reset}`);
|
|
2894
|
+
console.log(` ${rgb(139, 92, 246)}╚${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╝${c.reset}`);
|
|
2825
2895
|
return;
|
|
2826
2896
|
}
|
|
2827
2897
|
});
|
|
2828
2898
|
}
|
|
2829
2899
|
}
|
|
2900
|
+
|
|
2901
|
+
// Export the start function for CLI
|
|
2902
|
+
module.exports = { start };
|