dcp-worker 3.2.30-2 → 3.2.30-3

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
@@ -8,21 +8,16 @@
8
8
  */
9
9
  'use strict';
10
10
 
11
+ var worker;
12
+
11
13
  const process = require('process');
12
14
  const fs = require('fs');
15
+ const path = require('path');
13
16
  const crypto = require('crypto');
14
17
  const chalk = require('chalk');
18
+ const telnetd = require('../lib/remote-console');
15
19
 
16
20
  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
21
  const EXIT_UNHANDLED = 5;
27
22
 
28
23
  /* Setup the telnet REPL up early to ensure early-failure log messages are captured */
@@ -34,11 +29,11 @@ const replHelpers = {
34
29
  },
35
30
  commands: {
36
31
  report: printReport,
37
- kill: process.exit,
32
+ kill: processExit,
38
33
  die: () => worker && worker.stop()
39
34
  },
40
35
  };
41
- require('../lib/remote-console').init(replHelpers);
36
+ telnetd.init(replHelpers);
42
37
 
43
38
  /* Initialize dcp-client with local config defaults and run the main function. DCP_CONFIG_COOKIE becomes dcpConfig.cookie. */
44
39
  process.env.DCP_CONFIG_COOKIE = (Math.random().toString(16)).slice(2) + '-' + process.pid + '-' + Date.now();
@@ -48,7 +43,6 @@ function parseCliArgs()
48
43
  {
49
44
  var defaultPidFileName;
50
45
 
51
- dcpConfig = require('dcp/dcp-config');
52
46
  defaultPidFileName = require('../lib/pidfile').getDefaultPidFileName(dcpConfig.worker.pidfile);
53
47
 
54
48
  const cliArgs = require('dcp/cli')
@@ -98,18 +92,21 @@ function parseCliArgs()
98
92
  },
99
93
  priorityOnly: {
100
94
  alias: 'P',
95
+ hidden: true,
101
96
  describe: 'Set the priority mode [deprecated]',
102
97
  type: 'boolean',
103
98
  default: false
104
99
  },
105
100
  'job-id': {
106
101
  alias: 'j',
102
+ hidden: true,
107
103
  describe: 'Restrict worker to a specific job (use N times for N jobs)',
108
104
  type: 'array',
109
105
  },
110
106
 
111
107
  join: {
112
108
  alias: 'g',
109
+ hidden: true,
113
110
  describe: 'Join compute group; the format is "joinKey,joinSecret" or "joinKey,eh1-joinHash"',
114
111
  type: 'array'
115
112
  },
@@ -120,22 +117,27 @@ function parseCliArgs()
120
117
 
121
118
  leavePublicGroup: {
122
119
  type: 'boolean',
120
+ hidden: true,
123
121
  describe: 'Do not fetch slices from public compute group.',
124
122
  default: undefined,
125
123
  },
126
124
 
127
125
  publicGroupFallback: {
126
+ hidden: true,
128
127
  describe: 'If set, worker will prefer private groups but fall back on the public group if no preferred work is available',
129
128
  type: 'boolean',
130
- default: undefined,
129
+ default: 'undefined',
130
+ defaultDescription: undefined,
131
131
  },
132
132
 
133
133
  identityKey: {
134
+ hidden: true,
134
135
  describe: 'Identity key, in hex format',
135
136
  type: 'string',
136
137
  group: 'Identity options',
137
138
  },
138
139
  identityKeystore: {
140
+ hidden: true,
139
141
  describe: 'Identity keystore, in json format',
140
142
  type: 'string',
141
143
  group: 'Identity options',
@@ -147,30 +149,40 @@ function parseCliArgs()
147
149
  group: 'Output options',
148
150
  },
149
151
  eventDebug: {
150
- hide: true,
152
+ hidden: true,
151
153
  describe: 'If set, dump all sandbox and worker events',
152
154
  },
153
155
 
154
156
  logfile: {
155
- describe: 'Path to log file (if --output=file)',
157
+ describe: 'Path to log file',
156
158
  type: 'string',
157
159
  group: 'Log File output options',
160
+ default: path.resolve('../log/dcp-worker.log'),
158
161
  },
