dcp-worker 3.2.30-2 → 3.2.30-4

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/bin/dcp-worker CHANGED
@@ -4,25 +4,26 @@
4
4
  * Standalone NodeJS DCP Worker
5
5
  *
6
6
  * @author Ryan Rossiter, ryan@kingsds.network
7
+ * Paul, paul@distributive.network
8
+ * Wes Garland, wes@distributive.network
9
+ *
7
10
  * @date April 2020
11
+ * April-May 2023
12
+ * May-June 2023
8
13
  */
9
14
  'use strict';
10
15
 
16
+ var worker;
17
+
11
18
  const process = require('process');
12
19
  const fs = require('fs');
20
+ const path = require('path');
13
21
  const crypto = require('crypto');
14
22
  const chalk = require('chalk');
23
+ const telnetd = require('../lib/remote-console');
24
+ const utils = require('../lib/utils');
15
25
 
16
26
  const configName = process.env.DCP_CONFIG || '../etc/dcp-worker-config';
17
- var worker, dcpConfig, debugging;
18
-
19
- const debug = (...args) => {
20
- if (!debugging)
21
- debugging = require('dcp/internal/debugging').scope('dcp-worker');
22
- if (debugging())
23
- console.debug('dcp-worker:', ...args);
24
- };
25
-
26
27
  const EXIT_UNHANDLED = 5;
27
28
 
28
29
  /* Setup the telnet REPL up early to ensure early-failure log messages are captured */
@@ -34,13 +35,15 @@ const replHelpers = {
34
35
  },
35
36
  commands: {
36
37
  report: printReport,
37
- kill: process.exit,
38
+ kill: processExit,
38
39
  die: () => worker && worker.stop()
39
40
  },
40
41
  };
41
- require('../lib/remote-console').init(replHelpers);
42
+ telnetd.init(replHelpers);
42
43
 
43
- /* Initialize dcp-client with local config defaults and run the main function. DCP_CONFIG_COOKIE becomes dcpConfig.cookie. */
44
+ /* Initialize dcp-client with local config defaults and run the main function. DCP_CONFIG_COOKIE becomes dcpConfig.cookie.
45
+ * And dcpConfig is defined as a side effect of initializing dcp-client.
46
+ */
44
47
  process.env.DCP_CONFIG_COOKIE = (Math.random().toString(16)).slice(2) + '-' + process.pid + '-' + Date.now();
45
48
  require('dcp-client').init({ configName }).then(main).catch(handleUnhandled);
46
49
 
@@ -48,7 +51,6 @@ function parseCliArgs()
48
51
  {
49
52
  var defaultPidFileName;
50
53
 
51
- dcpConfig = require('dcp/dcp-config');
52
54
  defaultPidFileName = require('../lib/pidfile').getDefaultPidFileName(dcpConfig.worker.pidfile);
53
55
 
54
56
  const cliArgs = require('dcp/cli')
@@ -67,7 +69,6 @@ function parseCliArgs()
67
69
  alias: 'd',
68
70
  describe: 'default proportion of CPU,GPU to use when cores not specified',
69
71
  type: 'string',
70
- default: JSON.stringify(dcpConfig.worker.defaultCoreDensity),
71
72
  },
72
73
  verbose: {
73
74
  alias: 'v',
@@ -98,18 +99,21 @@ function parseCliArgs()
98
99
  },
99
100
  priorityOnly: {
100
101
  alias: 'P',
102
+ hidden: true,
101
103
  describe: 'Set the priority mode [deprecated]',
102
104
  type: 'boolean',
103
105
  default: false
104
106
  },
105
107
  'job-id': {
106
108
  alias: 'j',
109
+ hidden: true,
107
110
  describe: 'Restrict worker to a specific job (use N times for N jobs)',
108
111
  type: 'array',
109
112
  },
110
113
 
