dcp-worker 4.3.7 → 4.4.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/bin/dcp-evaluator-manager +15 -1
- package/bin/dcp-evaluator-start +9 -2
- package/bin/dcp-worker +197 -101
- package/lib/default-ui-events.js +3 -3
- package/lib/pidfile.js +7 -3
- package/lib/show.js +7 -16
- package/lib/telnetd.js +1 -1
- package/lib/utils.js +219 -9
- package/lib/web-iface.js +495 -91
- package/lib/worker-consoles/dashboard-console.js +8 -1
- package/lib/worker-consoles/stdio-console.js +9 -2
- package/package.json +6 -5
- package/www/admin/index.html +22 -0
- package/www/admin/manage-worker.html +19 -0
- package/www/admin/register-worker.html +334 -0
- package/www/hud/dark.css +10 -0
- package/www/hud/hud-common.css +19 -0
- package/www/hud/hud-common.mjs +69 -0
- package/www/hud/index.html +46 -0
- package/www/hud/small-dark.html +346 -0
- package/lib/web-console.js +0 -178
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dcp-worker",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "Node.js Worker for Distributive Compute Platform",
|
|
5
5
|
"main": "bin/dcp-worker",
|
|
6
6
|
"keywords": [
|
|
@@ -39,17 +39,17 @@
|
|
|
39
39
|
"blessed": "0.1.81",
|
|
40
40
|
"blessed-contrib": "4.11.0",
|
|
41
41
|
"chalk": "4.1.2",
|
|
42
|
-
"dcp-client": "^5.5.
|
|
42
|
+
"dcp-client": "^5.5.5",
|
|
43
43
|
"kvin": "^9.0.0",
|
|
44
44
|
"posix-getopt": "1.2.1",
|
|
45
45
|
"semver": "7.7.3",
|
|
46
|
-
"
|
|
46
|
+
"socket.io": "4.8.1",
|
|
47
47
|
"syslog-client-tls": "github:wesgarland/node-syslog-client#0d734f33767bc6a8275552d3daa51712cc2d306d",
|
|
48
48
|
"websocket": "1.0.34"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"dbus-next": "0.
|
|
52
|
-
"telnet-console": "^1.0.
|
|
51
|
+
"dbus-next": "0.10.2",
|
|
52
|
+
"telnet-console": "^1.0.7"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@distributive/eslint-config": "2.1.4",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
},
|
|
76
76
|
"dbus-next": {
|
|
77
77
|
"xml2js": "0.6.2",
|
|
78
|
+
"tar": "7.5.9",
|
|
78
79
|
"usocket": {
|
|
79
80
|
"node-gyp": "10.0.1"
|
|
80
81
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--
|
|
3
|
+
- @file admin/index.html - main entry point for worker's web management ui
|
|
4
|
+
- @author Wes Garland, wes@distributive.network
|
|
5
|
+
- @date Aug 2025
|
|
6
|
+
-->
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<script src="/dcp-client/dcp-client.js"></script>
|
|
10
|
+
<link rel="stylesheet" type="text/css" dcp-cdn-href="/css/dcp-style.css">
|
|
11
|
+
<script>
|
|
12
|
+
if (!dcpConfig.worker.registered)
|
|
13
|
+
window.location.href = '/admin/register-worker.html';
|
|
14
|
+
else
|
|
15
|
+
dcp.identity.login([
|
|
16
|
+
{ 'worker.admin': { related: dcpConfig.worker.id } },
|
|
17
|
+
], 3600);
|
|
18
|
+
</script>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--
|
|
3
|
+
- @file
|
|
4
|
+
- @author Wes Garland, wes@distributive.network
|
|
5
|
+
- @date Aug 2025
|
|
6
|
+
-->
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<script src="/dcp-client/dcp-client.js"></script>
|
|
10
|
+
<link rel="stylesheet" type="text/css" dcp-cdn-href="/css/dcp-style.css">
|
|
11
|
+
<script>
|
|
12
|
+
dcp.identity.login([
|
|
13
|
+
{ 'worker.admin': { related: dcpConfig.worker.id } },
|
|
14
|
+
], 3600);
|
|
15
|
+
</script>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--
|
|
3
|
+
- @file register-worker.html - displayed when index detects worker is not registered. Asks user for
|
|
4
|
+
* and runs through the registration workflow.
|
|
5
|
+
- @author Wes Garland, wes@distributive.network
|
|
6
|
+
- @date Aug 2025
|
|
7
|
+
-->
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<head>
|
|
10
|
+
<script src="/dcp-client/dcp-client.js"></script>
|
|
11
|
+
<script>
|
|
12
|
+
window.addEventListener('error', ev => dcp['dom-tk'].modals.alert(ev));
|
|
13
|
+
window.addEventListener('unhandledrejection', ev => dcp['dom-tk'].modals.alert(ev.reason));
|
|
14
|
+
</script>
|
|
15
|
+
<link rel="stylesheet" type="text/css" dcp-cdn-href="/css/dcp-style.css">
|
|
16
|
+
<style type="text/css">
|
|
17
|
+
/**
|
|
18
|
+
* This page operates as a series of whole-page (wp) interstitials, where the user moves from step to
|
|
19
|
+
* step until they have registered their worker. Each step is in a distinct SECTION. The appearance of
|
|
20
|
+
* the step is controlled by the .wp-dialog class, which can be on a SECTION or a contained inline block
|
|
21
|
+
* element. Moving from step to step is done by changing the step attribute on the body.
|
|
22
|
+
*/
|
|
23
|
+
BODY[step] SECTION[step] {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
BODY[step="3"] SECTION[step="3"],
|
|
28
|
+
BODY[step="2"] SECTION[step="2"],
|
|
29
|
+
BODY[step="1"] SECTION[step="1"] {
|
|
30
|
+
display: block;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
SECTION
|
|
34
|
+
{
|
|
35
|
+
text-align: center;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.wp-dialog {
|
|
39
|
+
margin: 2em;
|
|
40
|
+
padding: 2em;
|
|
41
|
+
border: 1px solid #999;
|
|
42
|
+
background-color: #f5f5f5;
|
|
43
|
+
border-radius: 3px;
|
|
44
|
+
}
|
|
45
|
+
SECTION.wp-dialog {
|
|
46
|
+
padding-top: 1.8em;
|
|
47
|
+
width: 80%;
|
|
48
|
+
max-width: 800px;
|
|
49
|
+
position: absolute;
|
|
50
|
+
left: 50%;
|
|
51
|
+
transform: translateX(-50%);
|
|
52
|
+
}
|
|
53
|
+
FORM.wp-dialog {
|
|
54
|
+
display: inline-block;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* INPUT[type="text"].with-eye is a password-like element with an eye icon that toggles password
|
|
59
|
+
* visibility. Visibility toggling happens by adding/removing the 'obscured' attribute.
|
|
60
|
+
*/
|
|
61
|
+
INPUT[type="text"].with-eye[obscured] {
|
|
62
|
+
-webkit-text-security: disc;
|
|
63
|
+
}
|
|
64
|
+
INPUT[type="text"].with-eye[obscured] + .show-eye {
|
|
65
|
+
background-image: url("/dcp-client/assets/lucide/eye-off.svg");
|
|
66
|
+
}
|
|
67
|
+
INPUT[type="text"].with-eye + .show-eye {
|
|
68
|
+
background-image: url("/dcp-client/assets/lucide/eye.svg");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Appearance of the eye itself */
|
|
72
|
+
.show-eye {
|
|
73
|
+
display: inline-block;
|
|
74
|
+
background-image: url("/dcp-client/assets/lucide/eye.svg");
|
|
75
|
+
background-repeat: no-repeat;
|
|
76
|
+
background-size: cover;
|
|
77
|
+
height: 0.75em;
|
|
78
|
+
width: 0.75em;
|
|
79
|
+
right: 1.1em;
|
|
80
|
+
margin-left: -1.1em;
|
|
81
|
+
transform: translateY(10%);
|
|
82
|
+
opacity: 50%;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#errorMessage {
|
|
87
|
+
display: block;
|
|
88
|
+
position: absolute;
|
|
89
|
+
margin-top: 2px;
|
|
90
|
+
height: 0;
|
|
91
|
+
color: red;
|
|
92
|
+
font-size: 0.8em;
|
|
93
|
+
font-style: italic;
|
|
94
|
+
transition: opacity 150ms ease-out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#errorMessage[hidden] {
|
|
98
|
+
opacity: 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#rkey {
|
|
102
|
+
font-family: roboto, ubuntu mono, monospace, lucida console, courier new;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
DIV.key-and-error {
|
|
106
|
+
display: inline-block;
|
|
107
|
+
position: relative;
|
|
108
|
+
top: 0.65px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A LOADER element is a throbber that becomes visible when it has a loading attribute that is not
|
|
113
|
+
* false. See loading().
|
|
114
|
+
*/
|
|
115
|
+
LOADER {
|
|
116
|
+
display: inline-block;
|
|
117
|
+
background-image: url("/dcp-client/assets/lucide/loader-circle.svg");
|
|
118
|
+
background-repeat: no-repeat;
|
|
119
|
+
background-size: cover;
|
|
120
|
+
height: 1em;
|
|
121
|
+
width: 1em;
|
|
122
|
+
margin: 0;
|
|
123
|
+
padding: 0;
|
|
124
|
+
visibility: hidden;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
LOADER[loading]:not([loading="false"]) {
|
|
128
|
+
transition: rotate 250ms linear;
|
|
129
|
+
animation: rotate 1s linear infinite;
|
|
130
|
+
visibility: visible;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@keyframes rotate {
|
|
134
|
+
0% {
|
|
135
|
+
transform: rotate(0deg);
|
|
136
|
+
}
|
|
137
|
+
100% {
|
|
138
|
+
transform: rotate(360deg);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
142
|
+
<script>
|
|
143
|
+
const $ = document.querySelector.bind(document); /* jQuery: The Good Parts */
|
|
144
|
+
function main()
|
|
145
|
+
{
|
|
146
|
+
Array.from(document.querySelectorAll('.show-eye')).forEach(element => {
|
|
147
|
+
element.addEventListener('click', ev => {
|
|
148
|
+
const inputElement = element.previousSibling;
|
|
149
|
+
if (inputElement.hasAttribute('obscured'))
|
|
150
|
+
element.previousSibling.removeAttribute('obscured');
|
|
151
|
+
else
|
|
152
|
+
element.previousSibling.setAttribute('obscured', 'true');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
$('#rkey').addEventListener('change', () => $('#errorMessage').setAttribute('hidden', true));
|
|
156
|
+
$('#rkey').addEventListener('keydown', () => $('#errorMessage').setAttribute('hidden', true));
|
|
157
|
+
|
|
158
|
+
document.body.setAttribute('step', 1);
|
|
159
|
+
|
|
160
|
+
if (dcp['dcp-build'].config.build === 'debug')
|
|
161
|
+
{
|
|
162
|
+
function hashChange()
|
|
163
|
+
{
|
|
164
|
+
if (window.location.hash.startsWith('#step='))
|
|
165
|
+
document.body.setAttribute('step', window.location.hash.slice(6));
|
|
166
|
+
}
|
|
167
|
+
hashChange();
|
|
168
|
+
window.addEventListener('hashchange', hashChange);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** toggle the first LOADER element in the current SECTION. */
|
|
173
|
+
function loader(state)
|
|
174
|
+
{
|
|
175
|
+
const step = document.body.getAttribute('step');
|
|
176
|
+
if (!step)
|
|
177
|
+
throw new Error('Cannot display loader; no body step defined');
|
|
178
|
+
|
|
179
|
+
console.log(`SECTION[step="${step}"] LOADER`, $(`SECTION[step="${step}"] LOADER`));
|
|
180
|
+
$(`SECTION[step="${step}"] LOADER`).setAttribute('loading', Boolean(state));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Register the worker with the given registration key. An endpoint in the dcp-worker web-iface is used
|
|
185
|
+
* to proxy the registration to Althea so that we don't need to send the worker's id.keystore to the
|
|
186
|
+
* front end.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} rkey string version of PrivateKey
|
|
189
|
+
*/
|
|
190
|
+
async function registerWorker(rkey)
|
|
191
|
+
{
|
|
192
|
+
const { alert } = dcp['dom-tk'].modals;
|
|
193
|
+
var error;
|
|
194
|
+
|
|
195
|
+
document.body.setAttribute('step', 2);
|
|
196
|
+
loader(true);
|
|
197
|
+
const p$sleep = dcp.utils.a$sleep(1); /* comfort sleep */
|
|
198
|
+
const reg = await dcp.utils.postQuery('/worker/register', { rkey });
|
|
199
|
+
await p$sleep;
|
|
200
|
+
loader(false);
|
|
201
|
+
|
|
202
|
+
document.body.setAttribute('step', 3);
|
|
203
|
+
if (!reg.success)
|
|
204
|
+
error = reg;
|
|
205
|
+
if (reg.error)
|
|
206
|
+
error = reg.error;
|
|
207
|
+
if (error)
|
|
208
|
+
{
|
|
209
|
+
await alert(error);
|
|
210
|
+
window.location.hash = '';
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (reg.failExpn)
|
|
215
|
+
{
|
|
216
|
+
await alert('Cannot register worker: ' + reg.failExpn, { title: 'Registration Failure' });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (reg.status === 'transfer-lock')
|
|
221
|
+
{
|
|
222
|
+
await alert('This worker is transfer-locked.\n' +
|
|
223
|
+
'The registered owner must release the lock via the Distributive Portal before it can be registered again.',
|
|
224
|
+
{ title: 'Transfer Locked'});
|
|
225
|
+
document.body.setAttribute('step', 1);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await waitForReboot(reg.pid);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Wait for the worker to reboot. The worker is also the webserver serving up this content, so we have to
|
|
234
|
+
* be careful requesting content while we are waiting for the reboot. The server knows to wait for the
|
|
235
|
+
* registerWorker() response to be fully transmitted before going down, and the loader graphic should
|
|
236
|
+
* already be loaded in memory from the previous step.
|
|
237
|
+
*/
|
|
238
|
+
async function waitForReboot(currentPid)
|
|
239
|
+
{
|
|
240
|
+
const { alert } = dcp['dom-tk'].modals;
|
|
241
|
+
loader(true);
|
|
242
|
+
|
|
243
|
+
for (let i=0; i < 60; i++)
|
|
244
|
+
{
|
|
245
|
+
await dcp.utils.a$sleep(1);
|
|
246
|
+
try
|
|
247
|
+
{
|
|
248
|
+
document.body.setAttribute('step', 3);
|
|
249
|
+
const response = (await dcp.utils.reallyJustFetch('/worker/pid')).responseText;
|
|
250
|
+
const newPid = Number(response);
|
|
251
|
+
if (isNaN(newPid))
|
|
252
|
+
throw new Error('invalid pid: ' + response);
|
|
253
|
+
if (newPid !== currentPid)
|
|
254
|
+
{
|
|
255
|
+
loader(false);
|
|
256
|
+
document.body.setAttribute('step', 0);
|
|
257
|
+
await alert('Registration complete.');
|
|
258
|
+
window.location.href = '/';
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch(error)
|
|
263
|
+
{
|
|
264
|
+
switch(error.code)
|
|
265
|
+
{
|
|
266
|
+
case 'EHTTP_STATUS_0':
|
|
267
|
+
case 'EHTTP_STATUS_502':
|
|
268
|
+
case 'EHTTP_STATUS_503':
|
|
269
|
+
case 'EHTTP_STATUS_504':
|
|
270
|
+
break;
|
|
271
|
+
default:
|
|
272
|
+
loader(false);
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
loader(false);
|
|
279
|
+
throw new Error('Timeout communicating with worker');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Submit handler for FORM. Does basic input validation hands off to registerWorker().
|
|
284
|
+
*/
|
|
285
|
+
function submitForm()
|
|
286
|
+
{
|
|
287
|
+
try
|
|
288
|
+
{
|
|
289
|
+
var rkey = $('#rkey').value.toLowerCase().trim();
|
|
290
|
+
if (rkey.startsWith('0x'))
|
|
291
|
+
rkey = rkey.slice(2);
|
|
292
|
+
if (/^(0x)?[0-9a-f]*$/.test(rkey) && rkey.length === 64)
|
|
293
|
+
registerWorker(rkey);
|
|
294
|
+
else
|
|
295
|
+
$('#errorMessage').removeAttribute('hidden');
|
|
296
|
+
}
|
|
297
|
+
catch(error)
|
|
298
|
+
{
|
|
299
|
+
console.error(error);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
</script>
|
|
305
|
+
</head>
|
|
306
|
+
<body step="0" onload="main();">
|
|
307
|
+
<section step="1">
|
|
308
|
+
<div style="display: none">
|
|
309
|
+
<h1>This worker is not registered.</h1>
|
|
310
|
+
<h3>You must register this worker before you can configure it.</h3>
|
|
311
|
+
</div>
|
|
312
|
+
<form class="wp-dialog" action="#" method="POST" onsubmit="return submitForm();" autocomplete="off">
|
|
313
|
+
Enter Registration Key:
|
|
314
|
+
<div class="key-and-error">
|
|
315
|
+
<input class="with-eye" id="rkey" type="text" obscured size="68"><div class="show-eye"></div>
|
|
316
|
+
<span id="errorMessage" hidden="true">
|
|
317
|
+
Please enter your registration key (64 hexadecimal characters) in the field above.
|
|
318
|
+
</span>
|
|
319
|
+
</div>
|
|
320
|
+
<input type="submit" value="Register">
|
|
321
|
+
</form>
|
|
322
|
+
<p>
|
|
323
|
+
If you do not have a registration code, <a href="generate-registration-code.html">click here</a>
|
|
324
|
+
to log into your Distributive account and generate one.
|
|
325
|
+
</p>
|
|
326
|
+
</section>
|
|
327
|
+
<section step="2" class="wp-dialog">
|
|
328
|
+
Registering worker <loader/>
|
|
329
|
+
</section>
|
|
330
|
+
<section step="3" class="wp-dialog">
|
|
331
|
+
Restarting worker <loader/>
|
|
332
|
+
</section>
|
|
333
|
+
</body>
|
|
334
|
+
</html>
|
package/www/hud/dark.css
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file status/worker-common.css - css support for worker-common.js
|
|
3
|
+
* @author Wes Garland, wes@distributive.network
|
|
4
|
+
* @date Feb 2026
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Behaviour for busy() / unbusy() */
|
|
8
|
+
BODY {
|
|
9
|
+
transition: all 150ms ease-in;
|
|
10
|
+
}
|
|
11
|
+
BODY[busy] {
|
|
12
|
+
cursor: wait;
|
|
13
|
+
filter: brightness(90%) saturate(70%);
|
|
14
|
+
backdrop-filter: brightness(90%) saturate(70%);
|
|
15
|
+
transition: all 2500ms ease-out;
|
|
16
|
+
}
|
|
17
|
+
BODY[busy] > * {
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file status/worker-common.js - common library functions for working with the worker
|
|
3
|
+
* @author Wes Garland, wes@distributive.network
|
|
4
|
+
* @date Feb 2026
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const $ = window.$ = document.querySelector.bind(document);
|
|
8
|
+
const $$ = window.$$ = document.querySelectorAll.bind(document);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reference-counted "busy" UI hint, depends on portal-iframe.css for behaviour.
|
|
12
|
+
*/
|
|
13
|
+
export function busy()
|
|
14
|
+
{
|
|
15
|
+
document.body.setAttribute('busy', Number(document.body.getAttribute('busy') || '0') + 1);
|
|
16
|
+
return arguments[0];
|
|
17
|
+
}
|
|
18
|
+
export function unbusy()
|
|
19
|
+
{
|
|
20
|
+
const next = Number(document.body.getAttribute('busy') || '0') - 1;
|
|
21
|
+
if (next > 0)
|
|
22
|
+
document.body.setAttribute('busy', next);
|
|
23
|
+
else
|
|
24
|
+
document.body.removeAttribute('busy');
|
|
25
|
+
return arguments[0];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function waitForReboot(currentPid)
|
|
29
|
+
{
|
|
30
|
+
const { alert } = dcp['dom-tk'].modals;
|
|
31
|
+
busy();
|
|
32
|
+
|
|
33
|
+
for (let i=0; i < 60; i++)
|
|
34
|
+
{
|
|
35
|
+
await dcp.utils.a$sleep(1);
|
|
36
|
+
try
|
|
37
|
+
{
|
|
38
|
+
const response = (await dcp.utils.reallyJustFetch('/worker/pid')).responseText;
|
|
39
|
+
const newPid = Number(response);
|
|
40
|
+
if (isNaN(newPid))
|
|
41
|
+
throw new Error('invalid pid: ' + response);
|
|
42
|
+
if (newPid !== currentPid)
|
|
43
|
+
{
|
|
44
|
+
loader(false);
|
|
45
|
+
document.body.setAttribute('step', 0);
|
|
46
|
+
await alert('Registration complete.');
|
|
47
|
+
window.location.href = '/';
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch(error)
|
|
52
|
+
{
|
|
53
|
+
switch(error.code)
|
|
54
|
+
{
|
|
55
|
+
case 'EHTTP_STATUS_0':
|
|
56
|
+
case 'EHTTP_STATUS_502':
|
|
57
|
+
case 'EHTTP_STATUS_503':
|
|
58
|
+
case 'EHTTP_STATUS_504':
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
unbusy();
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
unbusy();
|
|
68
|
+
throw new Error('Timeout communicating with worker');
|
|
69
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--
|
|
3
|
+
- @file index.html - extremely basic status screen for the worker
|
|
4
|
+
- @author Wes Garland, wes@distributive.network
|
|
5
|
+
- @date Feb 2026
|
|
6
|
+
-->
|
|
7
|
+
<html lang="en">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="UTF-8">
|
|
10
|
+
<meta http-equiv="refresh" content="15">
|
|
11
|
+
<title>DCP Worker - HUD</title>
|
|
12
|
+
<link rel="stylesheet" type="text/css" href="./hud-common.css">
|
|
13
|
+
<script src="/etc/dcp-config.js"></script>
|
|
14
|
+
<script src="/dcp-client/dcp-client.js"></script>
|
|
15
|
+
<link rel="stylesheet" type="text/css" dcp-cdn-href="/css/dcp-style.css">
|
|
16
|
+
<script cdn-src="/dom-tk.js?dcp-worker-version=4.3.7"></script>
|
|
17
|
+
<script type="module">
|
|
18
|
+
import * as hud from "./hud-common.mjs";
|
|
19
|
+
window.addEventListener('error', ev => dcp['dom-tk'].modals.alert(ev));
|
|
20
|
+
window.addEventListener('unhandledrejection', ev => dcp['dom-tk'].modals.alert(ev.reason));
|
|
21
|
+
</script>
|
|
22
|
+
<style type="text/css">
|
|
23
|
+
</style>
|
|
24
|
+
<script src="/worker/run-info"></script>
|
|
25
|
+
<script src="/worker/task-info"></script>
|
|
26
|
+
<script>
|
|
27
|
+
const $ = document.querySelector.bind(document);
|
|
28
|
+
const $$ = document.querySelectorAll.bind(document);
|
|
29
|
+
async function init()
|
|
30
|
+
{
|
|
31
|
+
runInfo.startTime = new Date(runInfo.startTime);
|
|
32
|
+
$$('[run-info]').forEach(element => {
|
|
33
|
+
const prop = element.getAttribute('run-info');
|
|
34
|
+
element.textContent = runInfo[prop];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
</head>
|
|
39
|
+
<body onload="init();">
|
|
40
|
+
<center><h3>DCP Worker</h3></center>
|
|
41
|
+
<table align=center>
|
|
42
|
+
<tr><td>Running Since:</td><td run-info="startTime"></td></tr>
|
|
43
|
+
<tr><td>Earnings Since Start:</td><td run-info="totalEarnings"></td></tr>
|
|
44
|
+
<tr><td>Earnings Account:</td><td run-info="earningsAccount"></td></tr>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|