159
162
  syslogAddress: {
160
- describe: 'Address of rsyslog server (if --output=syslog)',
163
+ describe: 'Address of syslog server',
161
164
  type: 'string',
162
165
  group: 'Syslog output options',
166
+ default: 'loghost',
167
+ },
168
+ syslogFacility: {
169
+ describe: 'Name of syslog facility',
170
+ type: 'string',
171
+ group: 'Syslog output options',
172
+ default: 'local7',
163
173
  },
164
174
  syslogTransport: {
165
- describe: 'Transport to connect to rsyslog daemon (if --output=syslog)',
175
+ describe: 'Transport to connect to use for syslog',
166
176
  type: 'string',
167
- choices: ['udp','tcp'],
177
+ choices: ['udp','tcp','unix','tls'],
168
178
  group: 'Syslog output options',
179
+ default: 'udp',
169
180
  },
170
181
  syslogPort: {
171
- describe: 'UDP/TCP port of rsyslog server',
182
+ describe: 'UDP/TCP port to use for syslog',
172
183
  type: 'number',
173
184
  group: 'Syslog output options',
185
+ default: 514,
174
186
  },
175
187
 
176
188
  allowedOrigins: {
@@ -202,8 +214,8 @@ function parseCliArgs()
202
214
 
203
215
  if (cliArgs.dumpConfig)
204
216
  {
205
- console.debug(JSON.stringify(require('dcp/dcp-config'), null, 2));
206
- process.exit(0);
217
+ console.log(JSON.stringify(require('dcp/dcp-config'), null, 2));
218
+ processExit(0);
207
219
  }
208
220
 
209
221
  return cliArgs;
@@ -228,6 +240,20 @@ function addConfig(target, ...objs)
228
240
  Object.assign(target, tmp);
229
241
  }
230
242
 
243
+ /**
244
+ * Replacement for process.exit() that tries to increase the probability
245
+ * that remote log messages will make it out over the network.
246
+ */
247
+ function processExit()
248
+ {
249
+ logClosing('debug', 'Exit Code:', process.exitCode || 0);
250
+ if (console.close)
251
+ console.close();
252
+ setImmediate(() => {
253
+ process.exit.apply(null, arguments);
254
+ });
255
+ }
256
+
231
257
  /**
232
258
  * Main program entry point. Assumes DCP client is already initialized and console logging is ready.
233
259
  */