111
114
  join: {
112
115
  alias: 'g',
116
+ hidden: true,
113
117
  describe: 'Join compute group; the format is "joinKey,joinSecret" or "joinKey,eh1-joinHash"',
114
118
  type: 'array'
115
119
  },
@@ -120,22 +124,27 @@ function parseCliArgs()
120
124
 
121
125
  leavePublicGroup: {
122
126
  type: 'boolean',
127
+ hidden: true,
123
128
  describe: 'Do not fetch slices from public compute group.',
124
129
  default: undefined,
125
130
  },
126
131
 
127
132
  publicGroupFallback: {
133
+ hidden: true,
128
134
  describe: 'If set, worker will prefer private groups but fall back on the public group if no preferred work is available',
129
135
  type: 'boolean',
130
- default: undefined,
136
+ default: 'undefined',
137
+ defaultDescription: undefined,
131
138
  },
132
139
 
133
140
  identityKey: {
141
+ hidden: true,
134
142
  describe: 'Identity key, in hex format',
135
143
  type: 'string',
136
144
  group: 'Identity options',
137
145
  },
138
146
  identityKeystore: {
147
+ hidden: true,
139
148
  describe: 'Identity keystore, in json format',
140
149
  type: 'string',
141
150
  group: 'Identity options',
@@ -147,30 +156,40 @@ function parseCliArgs()
147
156
  group: 'Output options',
148
157
  },
149
158
  eventDebug: {
150
- hide: true,
159
+ hidden: true,
151
160
  describe: 'If set, dump all sandbox and worker events',
152
161
  },
153
162
 
154
163
  logfile: {
155
- describe: 'Path to log file (if --output=file)',
164
+ describe: 'Path to log file',
156
165
  type: 'string',
157
166
  group: 'Log File output options',
167
+ default: path.resolve('../log/dcp-worker.log'),
158
168
  },
159
169
  syslogAddress: {
160
- describe: 'Address of rsyslog server (if --output=syslog)',
170
+ describe: 'Address of syslog server',
171
+ type: 'string',
172
+ group: 'Syslog output options',
173
+ default: 'loghost',
174
+ },
175
+ syslogFacility: {
176
+ describe: 'Name of syslog facility',
161
177
  type: 'string',
162
178
  group: 'Syslog output options',
179
+ default: 'local7',
163
180
  },
164
181
  syslogTransport: {
165
- describe: 'Transport to connect to rsyslog daemon (if --output=syslog)',
182
+ describe: 'Transport to connect to use for syslog',
166
183
  type: 'string',
167
- choices: ['udp','tcp'],
184
+ choices: ['udp','tcp','unix','tls'],
168
185
  group: 'Syslog output options',
186
+ default: 'udp',
169
187
  },
170
188
  syslogPort: {
171
- describe: 'UDP/TCP port of rsyslog server',
189
+ describe: 'UDP/TCP port to use for syslog',
172
190
  type: 'number',
173
191
  group: 'Syslog output options',
192
+ default: 514,
174
193
  },
175
194
 
176
195
  allowedOrigins: {
@@ -202,8 +221,8 @@ function parseCliArgs()
202
221
 
203
222
  if (cliArgs.dumpConfig)
204
223
  {
205
- console.debug(JSON.stringify(require('dcp/dcp-config'), null, 2));
206
- process.exit(0);
224
+ console.log(JSON.stringify(require('dcp/dcp-config'), null, 2));
225
+ processExit(0);
207
226
  }
208
227
 
209
228
  return cliArgs;
@@ -228,6 +247,20 @@ function addConfig(target, ...objs)
228
247
  Object.assign(target, tmp);
229
248
  }
230
249
 
250
+ /**
251
+ * Replacement for process.exit() that tries to increase the probability
252
+ * that remote log messages will make it out over the network.
253
+ */
254
+ function processExit()
255
+ {
256
+ logClosing('debug', 'Exit Code:', process.exitCode || 0);
257
+ if (console.close)
258
+ console.close();
259
+ setImmediate(() => {
260
+ process.exit.apply(null, arguments);
261
+ });
262
+ }
263
+
231
264
  /**
232
265
  * Main program entry point. Assumes DCP client is already initialized and console logging is ready.
233
266
  */
