ultravisor 1.0.12 → 1.0.14
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultravisor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Cyclic process execution with ai integration.",
|
|
5
5
|
"main": "source/Ultravisor.cjs",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"test": "npx quack test",
|
|
12
12
|
"tests": "npx mocha -u tdd --exit -R spec --grep",
|
|
13
13
|
"coverage": "npx quack coverage",
|
|
14
|
+
"postinstall": "cd webinterface && npm install && npm run build && cd ..",
|
|
14
15
|
"build": "npx quack build && cd webinterface && npm install && npm run build && cd ..",
|
|
15
16
|
"docs": "npx quack prepare-docs ./docs -d ./modules",
|
|
16
17
|
"docs-serve": "npx quack docs-serve ./docs"
|
|
@@ -433,10 +433,32 @@ class UltravisorBeaconClient
|
|
|
433
433
|
if (tmpOutputs.ExitCode && tmpOutputs.ExitCode !== 0)
|
|
434
434
|
{
|
|
435
435
|
console.warn(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed with exit code ${tmpOutputs.ExitCode}`);
|
|
436
|
+
this._reportComplete(pWorkItem.WorkItemHash, tmpOutputs, pResult.Log || []);
|
|
437
|
+
return;
|
|
436
438
|
}
|
|
437
|
-
|
|
439
|
+
|
|
440
|
+
console.log(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed successfully.`);
|
|
441
|
+
|
|
442
|
+
// If the result contains a file, upload it to the coordinator before reporting completion
|
|
443
|
+
if (tmpOutputs.Result && tmpOutputs.OutputSize > 0)
|
|
438
444
|
{
|
|
439
|
-
|
|
445
|
+
let tmpResultPath = tmpOutputs.Result;
|
|
446
|
+
let tmpOutputFile = pWorkItem.Settings && pWorkItem.Settings.OutputFile;
|
|
447
|
+
let tmpFilename = tmpOutputFile || require('path').basename(tmpResultPath);
|
|
448
|
+
|
|
449
|
+
this._uploadResultFile(pWorkItem.WorkItemHash, tmpResultPath, tmpFilename, (pUploadError) =>
|
|
450
|
+
{
|
|
451
|
+
if (pUploadError)
|
|
452
|
+
{
|
|
453
|
+
console.warn(`[Beacon] File upload failed for [${pWorkItem.WorkItemHash}]: ${pUploadError.message} — reporting completion without file`);
|
|
454
|
+
}
|
|
455
|
+
else
|
|
456
|
+
{
|
|
457
|
+
console.log(`[Beacon] Uploaded result file [${tmpFilename}] for [${pWorkItem.WorkItemHash}]`);
|
|
458
|
+
}
|
|
459
|
+
this._reportComplete(pWorkItem.WorkItemHash, tmpOutputs, pResult.Log || []);
|
|
460
|
+
});
|
|
461
|
+
return;
|
|
440
462
|
}
|
|
441
463
|
|
|
442
464
|
this._reportComplete(pWorkItem.WorkItemHash, tmpOutputs, pResult.Log || []);
|
|
@@ -447,6 +469,58 @@ class UltravisorBeaconClient
|
|
|
447
469
|
// Reporting
|
|
448
470
|
// ================================================================
|
|
449
471
|
|
|
472
|
+
_uploadResultFile(pWorkItemHash, pFilePath, pFilename, fCallback)
|
|
473
|
+
{
|
|
474
|
+
let tmpFS = require('fs');
|
|
475
|
+
|
|
476
|
+
if (!tmpFS.existsSync(pFilePath))
|
|
477
|
+
{
|
|
478
|
+
return fCallback(new Error(`File not found: ${pFilePath}`));
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
let tmpFileBuffer = tmpFS.readFileSync(pFilePath);
|
|
482
|
+
let tmpParsedURL = new URL(this._Config.ServerURL);
|
|
483
|
+
|
|
484
|
+
let tmpOptions = {
|
|
485
|
+
hostname: tmpParsedURL.hostname,
|
|
486
|
+
port: tmpParsedURL.port || 80,
|
|
487
|
+
path: `/Beacon/Work/${pWorkItemHash}/Upload`,
|
|
488
|
+
method: 'POST',
|
|
489
|
+
headers: {
|
|
490
|
+
'Content-Type': 'application/octet-stream',
|
|
491
|
+
'Content-Length': tmpFileBuffer.length,
|
|
492
|
+
'X-Output-Filename': pFilename
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
if (this._SessionCookie)
|
|
497
|
+
{
|
|
498
|
+
tmpOptions.headers['Cookie'] = this._SessionCookie;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
let tmpReq = libHTTP.request(tmpOptions, (pResponse) =>
|
|
502
|
+
{
|
|
503
|
+
let tmpData = '';
|
|
504
|
+
pResponse.on('data', (pChunk) => { tmpData += pChunk; });
|
|
505
|
+
pResponse.on('end', () =>
|
|
506
|
+
{
|
|
507
|
+
if (pResponse.statusCode >= 400)
|
|
508
|
+
{
|
|
509
|
+
return fCallback(new Error(`Upload failed: HTTP ${pResponse.statusCode}`));
|
|
510
|
+
}
|
|
511
|
+
return fCallback(null);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
tmpReq.on('error', (pError) =>
|
|
516
|
+
{
|
|
517
|
+
return fCallback(pError);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
tmpReq.write(tmpFileBuffer);
|
|
521
|
+
tmpReq.end();
|
|
522
|
+
}
|
|
523
|
+
|
|
450
524
|
_reportComplete(pWorkItemHash, pOutputs, pLog)
|
|
451
525
|
{
|
|
452
526
|
this._httpRequest('POST', `/Beacon/Work/${pWorkItemHash}/Complete`,
|
|
@@ -215,7 +215,9 @@ module.exports =
|
|
|
215
215
|
// Already exists — fine
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
let tmpTimeoutMs = parseInt(pResolvedSettings.TimeoutMs, 10) || 300000;
|
|
219
|
+
|
|
220
|
+
pTask.log.info(`File Transfer: downloading ${tmpSourceURL} → ${tmpFilename} (timeout ${Math.round(tmpTimeoutMs / 1000)}s)`);
|
|
219
221
|
|
|
220
222
|
// Validate URL before attempting request
|
|
221
223
|
if (!tmpSourceURL.startsWith('http://') && !tmpSourceURL.startsWith('https://'))
|
|
@@ -269,10 +271,10 @@ module.exports =
|
|
|
269
271
|
});
|
|
270
272
|
});
|
|
271
273
|
|
|
272
|
-
// Set
|
|
273
|
-
tmpRequest.setTimeout(
|
|
274
|
+
// Set timeout on the request (configurable via TimeoutMs setting, default 5 min)
|
|
275
|
+
tmpRequest.setTimeout(tmpTimeoutMs, function ()
|
|
274
276
|
{
|
|
275
|
-
tmpRequest.destroy(new Error(
|
|
277
|
+
tmpRequest.destroy(new Error(`File Transfer: download timed out after ${Math.round(tmpTimeoutMs / 1000)} seconds`));
|
|
276
278
|
});
|
|
277
279
|
}
|
|
278
280
|
},
|