dankgrinder 6.21.0 → 6.25.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.
Files changed (2) hide show
  1. package/lib/grinder.js +54 -40
  2. package/package.json +1 -1
package/lib/grinder.js CHANGED
@@ -458,7 +458,7 @@ function renderDashboard() {
458
458
  lines.push(bRow(` ${c.bold}${titleGrad}${c.reset} ${D}v${PKG_VERSION}${c.reset} ${G}${spin}${c.reset}`));
459
459
  }
460
460
 
461
- lines.push(bRow(` ${D}v${PKG_VERSION}${c.reset} ${G}${spin}${c.reset}`));
461
+ lines.push(bRow(` ${D}v${PKG_VERSION}${c.reset} ${G}${spin}${c.reset} ${Y}◷${c.reset} ${D}UP${c.reset} ${c.bold}${Y}${formatUptime()}${c.reset}`));
462
462
 
463
463
  // Subtitle info
464
464
  const activeCount = workers.filter(w => w.running && !w.paused && !w.dashboardPaused).length;
@@ -487,52 +487,62 @@ function renderDashboard() {
487
487
  lines.push(bEmpty);
488
488
 
489
489
  // ═══════════════════════════════════════════════════════════════
490
- // STATS PANEL (split: left = metrics, right = big trend)
490
+ // STATS PANEL left: metrics (fixed labels) | right: big trend
491
491
  // ═══════════════════════════════════════════════════════════════
492
492
  lines.push(bSep);
493
493
  lines.push(bEmpty);
494
494
 
495
- // Earnings sparkline data
496
495
  const now = Date.now();
497
496
  if (now - lastEarningsSample > 8000) { earningsHistory.push(totalCoins); lastEarningsSample = now; }
498
497
  const elapsedHrs = (Date.now() - startTime) / 3_600_000;
499
498
  const perHr = elapsedHrs > 0.01 ? Math.round(totalCoins / elapsedHrs) : 0;
500
- const peakFlag = isNewHigh ? ` ${R}${c.bold}* NEW HIGH *${c.reset}` : '';
501
499
 
502
- // Left column: fixed metrics (left-aligned)
500
+ // ── Compute all metric values ──────────────────────────────────
503
501
  const cpmVal = globalCmdRate.getRate().toFixed(1);
504
502
  const srColor = successRate >= 95 ? G : successRate >= 80 ? Y : R;
505
- const srBarW = Math.min(15, Math.floor(iw * 0.1));
506
- const srBar = progressBar(successRate, 100, srBarW, successRate >= 95 ? [52, 211, 153] : successRate >= 80 ? [251, 191, 36] : [239, 68, 68]);
503
+ const srBar = progressBar(successRate, 100, 10, successRate >= 95 ? [52, 211, 153] : successRate >= 80 ? [251, 191, 36] : [239, 68, 68]);
507
504
  const memMB = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
508
505
  const memCol = memMB > 900 ? [239, 68, 68] : memMB > 600 ? [251, 191, 36] : [52, 211, 153];
509
- const memBarW = Math.min(15, Math.floor(iw * 0.1));
510
- const memBar = progressBar(memMB, 1024, memBarW, memCol, [40, 40, 55]);
506
+ const memBar = progressBar(memMB, 1024, 10, memCol, [40, 40, 55]);
507
+ const perHrColor = perHr >= 0 ? G : R;
508
+ const perHrSign = perHr >= 0 ? '+' : '';
509
+ const newHighFlag = isNewHigh ? ` ${R}${c.bold}★ NEW HIGH ★${c.reset}` : '';
511
510
 
512
- // Right column: big trend sparkline
513
- const sparkW = Math.floor(iw * 0.42);
511
+ // ── Big trend sparkline (right column) ──────────────────────
512
+ const sparkW = Math.max(36, Math.floor(iw * 0.55));
514
513
  const spark = drawSparkline(earningsHistory.toArray(), sparkW);
515
514
 
516
- // Build split rows
517
- const leftHalf = Math.floor(iw * 0.52);
515
+ // ── Build each row: left metrics (fixed) | right trend ───────
516
+ const leftW = 36; // chars for left column content
518
517
 
519
- // Row 1: Balance + EARNED | TREND (big sparkline)
520
- const leftRow1 = `${Au}⟐${c.reset} ${D}BALANCE${c.reset} ${c.bold}${Au}⏣ ${formatCoins(totalBalance)}${c.reset} ${G}▲${c.reset} ${D}EARNED${c.reset} ${c.bold}${G}+⏣ ${formatCoins(totalCoins)}${c.reset}${peakFlag}`;
521
- const rightRow1 = `${A}~${c.reset} ${D}TREND${c.reset} ${spark}`;
522
- const combined1 = `${leftRow1}${' '.repeat(Math.max(2, leftHalf - leftRow1.replace(RE, '').length))}${rightRow1}`;
523
- lines.push(bRow(` ${combined1}`));
518
+ // Helper: build a stat row with fixed-width label
519
+ const statRow = (icon, label, val) => {
520
+ const raw = `${icon} ${label} ${val}`;
521
+ const padded = raw + ' '.repeat(Math.max(0, leftW - raw.replace(RE, '').length));
522
+ return padded;
523
+ };
524
+
525
+ // Row 1: Balance
526
+ const balVal = `${Au}⏣${c.reset}${c.bold} ${formatCoins(totalBalance)}${c.reset}`;
527
+ const balRaw = `${Au}⟐${c.reset} ${D}BALANCE${c.reset} ${balVal}`;
528
+ lines.push(bRow(` ${balRaw.padEnd(leftW + 4)}${A}~${c.reset} ${D}TREND${c.reset} ${spark}`));
529
+
530
+ // Row 2: Earned
531
+ const earnVal = `${perHrColor}${c.bold}${perHrSign}⏣ ${formatCoins(perHr)}/h${c.reset}`;
532
+ const earnRaw = `${G}▲${c.reset} ${D}EARNED${c.reset} ${c.bold}${G}${perHrSign}⏣ ${formatCoins(totalCoins)}${c.reset}${newHighFlag}`;
533
+ lines.push(bRow(` ${earnRaw.padEnd(leftW + 4)}${D}──┬──${c.reset} ${c.dim}earned over session${c.reset}`));
524
534
 
525
- // Row 2: Peak (left)
526
- const peakRow = `${O}★${c.reset} ${D}PEAK${c.reset} ${c.bold}${O}⏣ ${formatCoins(sessionPeakCoins)}${c.reset}`;
527
- lines.push(bRow(` ${peakRow}${' '.repeat(Math.max(2, leftHalf - peakRow.replace(RE, '').length))}${Y}◷${c.reset} ${D}UP${c.reset} ${c.bold}${Y}${formatUptime()}${c.reset}`));
535
+ // Row 3: Peak
536
+ const peakRaw = `${O}★${c.reset} ${D}PEAK${c.reset} ${c.bold}${O}⏣ ${formatCoins(sessionPeakCoins)}${c.reset}`;
537
+ lines.push(bRow(` ${peakRaw.padEnd(leftW + 4)}${D} │${c.reset} ${c.dim}${formatCoins(Math.abs(perHr))}/h${c.reset}`));
528
538
 
529
- // Row 3: CMDS (left) + success bar + rate
530
- const cmdsRow = `${B}◆${c.reset} ${D}CMDS${c.reset} ${c.bold}${B}${totalCommands}${c.reset} ${srColor}${successRate}%${c.reset} ${srBar} ${Cy}${cpmVal}${c.reset}${D}/min${c.reset}`;
531
- lines.push(bRow(` ${cmdsRow}`));
539
+ // Row 4: Commands
540
+ const cmdsRaw = `${B}◆${c.reset} ${D}CMDS${c.reset} ${c.bold}${B}${totalCommands}${c.reset} ${srColor}${successRate}%${c.reset} ${srBar} ${Cy}${cpmVal}${c.reset}${D}/min${c.reset}`;
541
+ lines.push(bRow(` ${cmdsRaw.padEnd(leftW + 4)}${D} │${c.reset}`));
532
542
 
533
- // Row 4: MEM (left)
534
- const memRow = `${D}≡${c.reset} ${D}MEM${c.reset} ${rgb(memCol[0], memCol[1], memCol[2])}${c.bold}${memMB}MB${c.reset} ${memBar}`;
535
- lines.push(bRow(` ${memRow}`));
543
+ // Row 5: Memory
544
+ const memRaw = `${D}≡${c.reset} ${D}MEM${c.reset} ${rgb(memCol[0], memCol[1], memCol[2])}${c.bold}${memMB}MB${c.reset} ${memBar}`;
545
+ lines.push(bRow(` ${memRaw.padEnd(leftW + 4)}${D} │${c.reset}`));
536
546
 
537
547
  lines.push(bEmpty);
538
548
 
@@ -642,9 +652,9 @@ function renderDashboard() {
642
652
  else if (ls != null) lsStr = `${G}♥${ls}${c.reset}`;
643
653
  else lsStr = `${D}♥?${c.reset}`;
644
654
 
645
- // ── Level indicator ──
655
+ // ── Level indicator (fixed width so value changes don't jitter) ──
646
656
  const lvl = wk._level || 0;
647
- const lvlStr = lvl > 0 ? `${Cy}L${lvl}${c.reset}` : `${D}L?${c.reset}`;
657
+ const lvlStr = lvl > 0 ? `${Cy}L${String(lvl).padStart(3)}${c.reset}` : `${D}L???${c.reset}`;
648
658
 
649
659
  // ── Earned (fixed visible width) ──
650
660
  const earnNum = wk.stats.coins || 0;
@@ -1413,6 +1423,7 @@ class AccountWorker {
1413
1423
  startupProgress = null,
1414
1424
  requireComplete = false,
1415
1425
  maxAttempts = 1,
1426
+ silent = false,
1416
1427
  } = options;
1417
1428
  if (this._invRunning) return { ok: false, skipped: 'busy' };
1418
1429
  if (!force && this._lastInvCheck && Date.now() - this._lastInvCheck < 300_000) return { ok: false, skipped: 'recent' };
@@ -1426,7 +1437,7 @@ class AccountWorker {
1426
1437
  const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
1427
1438
  const attemptLabel = tries > 1 ? ` [try ${attempt}/${tries}]` : '';
1428
1439
  const progressLine = `${baseLabel}${c.bold} ${this.username}${c.reset}${attemptLabel}`;
1429
- process.stdout.write(`\x1b[2K\r${progressLine}`);
1440
+ if (!silent) process.stdout.write(`\x1b[2K\r${progressLine}`);
1430
1441
 
1431
1442
  try {
1432
1443
  const result = await commands.runInventory({
@@ -1436,9 +1447,10 @@ class AccountWorker {
1436
1447
  accountId: this.account.id,
1437
1448
  redis,
1438
1449
  onPageProgress: ({ page, total }) => {
1439
- // Update progress on same line
1440
- const erase = '\x1b[2K\r';
1441
- process.stdout.write(`${erase}${baseLabel} ${c.bold}${this.username}${c.reset} · page ${page}/${total}${attemptLabel}`);
1450
+ if (!silent) {
1451
+ const erase = '\x1b[2K\r';
1452
+ process.stdout.write(`${erase}${baseLabel} ${c.bold}${this.username}${c.reset} · page ${page}/${total}${attemptLabel}`);
1453
+ }
1442
1454
  },
1443
1455
  });
1444
1456
 
@@ -1447,8 +1459,10 @@ class AccountWorker {
1447
1459
  }
1448
1460
 
1449
1461
  // Final result on same line
1450
- 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}`;
1451
- process.stdout.write(`\x1b[2K\r${resultLine}\n`);
1462
+ if (!silent) {
1463
+ 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}`;
1464
+ process.stdout.write(`\x1b[2K\r${resultLine}\n`);
1465
+ }
1452
1466
 
1453
1467
  // Extract lifesaver count from inventory and cache in Redis
1454
1468
  if (result.items && redis) {
@@ -1492,7 +1506,7 @@ class AccountWorker {
1492
1506
  if (attempt < tries) {
1493
1507
  const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
1494
1508
  const retryLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.yellow}attempt ${attempt}/${tries} failed${c.reset} — retrying...`;
1495
- process.stdout.write(`\x1b[2K\r${retryLine}\n`);
1509
+ if (!silent) process.stdout.write(`\x1b[2K\r${retryLine}\n`);
1496
1510
  await new Promise((r) => setTimeout(r, 1500 + Math.floor(Math.random() * 1500)));
1497
1511
  continue;
1498
1512
  }
@@ -1503,7 +1517,7 @@ class AccountWorker {
1503
1517
  } catch (e) {
1504
1518
  const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
1505
1519
  const failLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.red}failed${c.reset} — ${e.message}`;
1506
- process.stdout.write(`\x1b[2K\r${failLine}\n`);
1520
+ if (!silent) process.stdout.write(`\x1b[2K\r${failLine}\n`);
1507
1521
  return { ok: false, error: e.message };
1508
1522
  } finally {
1509
1523
  this._invRunning = false;
@@ -3209,9 +3223,9 @@ async function start(apiKey, apiUrl) {
3209
3223
 
3210
3224
  const num = `${c.dim}${(idx + 1).toString().padStart(iColNum - 1)}${c.reset} `;
3211
3225
  const name = s.name.substring(0, iColName).padEnd(iColName);
3212
- const items = `${invRes?.items?.length || 0}`.padEnd(iColItems);
3226
+ const items = `${invRes?.result?.items?.length || 0}`.padEnd(iColItems);
3213
3227
  const val = invRes?.ok
3214
- ? `${c.green}⏣${(invRes.totalValue || 0).toLocaleString()}${c.reset}`.padEnd(iColVal + 3)
3228
+ ? `${c.green}⏣${(invRes.result?.totalValue || 0).toLocaleString()}${c.reset}`.padEnd(iColVal + 3)
3215
3229
  : `${c.dim}··${c.reset}`.padEnd(iColVal);
3216
3230
  const sts = invRes?.ok ? `${rgb(52, 211, 153)}✓${c.reset}` : `${rgb(239, 68, 68)}✗${c.reset}`;
3217
3231
 
@@ -3221,7 +3235,7 @@ async function start(apiKey, apiUrl) {
3221
3235
 
3222
3236
  await Promise.all(activeWorkers.map(async (w, i) => {
3223
3237
  try {
3224
- const invRes = await w.checkInventory({ force: true, requireComplete: true, maxAttempts: 3 });
3238
+ const invRes = await w.checkInventory({ force: true, requireComplete: true, maxAttempts: 3, silent: true });
3225
3239
  finalizeInvLine(i, invRes);
3226
3240
  } catch {
3227
3241
  finalizeInvLine(i, { ok: false });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "6.21.0",
3
+ "version": "6.25.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"