@@ -235,21 +268,22 @@ async function main()
235
268
  {
236
269
  const wallet = require('dcp/wallet');
237
270
  const DCPWorker = require('dcp/worker').Worker;
238
- const { startWorkerLogger } = require('../lib/startWorkerLogger');
239
271
  const cliArgs = parseCliArgs();
240
272
  const sawOptions = {
241
273
  hostname: cliArgs.hostname,
242
274
  port: cliArgs.port
243
275
  };
244
276
 
245
- verifyDefaultConfigIntegrity(); /* Bail before TUI & as early as possible if config file is bad */
277
+ telnetd.setMainEval(function mainEval() { return eval(arguments[0]) }); // eslint-disable-line no-eval
278
+ require('../lib/startWorkerLogger').init(cliArgs); /* Start remote logger as early as possible */
279
+ verifyDefaultConfigIntegrity(); /* Bail before TUI & as early as possible if bad conf */
246
280
 
247
281
  process.on('SIGINT', handleSigDeath);
248
282
  process.on('SIGTERM', handleSigDeath);
249
283
  process.on('SIGQUIT', handleSigDeath);
250
284
  process.on('unhandledRejection', handleUnhandled);
251
285
  process.on('uncaughtException', handleUnhandled);
252
-
286
+
253
287
  let paymentAddress = false
254
288
  || cliArgs.paymentAddress
255
289
  || dcpConfig.worker.paymentAddress
@@ -285,6 +319,7 @@ async function main()
285
319
  const dcpWorkerOptions = dcpConfig.worker;
286
320
  const forceOptions = {
287
321
  paymentAddress,
322
+ maxWorkingSandboxes: cliArgs.cores,
288
323
  };
289
324
  const defaultOptions = {
290
325
  sandboxOptions: {
@@ -339,27 +374,26 @@ async function main()
339
374
  dcpWorkerOptions.watchdogInterval = cliArgs.watchdogInterval;
340
375
 
341
376
  worker = new DCPWorker(identityKeystore, dcpWorkerOptions);
342
- worker.on('error', (...payload) => console.error(...payload));
343
- worker.on('warning', (...payload) => console.warn(...payload));
377
+ worker.on('error', (...payload) => console.error(...payload));
378
+ worker.on('warning', (...payload) => console.warn (...payload));
379
+ worker.on('stop', () => { console.log('Worker is stopping') });
380
+ worker.on('end', () => { logClosing('log', 'Worker has stopped') });
381
+ require('../lib/default-ui-events').hook(worker, cliArgs);
344
382
 
383
+ if (cliArgs.outputMode === 'dashboard')
384
+ require('../lib/dashboard-tui').init(worker, cliArgs);
385
+
345
386
  /* Let incorrect event-loop references keep us alive when linked with a debug library, but
346
387
  * exit quickly/accurately for production code even when the library isn't perfect.
347
388
  */
348
389
  if (require('dcp/build').config.build !== 'debug')
349
- worker.on('end', process.exit);
390
+ worker.on('end', processExit);
350
391
  else
351
- worker.on('end', () => setTimeout(process.exit, getCleanupTimeoutMs()).unref());
392
+ worker.on('end', () => setTimeout(processExit, getCleanupTimeoutMs()).unref());
352
393
 
353
394
  if (cliArgs.eventDebug)
354
395
  worker.enableDebugEvents = true;
355
396
 
356
- worker.on('stop', () => { console.log('Worker is stopping') });
357
- worker.on('end', () => { logClosing('log', 'Worker has stopped') });
358
- startWorkerLogger(worker, cliArgs);
359
-
360
- require('../lib/remote-console').setMainEval(function mainEval() { return eval(arguments[0]) });
361
- require('../lib/remote-console').reintercept();
362
-
363
397
  if (dcpWorkerOptions.publicGroupFallback)
364
398
  {
365
399
  if (dcpWorkerOptions.leavePublicGroup)
@@ -373,23 +407,10 @@ async function main()
373
407
 
374
408
  function fetchEventHandler(ev)
375
409
  {
376
- var slicesFetched;
377
-
378
410
  if (ev instanceof Error)
379
- {
380
411
  console.error('Error fetching task:', ev);
381
- return;
382
- }
383
-
384
- if (typeof ev === 'string') /* <= June 2023 Worker events: remove ~ Sep 2023 /wg */
385
- slicesFetched = ev;
386
412
  else
387
- {
388
- const task = ev;
389
- slicesFetched = task.slices?.length;
390
- }
391
-
392
- dcpWorkerOptions.leavePublicGroup = Boolean(slicesFetched > 0);
413
+ dcpWorkerOptions.leavePublicGroup = Boolean(utils.slicesFetched(ev) > 0);
393
414
  }
394
415
  }
395
416
  }
@@ -416,21 +437,23 @@ async function main()
416
437
 
417
438
  if (dcpWorkerOptions.jobAddresses?.length > 0)
418
439
  introBanner += ` * Processing only ${qty(dcpWorkerOptions.jobAddresses, 'job')} ` + dcpWorkerOptions.jobAddresses.join(', ') + '\n';
419
- if (dcpWorkerOptions.computeGroups?.length > 0)
440
+ if (dcpWorkerOptions.computeGroups && Object.keys(dcpWorkerOptions.computeGroups).length > 0)
420
441
  introBanner += ` . Joining compute ${qty(dcpWorkerOptions.computeGroups, 'group')} ` + dcpWorkerOptions.computeGroups.map(el => el.joinKey).join(', ') + '\n';
421
442
  if (dcpWorkerOptions.publicGroupFallback)
422
443
  introBanner += ' . Falling back on public group when preferred groups have no work' + '\n';
423
444
  if (dcpWorkerOptions.leavePublicGroup)
424
445
  introBanner += ' . Leaving the public compute group' + '\n';
425
446
  if (dcpWorkerOptions.cores)
426
- introBanner += ` . Target cores: ${JSON.stringify(dcpWorkerOptions.cores)}\n`;
447
+ introBanner += ` . Configured Cores: ${dcpWorkerOptions.cores.cpu},${dcpWorkerOptions.cores.gpu}\n`
427
448
  else
428
449
  introBanner += ` . Target core density: ${JSON.stringify(dcpWorkerOptions.defaultCoreDensity)}\n`;
429
450
  if (cliArgs.verbose)
430
451
  introBanner += ` + Verbosity level: ${cliArgs.verbose}` + '\n';
431
452
  if (cliArgs.eventDebug)
432
453
  introBanner += ' + Event debug on' + '\n';
433
-
454
+ if (telnetd.hasOwnProperty('port'))
455
+ introBanner += ` ! telnetd listening on port ${telnetd.port}\n`;
456
+
434
457
  introBanner += ' . Supervisor version: ' + worker.supervisorVersion;
435
458
  introBanner += ' . Output mode: ' + cliArgs.outputMode + '\n';
436
459
  introBanner += ' * Ready' + '\n';
@@ -443,7 +466,7 @@ async function main()
443
466
  if (cliArgs.outputMode !== 'dashboard')
444
467
  setInterval(printReport, parseFloat(cliArgs.reportInterval) * 1000).unref();
445
468
  else
446
- console.log('Ignoring --reportInterval in dashboard output mode');
469
+ console.warn('Ignoring --reportInterval in dashboard output mode');
447
470
  }
448
471
 
449
472
  /* Start the worker. Normal process exit happens by virtue of the worker<end> event */
@@ -470,21 +493,22 @@ function processCoresAndDensity (dcpWorkerOptions, cliArgs)
470
493
 
471
494
  const parseArg = (which) => {
472
495
  if (!cliArgs[which])
473
- return false;
474
-
475
- const [cpu, gpu] = cliArgs[which].split(',');
476
- dcpWorkerOptions[which] = { cpu: Number(cpu || defaultTargets[which].cpu),
477
- gpu: Number(gpu || defaultTargets[which].gpu) };
478
- return true;
496
+ dcpWorkerOptions[which] = defaultTargets[which];
497
+ else
498
+ {
499
+ const [cpu, gpu] = cliArgs[which].split(',');
500
+ dcpWorkerOptions[which] = { cpu: Number(cpu || defaultTargets[which].cpu),
501
+ gpu: Number(gpu || defaultTargets[which].gpu) };
502
+ }
479
503
  };
480
504
 
481
505
  parseArg('density');
482
506
  parseArg('cores');
483
507
 
484
508
  if (dcpWorkerOptions.cores)
485
- debug('cores = ', dcpWorkerOptions.cores);
486
- else
487
- debug('core density = ', dcpWorkerOptions.defaultCoreDensity);
509
+ debugging() && console.debug('dcp-worker: cores =', dcpWorkerOptions.cores);
510
+ if (dcpWorkerOptions.density)
511
+ debugging() && console.debug('dcp-worker: core density =', dcpWorkerOptions.density);
488
512
  }