@@ -235,21 +261,22 @@ async function main()
235
261
  {
236
262
  const wallet = require('dcp/wallet');
237
263
  const DCPWorker = require('dcp/worker').Worker;
238
- const { startWorkerLogger } = require('../lib/startWorkerLogger');
239
264
  const cliArgs = parseCliArgs();
240
265
  const sawOptions = {
241
266
  hostname: cliArgs.hostname,
242
267
  port: cliArgs.port
243
268
  };
244
269
 
245
- verifyDefaultConfigIntegrity(); /* Bail before TUI & as early as possible if config file is bad */
270
+ telnetd.setMainEval(function mainEval() { return eval(arguments[0]) }); // eslint-disable-line no-eval
271
+ require('../lib/startWorkerLogger').init(cliArgs); /* Start remote logger as early as possible */
272
+ verifyDefaultConfigIntegrity(); /* Bail before TUI & as early as possible if bad conf */
246
273
 
247
274
  process.on('SIGINT', handleSigDeath);
248
275
  process.on('SIGTERM', handleSigDeath);
249
276
  process.on('SIGQUIT', handleSigDeath);
250
277
  process.on('unhandledRejection', handleUnhandled);
251
278
  process.on('uncaughtException', handleUnhandled);
252
-
279
+
253
280
  let paymentAddress = false
254
281
  || cliArgs.paymentAddress
255
282
  || dcpConfig.worker.paymentAddress
@@ -285,6 +312,7 @@ async function main()
285
312
  const dcpWorkerOptions = dcpConfig.worker;
286
313
  const forceOptions = {
287
314
  paymentAddress,
315
+ maxWorkingSandboxes: cliArgs.cores,
288
316
  };
289
317
  const defaultOptions = {
290
318
  sandboxOptions: {
@@ -339,27 +367,26 @@ async function main()
339
367
  dcpWorkerOptions.watchdogInterval = cliArgs.watchdogInterval;
340
368
 
341
369
  worker = new DCPWorker(identityKeystore, dcpWorkerOptions);
342
- worker.on('error', (...payload) => console.error(...payload));
343
- worker.on('warning', (...payload) => console.warn(...payload));
370
+ worker.on('error', (...payload) => console.error(...payload));
371
+ worker.on('warning', (...payload) => console.warn (...payload));
372
+ worker.on('stop', () => { console.log('Worker is stopping') });
373
+ worker.on('end', () => { logClosing('log', 'Worker has stopped') });
374
+ require('../lib/default-ui-events').hook(worker, cliArgs);
344
375
 
376
+ if (cliArgs.outputMode === 'dashboard')
377
+ require('../lib/dashboard-tui').init(worker, cliArgs);
378
+
345
379
  /* Let incorrect event-loop references keep us alive when linked with a debug library, but
346
380
  * exit quickly/accurately for production code even when the library isn't perfect.
347
381
  */
348
382
  if (require('dcp/build').config.build !== 'debug')
349
- worker.on('end', process.exit);
383
+ worker.on('end', processExit);
350
384
  else
351
- worker.on('end', () => setTimeout(process.exit, getCleanupTimeoutMs()).unref());
385
+ worker.on('end', () => setTimeout(processExit, getCleanupTimeoutMs()).unref());
352
386
 
353
387
  if (cliArgs.eventDebug)
354
388
  worker.enableDebugEvents = true;
355
389
 
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
390
  if (dcpWorkerOptions.publicGroupFallback)
364
391
  {
365
392
  if (dcpWorkerOptions.leavePublicGroup)
@@ -381,12 +408,12 @@ async function main()
381
408
  return;
382
409
  }
383
410
 
384
- if (typeof ev === 'string') /* <= June 2023 Worker events: remove ~ Sep 2023 /wg */
411
+ if (typeof ev === 'number' || typeof ev === 'string') /* <= June 2023 Worker events: remove ~ Sep 2023 /wg */
385
412
  slicesFetched = ev;
386
413
  else
387
414
  {
388
415
  const task = ev;
389
- slicesFetched = task.slices?.length;
416
+ slicesFetched = task.slices.length;
390
417
  }
391
418
 
392
419
  dcpWorkerOptions.leavePublicGroup = Boolean(slicesFetched > 0);
@@ -416,21 +443,23 @@ async function main()
416
443
 
417
444
  if (dcpWorkerOptions.jobAddresses?.length > 0)
418
445
  introBanner += ` * Processing only ${qty(dcpWorkerOptions.jobAddresses, 'job')} ` + dcpWorkerOptions.jobAddresses.join(', ') + '\n';
419
- if (dcpWorkerOptions.computeGroups?.length > 0)
446
+ if (dcpWorkerOptions.computeGroups && Object.keys(dcpWorkerOptions.computeGroups).length > 0)
420
447
  introBanner += ` . Joining compute ${qty(dcpWorkerOptions.computeGroups, 'group')} ` + dcpWorkerOptions.computeGroups.map(el => el.joinKey).join(', ') + '\n';
421
448
  if (dcpWorkerOptions.publicGroupFallback)
422
449
  introBanner += ' . Falling back on public group when preferred groups have no work' + '\n';
423
450
  if (dcpWorkerOptions.leavePublicGroup)
424
451
  introBanner += ' . Leaving the public compute group' + '\n';
425
452
  if (dcpWorkerOptions.cores)
426
- introBanner += ` . Target cores: ${JSON.stringify(dcpWorkerOptions.cores)}\n`;
453
+ introBanner += ` . Configured Cores: ${dcpWorkerOptions.cores.cpu},${dcpWorkerOptions.cores.gpu}\n`
427
454
  else
428
455
  introBanner += ` . Target core density: ${JSON.stringify(dcpWorkerOptions.defaultCoreDensity)}\n`;
429
456
  if (cliArgs.verbose)
430
457
  introBanner += ` + Verbosity level: ${cliArgs.verbose}` + '\n';
431
458
  if (cliArgs.eventDebug)
432
459
  introBanner += ' + Event debug on' + '\n';
433
-
460
+ if (telnetd.hasOwnProperty('port'))
461
+ introBanner += ` ! telnetd listening on port ${telnetd.port}\n`;
462
+
434
463
  introBanner += ' . Supervisor version: ' + worker.supervisorVersion;
435
464
  introBanner += ' . Output mode: ' + cliArgs.outputMode + '\n';
436
465
  introBanner += ' * Ready' + '\n';
@@ -443,7 +472,7 @@ async function main()
443
472
  if (cliArgs.outputMode !== 'dashboard')
444
473
  setInterval(printReport, parseFloat(cliArgs.reportInterval) * 1000).unref();
445
474
  else
446
- console.log('Ignoring --reportInterval in dashboard output mode');
475
+ console.warn('Ignoring --reportInterval in dashboard output mode');
447
476
  }
448
477
 
449
478
  /* Start the worker. Normal process exit happens by virtue of the worker<end> event */
@@ -482,9 +511,9 @@ function processCoresAndDensity (dcpWorkerOptions, cliArgs)
482
511
  parseArg('cores');
483
512
 
484
513
  if (dcpWorkerOptions.cores)
485
- debug('cores = ', dcpWorkerOptions.cores);
514
+ debugging() && console.debug('dcp-worker: cores =', dcpWorkerOptions.cores);
486
515
  else
487
- debug('core density = ', dcpWorkerOptions.defaultCoreDensity);
516
+ debugging() && console.debug('dcp-worker: core density =', dcpWorkerOptions.defaultCoreDensity);
488
517
  }
489
518
 
490
519
  /**
@@ -495,9 +524,7 @@ function logClosing(facility, ...message)
495
524
  {
496
525
  var screen = require('../lib/worker-loggers/dashboard').screen;
497
526
 
498
- if (!screen)
499
- console[facility](message);
500
- else
527
+ if (screen)
501
528
  {
502
529
  /* Turn off fullscreen TUI and resume "normal" console logging.
503
530
  * FUTURE: dashboard API should know how to unregister its hook so that we don't have to clobber
@@ -506,10 +533,11 @@ function logClosing(facility, ...message)
506
533
  screen.log(...message);
507
534
  screen.destroy();
508
535
  screen = false;
509
- console = new (require('console').Console)(process);
510
- require('../lib/remote-console').reintercept();
511
- console[facility].call(null, ...message);
536
+ console = new (require('console').Console)(process); // eslint-disable-line no-global-assign
537
+ telnetd.reintercept();
512
538
  }
539
+
540
+ console[facility](...message);
513
541
  }
514
542
 
515
543
  /**
@@ -518,7 +546,7 @@ function logClosing(facility, ...message)
518
546
  * the worker must be restarted. This handler does its best to report the rejection and give the worker a few
519
547
  * seconds in which to attempt to return slices to the scheduler before it gives up completely.
520
548
  */
521
- async function handleUnhandled(error)
549
+ function handleUnhandled(error)
522
550
  {
523
551
  var _worker = worker;
524
552
  worker = false;
@@ -527,21 +555,21 @@ async function handleUnhandled(error)
527
555
 
528
556
  try
529
557
  {
530
- logClosing(error);
531
- } catch(e) {};
558
+ logClosing('error', error);
559
+ } catch(e) {} // eslint-disable-line no-empty
532
560
 
533
561
  if (!_worker)
534
562
  console.error('trapped unhandled error:', error)
535
563
  else
536
564
  {
537
565
  console.error('trapped unhandled error -- stopping worker:', error);
538
- _worker.on('end', process.exit);
566
+ _worker.on('end', processExit);
539
567
  _worker.stop();
540
568
  }
541
569
 
542
570
  setTimeout(() => {
543
571
  logClosing('error', 'handleFatalError timeout - exiting now');
544
- process.exit();
572
+ processExit();
545
573
  }, getCleanupTimeoutMs()).unref();
546
574
 
547
575
  try {
@@ -551,7 +579,7 @@ async function handleUnhandled(error)
551
579
  fs.appendFileSync(process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG,
552
580
  `${Date.now()}: ${error.message}\n${error.stack}\n\n`);
553
581
  }
554
- } catch(e) {};
582
+ } catch(e) {} // eslint-disable-line no-empty
555
583
  }
