dankgrinder 8.48.0 → 8.49.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 CHANGED
@@ -2932,7 +2932,6 @@ async function start(apiKey, apiUrl, opts = {}) {
2932
2932
  ui.log(-1, `Starting ${activeWorkers.length} grind loops...`);
2933
2933
  ui.setLive(true);
2934
2934
  ui.draw();
2935
- ui.startRefresh(3000);
2936
2935
  for (const w of activeWorkers) {
2937
2936
  if (!shutdownCalled) w.grindLoop();
2938
2937
  }
@@ -3003,7 +3002,6 @@ async function start(apiKey, apiUrl, opts = {}) {
3003
3002
  shutdownInProgress = true;
3004
3003
  shutdownCalled = true;
3005
3004
  setDashboardActive(false);
3006
- ui.stopRefresh();
3007
3005
  ui.stop();
3008
3006
  process.stdout.write(c.show + '\n');
3009
3007
 
@@ -3050,10 +3048,12 @@ async function start(apiKey, apiUrl, opts = {}) {
3050
3048
  const webhookMsg = `+⏣ ${finalCoins.toLocaleString()} | ${finalCmds} cmds | ${formatUptime()}` +
3051
3049
  (totalRecoveries > 0 ? ` | ${totalRecoveries} auto-recoveries` : '') +
3052
3050
  (CLUSTER_ENABLED ? ` | node: ${NODE_ID.substring(0, 12)}` : '');
3053
- sendWebhook('Session Ended', webhookMsg, 0x8b5cf6);
3051
+ sendWebhook('Session Ended', webhookMsg, 0x8b5cf6).catch(() => {});
3054
3052
 
3055
3053
  if (redis) { redis.disconnect().catch(() => {}); }
3056
3054
  console.log(`${c.green}Goodbye!${c.reset}\n`);
3055
+ // Force exit after 5s so Ctrl+C always terminates even if cleanup hangs
3056
+ setTimeout(() => process.exit(0), 5000);
3057
3057
  process.exit(0);
3058
3058
  }
3059
3059
 
package/lib/ui.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * CLI Live Dashboard — ASCII banner, per-account status table, event stream below.
3
+ * Box fixed at top. Events append line-by-line below. Manual redraw only.
3
4
  */
4
5
 
5
6
  let _startTime = Date.now();
@@ -8,10 +9,6 @@ let _isShuttingDown = () => false;
8
9
  let _version = '0.0.0';
9
10
  let _live = false;
10
11
  let _phase = 'init'; // 'init' | 'login' | 'inventory' | 'balance' | 'dms' | 'grinding'
11
- let _boxRow = 0; // which terminal row the box top starts on
12
- let _boxHeight = 0; // total rows consumed by the box
13
- let _eventCountAtDraw = 0; // events logged since last draw
14
- let _eventCount = 0; // total events ever logged
15
12
 
16
13
  // ── Spinner frames ────────────────────────────────────────────
17
14
  const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
@@ -20,7 +17,7 @@ function spinnerFrame() { return SPINNER[Math.floor(Date.now() / 150) % SPINNER.
20
17
  // ── Compact ASCII art banner (fits in 80-col box) ─────────────
21
18
  const BANNER_LINES = [
22
19
  ' ██████╗ ██╗ ██╗███╗ ██╗ ██████╗ ███████╗ ██████╗ ███╗ ██╗ DANKGRINDER',
23
- ' ██╔══██╗██║ ██║████╗ ██║██╔════╝ ██╔════╝██╔═══██╗████╗ ██║ v' + 'PLACEHOLDER',
20
+ ' ██╔══██╗██║ ██║████╗ ██║██╔════╝ ██╔════╝██╔═══██╗████╗ ██║ vPLACEHOLDER',
24
21
  ' ██║ ██║██║ ██║██╔██╗ ██║██║ ███╗█████╗ ██║ ██║██╔██╗ ██║',
25
22
  ' ██║ ██║██║ ██║██║╚██╗██║██║ ██║██╔══╝ ██║ ██║██║╚██╗██║',
26
23
  ' ██████╔╝╚██████╔╝██║ ╚████║╚██████╔╝███████╗╚██████╔╝██║ ╚████║',
@@ -123,27 +120,18 @@ function draw() {
123
120
  const { W, maxAccounts } = layout();
124
121
  const inner = W - 2;
125
122
 
126
- // ── Clear screen + home ──
127
- const isFirst = _boxRow === 0;
128
- if (isFirst) {
129
- process.stdout.write('\x1b[2J\x1b[H');
130
- } else {
131
- // Move cursor up to box start: box rows + events since last draw
132
- const eventsSinceDraw = _eventCount - _eventCountAtDraw;
133
- const up = _boxHeight + eventsSinceDraw;
134
- process.stdout.write(`\x1b[${up}A`);
135
- }
123
+ // ── Clear screen + home (always from top — keeps box fixed) ──
124
+ process.stdout.write('\x1b[2J\x1b[H');
136
125
 
137
126
  // ── Top border ──
138
127
  process.stdout.write(`\x1b[38;2;77;212;238m┌${'─'.repeat(inner)}┐\x1b[0m\n`);
139
128
 
140
- // ── ASCII banner (dynamic — includes version) ──
129
+ // ── ASCII banner ──
141
130
  const bannerWithVer = BANNER_LINES[1].replace('PLACEHOLDER', _version);
142
131
  const bannerLines = [BANNER_LINES[0], bannerWithVer, ...BANNER_LINES.slice(2)];
143
132
 
144
133
  for (let i = 0; i < bannerLines.length; i++) {
145
134
  const line = bannerLines[i];
146
- // Gradient: cyan → pink for row 0 and 1, dim for others
147
135
  if (i < 2) {
148
136
  const gradLine = gradientLine(line, 77, 212, 238, 255, 92, 147);
149
137
  const pad = ' '.repeat(Math.max(0, inner - stripAnsi(gradLine).length));
@@ -252,10 +240,6 @@ function draw() {
252
240
 
253
241
  // ── Bottom ──
254
242
  process.stdout.write(`\x1b[38;2;77;212;238m└${'─'.repeat(inner)}┘\x1b[0m\n`);
255
-
256
- // Record box height + current event count for next redraw
257
- _boxHeight = bannerLines.length + 1 + 1 + 2 + shown.length + (sorted.length > maxAccounts ? 1 : 0) + 3;
258
- _eventCountAtDraw = _eventCount;
259
243
  }
260
244
 
261
245
  // ── Gradient line ─────────────────────────────────────────────
@@ -270,7 +254,7 @@ function gradientLine(text, r1, g1, b1, r2, g2, b2) {
270
254
 
271
255
  // ── Event tracking ────────────────────────────────────────────
272
256
  let _eventLines = []; // [accountIdx] = [{text, ts}]
273
- const MAX_EVENTS = 4;
257
+ const MAX_EVENTS = 20; // keep last 20 events per account
274
258
 
275
259
  // ── Public API ────────────────────────────────────────────────
276
260
 
@@ -282,22 +266,22 @@ function init({ workers, isShuttingDown }) {
282
266
  _eventLines = [];
283
267
  _live = false;
284
268
  _phase = 'init';
285
- _boxRow = 0;
286
- _boxHeight = 0;
287
- _eventCount = 0;
288
- _eventCountAtDraw = 0;
289
269
  }
290
270
 
291
271
  function drawBanner(version) { _version = version || '0.0.0'; }
292
272
 
293
273
  function start() {}
294
- function stop() { _live = false; _phase = 'init'; process.stdout.write(c.reset + '\n'); }
274
+ function stop() {
275
+ _live = false;
276
+ _phase = 'init';
277
+ // Clear screen on shutdown so box disappears
278
+ process.stdout.write('\x1b[2J\x1b[H' + c.reset + '\n');
279
+ }
295
280
 
296
281
  function setLive(val) { _live = val; }
297
-
298
282
  function setPhase(phase) { _phase = phase; }
299
283
 
300
- // log: append event below the box (no redraw of box)
284
+ // log: draw box + append event below it
301
285
  function log(accountIdx, msg) {
302
286
  const now = new Date();
303
287
  const ts = `${padL(now.getHours(), 2, '0')}:${padL(now.getMinutes(), 2, '0')}:${padL(now.getSeconds(), 2, '0')}`;
@@ -307,11 +291,11 @@ function log(accountIdx, msg) {
307
291
  _eventLines[accountIdx].push({ text: msg, ts });
308
292
  if (_eventLines[accountIdx].length > MAX_EVENTS) _eventLines[accountIdx].shift();
309
293
  }
310
- _eventCount++;
311
294
 
312
295
  if (!_live) return;
313
296
 
314
- // Clean event line: colored name + timestamp + message
297
+ // Redraw the whole box (clears screen + redraws at top) then append event
298
+ draw();
315
299
  const col2 = accountIdx >= 0 ? wc(accountIdx) : c.cyan;
316
300
  const name = accountIdx >= 0 ? trunc(_workers[accountIdx]?.username || '?', 14) : 'GLOBAL';
317
301
  process.stdout.write(`${col2}${name}${c.reset} ${DIM}[${ts}]${c.reset} ${msg}\n`);
@@ -319,16 +303,6 @@ function log(accountIdx, msg) {
319
303
 
320
304
  function logGlobal(msg) { log(-1, msg); }
321
305
 
322
- // Start periodic box refresh (every 3s for spinner/countdown updates)
323
- let _refreshInterval = null;
324
- function startRefresh(ms = 3000) {
325
- if (_refreshInterval) clearInterval(_refreshInterval);
326
- _refreshInterval = setInterval(() => {
327
- if (_live) draw();
328
- }, ms);
329
- }
330
- function stopRefresh() {
331
- if (_refreshInterval) { clearInterval(_refreshInterval); _refreshInterval = null; }
332
- }
306
+ // Periodic refresh removed draw() is now only called explicitly
333
307
 
334
- module.exports = { init, drawBanner, start, draw, log, logGlobal, workerColor: wc, stop, setLive, setPhase, startRefresh, stopRefresh };
308
+ module.exports = { init, drawBanner, start, draw, log, logGlobal, workerColor: wc, stop, setLive, setPhase };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "8.48.0",
3
+ "version": "8.49.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"