489
513
 
490
514
  /**
@@ -495,9 +519,7 @@ function logClosing(facility, ...message)
495
519
  {
496
520
  var screen = require('../lib/worker-loggers/dashboard').screen;
497
521
 
498
- if (!screen)
499
- console[facility](message);
500
- else
522
+ if (screen)
501
523
  {
502
524
  /* Turn off fullscreen TUI and resume "normal" console logging.
503
525
  * FUTURE: dashboard API should know how to unregister its hook so that we don't have to clobber
@@ -506,10 +528,11 @@ function logClosing(facility, ...message)
506
528
  screen.log(...message);
507
529
  screen.destroy();
508
530
  screen = false;
509
- console = new (require('console').Console)(process);
510
- require('../lib/remote-console').reintercept();
511
- console[facility].call(null, ...message);
531
+ console = new (require('console').Console)(process); // eslint-disable-line no-global-assign
532
+ telnetd.reintercept();
512
533
  }
534
+
535
+ console[facility](...message);
513
536
  }
514
537
 
515
538
  /**
@@ -518,7 +541,7 @@ function logClosing(facility, ...message)
518
541
  * the worker must be restarted. This handler does its best to report the rejection and give the worker a few
519
542
  * seconds in which to attempt to return slices to the scheduler before it gives up completely.
520
543
  */