556
584
 
557
585
  /** print the slice report via console.log */
@@ -648,14 +676,14 @@ function handleSigDeath(signalName, signal)
648
676
  process.off(signalName, handleSigDeath);
649
677
 
650
678
  if (!worker)
651
- console.error(`trapped ${signalName}, signal ${signal}`);
679
+ console.warn(`trapped ${signalName}, signal ${signal}`);
652
680
  else
653
681
  {
654
- console.error(`trapped ${signalName}, signal ${signal} -- stopping worker`);
682
+ console.warn(`trapped ${signalName}, signal ${signal} -- stopping worker`);
655
683
  worker.stop(signalName === 'SIGQUIT');
656
684
  }
657
685
 
658
- setTimeout(() => process.exit(signal - 128), getCleanupTimeoutMs()).unref();
686
+ setTimeout(() => processExit(signal - 128), getCleanupTimeoutMs()).unref();
659
687
  }
660
688
 
661
689
  /**
@@ -675,7 +703,7 @@ function getCleanupTimeoutMs()
675
703
  cleanupTimeout = defaultCT;
676
704
  if (!getCleanupTimeoutMs.warned)
677
705
  {
678
- console.warn(`warning: dcpConfig.worker.cleanupTimeout is not a number (${dcpConfig.worker.cleanupTimeout})`);
706
+ console.error(`warning: dcpConfig.worker.cleanupTimeout is not a number (${dcpConfig.worker.cleanupTimeout})`);
679
707
  getCleanupTimeoutMs.warned = true;
680
708
  }
681
709
  }
@@ -697,7 +725,7 @@ function verifyDefaultConfigIntegrity()
697
725
 
698
726
  if (!fs.existsSync(md5sumPath))
699
727
  {
700
- console.log(chalk.bold.red(` ! warning: ${md5sumPath} not found; cannot verify configuration integrity`));
728
+ console.error(chalk.bold.red(` ! warning: ${md5sumPath} not found; cannot verify configuration integrity`));
701
729
  require('dcp/utils').sleep(2);
702
730
  }
703
731
  else
@@ -719,7 +747,7 @@ function verifyDefaultConfigIntegrity()
719
747
  console.warn(' - the Windows Registry');
720
748
 
721
749
  if (require('dcp/build').config.build !== 'debug')
722
- process.exit(1);
750
+ processExit(1);
723
751
 
724
752
  console.log(chalk.bold.red.inverse("If this wasn't a debug build, the worker would exit now."));
725
753
  require('dcp/utils').sleep(2);
@@ -729,10 +757,18 @@ function verifyDefaultConfigIntegrity()
729
757
  if (dcpConfig.cookie !== process.env.DCP_CONFIG_COOKIE || !dcpConfig.cookie)
730
758
  {
731
759
  console.error(' ! DCP Worker default configuration was not loaded; exiting.');
732
- process.exit(1);
760
+ processExit(1);
733
761
  }
734
762
  }
735
763
 
764
+ /* thunk - ensures global debugging() symbol always available even if called before dcp-client init */
765
+ function debugging()
766
+ {
767
+ require('dcp-client');
768
+ debugging = require('dcp/internal/debugging').scope('dcp-worker');
769
+ return debugging.apply(this, arguments);
770
+ }
771
+
736
772
  /**
737
773
  * Cast b to boolean such that 'false' becomes false, falsey things become false, and everything else
738
774
  * 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
@@ -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
 
@@ -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
  {
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @file worker-loggers/dashboard.js
3
+ * @author Ryan Rossiter, ryan@kingsds.network
4
+ * @date April 2020
5
+ * @author Wes Garland, wes@distributive.network
6
+ * @date June 2023
7
+ *
8
+ * This module uses the blessed library to create a monitoring dashboard for the worker.
9
+ * A corresponding worker-logger, dashboard.js, knows how to log to this dashboard.
10
+ */
11
+ 'use strict';
12
+
13
+ const dcpConfig = require('dcp/dcp-config');
14
+ const chalk = require('chalk');
15
+ const blessed = require('blessed');
16
+ const contrib = require('blessed-contrib');
17
+ const components = require('./blessed-components');
18
+
19
+ const { replaceWorkerEvent, replaceSandboxEvent } = require('./default-ui-events');
20
+
21
+ const SLICE_FETCH_STATUS = {
22
+ IDLE: chalk.yellow('Idle'),
23
+ FETCHING: chalk.blue('Fetching Work...'),
24
+ WORKING: chalk.green('Working'),
25
+ NO_WORK: chalk.red('No Work Available'),
26
+ }
27
+
28
+ const usingDebugger = require('module')._cache.niim instanceof require('module').Module;
29
+ const screenConf = {
30
+ input: usingDebugger ? new (require('events').EventEmitter) : undefined,
31
+ output: usingDebugger ? new (require('events').EventEmitter) : undefined,
32
+ };
33
+ /**
34
+ */
35
+ exports.init = function dashboard$$init(worker, options)
36
+ {
37
+ var sliceFetchStatus = SLICE_FETCH_STATUS.IDLE;
38
+ var totalDCCs = 0;
39
+ const screen = blessed.screen(screenConf);
40
+ const grid = new contrib.grid({rows: 3, cols: 5, screen});
41
+ const workerInfoPane = grid.set(2, 0, 1, 5, blessed.text);
42
+
43
+ const logPane = grid.set(0, 2, 2, 3, components.log, {
44
+ label: 'Worker Log',
45
+ scrollable: true,
46
+ alwaysScroll: true,
47
+ mouse: true,
48
+ scrollbar: {
49
+ bg: 'blue',
50
+ },
51
+ });
52
+
53
+ const sandboxPane = grid.set(0, 0, 2, 2, components.sandboxes, {
54
+ label: 'Sandboxes',
55
+ defaultProgressBars: Math.floor(worker.workerOptions.cores.cpu) || 1,
56
+ scrollable: true,
57
+ alwaysScroll: true,
58
+ mouse: true,
59
+ scrollbar: {
60
+ bg: 'blue',
61
+ },
62
+ });
63
+
64
+ delete exports.init; /* singleton */
65
+
66
+ if (!usingDebugger)
67
+ exports.logPane = logPane; /* now dashboard log can find the pane */
68
+ setInterval(() => screen.render(), 50).unref(); /* 50ms = 20 fps */
69
+ updateWorkerInfo();
70
+ screen.render();
71
+
72
+ /* Apply key bindings which mimic canonical input mode */
73
+ screen.key(['C-c'], () => raise('SIGINT'));
74
+ screen.key(['C-z'], () => raise('SIGTSTP'));
75
+ screen.key(['\u001c'], () => raise('SIGQUIT')); /* C-\ */
76
+
77
+ screen.key(['escape'], () => {
78
+ console.log('Stopping worker...');
79
+ worker.stop();
80
+ });
81
+
82
+ function updateWorkerInfo()
83
+ {
84
+ const workerOptions = worker.workerOptions;
85
+
86
+ workerInfoPane.setLabel(`Worker Status [${sliceFetchStatus}]`);
87
+ workerInfoPane.setContent([
88
+ chalk.green(` DCCs Earned: ${chalk.bold(totalDCCs.toFixed(3))}`),
89
+ '',
90
+ ` Scheduler: ${chalk.yellow(dcpConfig.scheduler.location.href)}`,
91
+ ` Bank: ${chalk.yellow(dcpConfig.bank.location.href)}`,
92
+ `Bank Account: ${chalk.yellow(worker.paymentAddress || 'Starting...')}`,
93
+ ` Identity: ${chalk.yellow(worker.identityKeystore? worker.identityKeystore.address : 'Starting...')}`,
94
+ ` Jobs: ${workerOptions.jobAddresses?.length ? workerOptions.jobAddresses.join(', ') : '<any>'}`,
95
+ ` Priv Groups: ${Object.keys(workerOptions.computeGroups).length}`,
96
+ ` Pub Group: ${workerOptions.leavePublicGroup ? 'no' : 'yes'}`,
97
+ ].join('\n'));
98
+ }
99
+
100
+ /* Override default event behaviour to work better with the Dashboard. */
101
+
102
+ replaceSandboxEvent('start', function dashboard$$sliceStart(sandbox, sandboxData, ev) {
103
+ sandboxData.progressData = {
104
+ indeterminate: true,
105
+ progress: 0,
106
+ label: sandbox.public.name,
107
+ };
108
+
109
+ sandboxPane.data.push(sandboxData.progressData);
110
+ sandboxPane.update();
111
+ });
112
+
113
+ replaceSandboxEvent('sliceProgress', function dashboard$$sliceProgress(sandbox, sandboxData, ev) {
114
+ if (ev.indeterminate)
115
+ {
116
+ sandboxData.progressData.progress = 100;
117
+ setTimeout(() => {
118
+ if (sandboxData.progressData.indeterminate) {
119
+ sandboxData.progressData.progress = 0;
120
+ sandboxPane.update();
121
+ }
122
+ }, 500).unref();
123
+ }
124
+ else
125
+ {
126
+ sandboxData.progressData.progress = ev.progress;
127
+ sandboxData.progressData.indeterminate = false;
128
+ }
129
+
130
+ sandboxPane.update();
131
+ });
132
+
133
+ replaceSandboxEvent('sliceFinish', function dashboard$$sliceFinish(sandbox, sandboxData, ev) {
134
+ sandboxPane.data = sandboxPane.data.filter(d => d != sandboxData.progressData);
135
+ sandboxPane.update();
136
+ });
137
+
138
+ worker.on('payment', updateWorkerInfo);
139
+
140
+ replaceWorkerEvent('fetchStart', function dashboard$$fetchStart(ev) {
141
+ sliceFetchStatus = SLICE_FETCH_STATUS.FETCHING;
142
+ updateWorkerInfo();
143
+ });
144
+
145
+ replaceWorkerEvent('fetch', function dashboard$$fetch(ev) {
146
+ var slicesFetched;
147
+
148
+ if (ev instanceof Error)
149
+ {
150
+ console.error('Error fetching slices:', ev);
151
+ return;
152
+ }
153
+
154
+ if (typeof ev === 'number' || typeof ev === 'string') /* <= June 2023 Worker events: remove ~ Sep 2023 /wg */
155
+ slicesFetched = ev;
156
+ else
157
+ {
158
+ const task = ev;
159
+ slicesFetched = task.slices.length;
160
+ }
161
+
162
+ if (slicesFetched === 0 && sandboxPane.data.length === 0) {
163
+ sliceFetchStatus = SLICE_FETCH_STATUS.NO_WORK;
164
+ } else {
165
+ sliceFetchStatus = SLICE_FETCH_STATUS.WORKING;
166
+ }
167
+
168
+ updateWorkerInfo();
169
+ });
170
+
171
+ replaceWorkerEvent('fetchError', function dashabord$$fetchError() {
172
+ sliceFetchStatus = SLICE_FETCH_STATUS.NO_WORK;
173
+ updateWorkerInfo();
174
+ });
175
+ };
176
+
177
+ /**
178
+ * Send a signal to the caller
179
+ * @param {number|string} sig the signal to raise
180
+ */
181
+ function raise(sig)
182
+ {
183
+ process.kill(process.pid, sig);
184
+ }