521
- async function handleUnhandled(error)
544
+ function handleUnhandled(error)
522
545
  {
523
546
  var _worker = worker;
524
547
  worker = false;
@@ -527,21 +550,21 @@ async function handleUnhandled(error)
527
550
 
528
551
  try
529
552
  {
530
- logClosing(error);
531
- } catch(e) {};
553
+ logClosing('error', error);
554
+ } catch(e) {} // eslint-disable-line no-empty
532
555
 
533
556
  if (!_worker)
534
557
  console.error('trapped unhandled error:', error)
535
558
  else
536
559
  {
537
560
  console.error('trapped unhandled error -- stopping worker:', error);
538
- _worker.on('end', process.exit);
561
+ _worker.on('end', processExit);
539
562
  _worker.stop();
540
563
  }
541
564
 
542
565
  setTimeout(() => {
543
566
  logClosing('error', 'handleFatalError timeout - exiting now');
544
- process.exit();
567
+ processExit();
545
568
  }, getCleanupTimeoutMs()).unref();
546
569
 
547
570
  try {
@@ -551,7 +574,7 @@ async function handleUnhandled(error)
551
574
  fs.appendFileSync(process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG,
552
575
  `${Date.now()}: ${error.message}\n${error.stack}\n\n`);
553
576
  }
554
- } catch(e) {};
577
+ } catch(e) {} // eslint-disable-line no-empty
555
578
  }
556
579
 
557
580
  /** print the slice report via console.log */
@@ -609,14 +632,14 @@ function sliceReport()
609
632
 
610
633
  report += ('Progress:') + '\n';
611
634
  worker.workingSandboxes.forEach(sb => {
612
- const jobName = sb.job && sb.job.public && sb.job.public.name || `idek (${sb.jobAddress})`;
635
+ const jobName = sb.job?.public?.name || `idek (${sb.jobAddress})`;
613
636
  let el = Date.now() - sb.sliceStartTime;
614
637
  const t = el < 1000000
615
638
  ? toInterval(el)
616
639
  : 'new';
617
640
 
618
641
  el = sb.progressReports && sb.progressReports.last
619
- ? Date.now() - (sb.sliceStartTime + sb.progressReports.last.timestamp)
642
+ ? Date.now() - (sb.sliceStartTime + (sb.progressReports.last?.timestamp ?? 0))
620
643
  : 0;
621
644
  const pct = (typeof sb.progress) === 'number'
622
645
  ? `${Number(sb.progress).toFixed(0).padStart(2)}%`
@@ -648,14 +671,14 @@ function handleSigDeath(signalName, signal)
648
671
  process.off(signalName, handleSigDeath);
649
672
 
650
673
  if (!worker)
651
- console.error(`trapped ${signalName}, signal ${signal}`);
674
+ console.warn(`trapped ${signalName}, signal ${signal}`);
652
675
  else
653
676
  {
654
- console.error(`trapped ${signalName}, signal ${signal} -- stopping worker`);
677
+ console.warn(`trapped ${signalName}, signal ${signal} -- stopping worker`);
655
678
  worker.stop(signalName === 'SIGQUIT');
656
679
  }
657
680
 
658
- setTimeout(() => process.exit(signal - 128), getCleanupTimeoutMs()).unref();
681
+ setTimeout(() => processExit(signal - 128), getCleanupTimeoutMs()).unref();
659
682
  }
660
683
 
661
684
  /**
@@ -675,7 +698,7 @@ function getCleanupTimeoutMs()
675
698
  cleanupTimeout = defaultCT;
676
699
  if (!getCleanupTimeoutMs.warned)
677
700
  {
678
- console.warn(`warning: dcpConfig.worker.cleanupTimeout is not a number (${dcpConfig.worker.cleanupTimeout})`);
701
+ console.error(`warning: dcpConfig.worker.cleanupTimeout is not a number (${dcpConfig.worker.cleanupTimeout})`);
679
702
  getCleanupTimeoutMs.warned = true;
680
703
  }
681
704
  }
@@ -697,7 +720,7 @@ function verifyDefaultConfigIntegrity()
697
720
 
698
721
  if (!fs.existsSync(md5sumPath))
699
722
  {
700
- console.log(chalk.bold.red(` ! warning: ${md5sumPath} not found; cannot verify configuration integrity`));
723
+ console.error(chalk.bold.red(` ! warning: ${md5sumPath} not found; cannot verify configuration integrity`));
701
724
  require('dcp/utils').sleep(2);
702
725
  }
703
726
  else
@@ -719,7 +742,7 @@ function verifyDefaultConfigIntegrity()
719
742
  console.warn(' - the Windows Registry');
720
743
 
721
744
  if (require('dcp/build').config.build !== 'debug')
722
- process.exit(1);
745
+ processExit(1);
723
746
 
724
747
  console.log(chalk.bold.red.inverse("If this wasn't a debug build, the worker would exit now."));
725
748
  require('dcp/utils').sleep(2);
@@ -729,10 +752,18 @@ function verifyDefaultConfigIntegrity()
729
752
  if (dcpConfig.cookie !== process.env.DCP_CONFIG_COOKIE || !dcpConfig.cookie)
730
753
  {
731
754
  console.error(' ! DCP Worker default configuration was not loaded; exiting.');
732
- process.exit(1);
755
+ processExit(1);
733
756
  }
734
757
  }
735
758
 
759
+ /* thunk - ensures global debugging() symbol always available even if called before dcp-client init */
760
+ function debugging()
761
+ {
762
+ require('dcp-client');
763
+ debugging = require('dcp/internal/debugging').scope('dcp-worker');
764
+ return debugging.apply(this, arguments);
765
+ }
766
+
736
767
  /**
737
768
  * Cast b to boolean such that 'false' becomes false, falsey things become false, and everything else
738
769
  * becomes true.
package/docs/CODEOWNERS CHANGED
@@ -18,9 +18,11 @@
18
18
  /package-lock.json @eroosenmaallen
19
19
 
20
20
  [Wes]
21
+ /npm-hooks/ @wesgarland
21
22
  /README.md @wesgarland
22
23
  /docs/ @wesgarland
23
24
  /.eslintrc.json @wesgarland
25
+ /.npmrc @wesgarland
24
26
  /.tidelift @wesgarland
25
27
  /lib/ @wesgarland
26
28
  /LICENSE.md @wesgarland
@@ -49,7 +49,7 @@
49
49
  // keystore('~/.dcp/scott'),
50
50
  ],
51
51
 
52
- jobAddresses: [], /* If specified, restrict the worker to only these jobs */
52
+ jobAddresses: false, /* If specified, restrict the worker to only these jobs */
53
53
  paymentAddress: undefined, /* Bank account where earned funds are transfered if not specified on command-line */
54
54
  },
55
55
 
@@ -1,2 +1,2 @@
1
- 8edf1c57bee521ed7f151a21b8f3f34d
1
+ 4d49057dd014344e4be5a266e9754824
2
2
  ### DO NOT MODIFY THIS FILE!!! ###
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
 
2
3
  Object.assign(exports, require('./log'));
3
4
  Object.assign(exports, require('./sandboxes'));
@@ -7,6 +7,7 @@
7
7
  * log component, but features text wrapping and behaves
8
8
  * closer to the built-in console log methods.
9
9
  */
10
+ 'use strict';
10
11
 
11
12
  const { Box } = require('blessed');
12
13
 
@@ -67,11 +67,7 @@ class Sandboxes extends Box {
67
67
  update(data=this.data) {
68
68
  this.data = data;
69
69
  for (let i = 0; i < this.data.length; i++) {
70
- if (i < this.progressBars.length) {
71
- this.updateProgressBar(i, this.data[i]);
72
- } else {
73
- this.createProgressBar();
74
- }
70
+ this.updateProgressBar(i, this.data[i]);
75
71
  }
76
72
 
77
73
  if (this.data.length < this.progressBars.length) {
@@ -80,7 +76,15 @@ class Sandboxes extends Box {
80
76
  }
81
77
  }
82
78
 
83
- this.setLabel(`${this.options.label} (${this.data.length})`);
79
+ this.setLabel(`${this.options.label} (${this.progressBars.length})`);
80
+ }
81
+
82
+ // Deletes last progress bar
83
+ deleteProgressBar() {
84
+ let i = this.progressBars.length - 1;
85
+ this.progressBars[i].label.destroy()
86
+ this.progressBars[i].progressBar.destroy()
87
+ this.progressBars.pop(i)
84
88
  }
85
89
  }
86
90
 
@@ -6,6 +6,7 @@
6
6
  * @author Sam Cantor
7
7
  * @date Nov 2020
8
8
  */
9
+ 'use strict';
9
10
 
10
11
  exports.check = function checkSchedulerVersion$$check(quiet)
11
12
  {