ultravisor-beacon 0.0.7 → 0.0.8
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 +1 -1
- package/source/Ultravisor-Beacon-CapabilityManager.cjs +9 -3
- package/source/Ultravisor-Beacon-Client.cjs +91 -48
- package/source/Ultravisor-Beacon-Executor.cjs +8 -3
- package/source/Ultravisor-Beacon-ProviderRegistry.cjs +14 -8
- package/source/Ultravisor-Beacon-Service.cjs +1 -0
package/package.json
CHANGED
|
@@ -29,8 +29,14 @@ const libCapabilityAdapter = require('./Ultravisor-Beacon-CapabilityAdapter.cjs'
|
|
|
29
29
|
|
|
30
30
|
class UltravisorBeaconCapabilityManager
|
|
31
31
|
{
|
|
32
|
-
constructor()
|
|
32
|
+
constructor(pLog)
|
|
33
33
|
{
|
|
34
|
+
this.log = pLog || {
|
|
35
|
+
info: (...pArgs) => { console.log(...pArgs); },
|
|
36
|
+
warn: (...pArgs) => { this.log.warn(...pArgs); },
|
|
37
|
+
error: (...pArgs) => { this.log.error(...pArgs); }
|
|
38
|
+
};
|
|
39
|
+
|
|
34
40
|
// Map of Capability name -> descriptor
|
|
35
41
|
this._Capabilities = {};
|
|
36
42
|
}
|
|
@@ -45,13 +51,13 @@ class UltravisorBeaconCapabilityManager
|
|
|
45
51
|
{
|
|
46
52
|
if (!pDescriptor || !pDescriptor.Capability)
|
|
47
53
|
{
|
|
48
|
-
|
|
54
|
+
this.log.error('[CapabilityManager] Descriptor must have a Capability name.');
|
|
49
55
|
return false;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
if (!pDescriptor.actions || Object.keys(pDescriptor.actions).length === 0)
|
|
53
59
|
{
|
|
54
|
-
|
|
60
|
+
this.log.warn(`[CapabilityManager] Capability "${pDescriptor.Capability}" has no actions.`);
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
this._Capabilities[pDescriptor.Capability] = pDescriptor;
|
|
@@ -45,6 +45,15 @@ class UltravisorBeaconClient
|
|
|
45
45
|
Tags: {}
|
|
46
46
|
}, pConfig || {});
|
|
47
47
|
|
|
48
|
+
// Logger: use provided Fable log or fall back to console
|
|
49
|
+
this.log = this._Config.Log || {
|
|
50
|
+
trace: (...pArgs) => { console.log(...pArgs); },
|
|
51
|
+
debug: (...pArgs) => { console.log(...pArgs); },
|
|
52
|
+
info: (...pArgs) => { console.log(...pArgs); },
|
|
53
|
+
warn: (...pArgs) => { console.warn(...pArgs); },
|
|
54
|
+
error: (...pArgs) => { console.error(...pArgs); }
|
|
55
|
+
};
|
|
56
|
+
|
|
48
57
|
this._BeaconID = null;
|
|
49
58
|
this._PollInterval = null;
|
|
50
59
|
this._HeartbeatInterval = null;
|
|
@@ -52,13 +61,17 @@ class UltravisorBeaconClient
|
|
|
52
61
|
this._ActiveWorkItems = 0;
|
|
53
62
|
this._SessionCookie = null;
|
|
54
63
|
this._Authenticating = false;
|
|
64
|
+
this._ReconnectPending = false;
|
|
65
|
+
this._ReconnectAttempts = 0;
|
|
66
|
+
this._MaxReconnectDelayMs = 300000;
|
|
55
67
|
|
|
56
68
|
// WebSocket transport state — determined at runtime, not config
|
|
57
69
|
this._WebSocket = null;
|
|
58
70
|
this._UseWebSocket = false;
|
|
59
71
|
|
|
60
72
|
this._Executor = new libBeaconExecutor({
|
|
61
|
-
StagingPath: this._Config.StagingPath
|
|
73
|
+
StagingPath: this._Config.StagingPath,
|
|
74
|
+
Log: this.log
|
|
62
75
|
});
|
|
63
76
|
|
|
64
77
|
// Load capability providers
|
|
@@ -84,7 +97,7 @@ class UltravisorBeaconClient
|
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
let tmpCount = this._Executor.providerRegistry.loadProviders(tmpProviders);
|
|
87
|
-
|
|
100
|
+
this.log.info(`[Beacon] Loaded ${tmpCount} capability provider(s).`);
|
|
88
101
|
}
|
|
89
102
|
|
|
90
103
|
// ================================================================
|
|
@@ -96,31 +109,31 @@ class UltravisorBeaconClient
|
|
|
96
109
|
*/
|
|
97
110
|
start(fCallback)
|
|
98
111
|
{
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
this.log.info(`[Beacon] Starting "${this._Config.Name}"...`);
|
|
113
|
+
this.log.info(`[Beacon] Server: ${this._Config.ServerURL}`);
|
|
101
114
|
|
|
102
115
|
// Initialize all providers before registering
|
|
103
116
|
this._Executor.providerRegistry.initializeAll((pInitError) =>
|
|
104
117
|
{
|
|
105
118
|
if (pInitError)
|
|
106
119
|
{
|
|
107
|
-
|
|
120
|
+
this.log.error(`[Beacon] Provider initialization failed: ${pInitError.message}`);
|
|
108
121
|
return fCallback(pInitError);
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
let tmpCapabilities = this._Executor.providerRegistry.getCapabilities();
|
|
112
|
-
|
|
125
|
+
this.log.info(`[Beacon] Capabilities: ${tmpCapabilities.join(', ')}`);
|
|
113
126
|
|
|
114
127
|
// Authenticate before registering (both transports need a session)
|
|
115
128
|
this._authenticate((pAuthError) =>
|
|
116
129
|
{
|
|
117
130
|
if (pAuthError)
|
|
118
131
|
{
|
|
119
|
-
|
|
132
|
+
this.log.error(`[Beacon] Authentication failed: ${pAuthError.message}`);
|
|
120
133
|
return fCallback(pAuthError);
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
|
|
136
|
+
this.log.info(`[Beacon] Authenticated successfully.`);
|
|
124
137
|
|
|
125
138
|
// Try WebSocket first — if ws library is available and the
|
|
126
139
|
// server supports it, we get push-based work dispatch.
|
|
@@ -131,7 +144,7 @@ class UltravisorBeaconClient
|
|
|
131
144
|
{
|
|
132
145
|
if (pWSError)
|
|
133
146
|
{
|
|
134
|
-
|
|
147
|
+
this.log.info(`[Beacon] WebSocket unavailable (${pWSError.message}), using HTTP polling.`);
|
|
135
148
|
this._UseWebSocket = false;
|
|
136
149
|
this._startHTTP(fCallback);
|
|
137
150
|
return;
|
|
@@ -157,14 +170,14 @@ class UltravisorBeaconClient
|
|
|
157
170
|
{
|
|
158
171
|
if (pError)
|
|
159
172
|
{
|
|
160
|
-
|
|
173
|
+
this.log.error(`[Beacon] Registration failed: ${pError.message}`);
|
|
161
174
|
return fCallback(pError);
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
this._BeaconID = pBeacon.BeaconID;
|
|
165
178
|
this._Running = true;
|
|
166
179
|
|
|
167
|
-
|
|
180
|
+
this.log.info(`[Beacon] Registered as ${this._BeaconID}`);
|
|
168
181
|
|
|
169
182
|
// Start polling for work
|
|
170
183
|
this._PollInterval = setInterval(() =>
|
|
@@ -190,7 +203,7 @@ class UltravisorBeaconClient
|
|
|
190
203
|
*/
|
|
191
204
|
stop(fCallback)
|
|
192
205
|
{
|
|
193
|
-
|
|
206
|
+
this.log.info(`[Beacon] Stopping...`);
|
|
194
207
|
this._Running = false;
|
|
195
208
|
|
|
196
209
|
if (this._PollInterval)
|
|
@@ -226,7 +239,7 @@ class UltravisorBeaconClient
|
|
|
226
239
|
{
|
|
227
240
|
if (pShutdownError)
|
|
228
241
|
{
|
|
229
|
-
|
|
242
|
+
this.log.warn(`[Beacon] Provider shutdown warning: ${pShutdownError.message}`);
|
|
230
243
|
}
|
|
231
244
|
|
|
232
245
|
if (this._BeaconID)
|
|
@@ -235,15 +248,15 @@ class UltravisorBeaconClient
|
|
|
235
248
|
{
|
|
236
249
|
if (pError)
|
|
237
250
|
{
|
|
238
|
-
|
|
251
|
+
this.log.warn(`[Beacon] Deregistration warning: ${pError.message}`);
|
|
239
252
|
}
|
|
240
|
-
|
|
253
|
+
this.log.info(`[Beacon] Stopped.`);
|
|
241
254
|
if (fCallback) return fCallback(null);
|
|
242
255
|
});
|
|
243
256
|
}
|
|
244
257
|
else
|
|
245
258
|
{
|
|
246
|
-
|
|
259
|
+
this.log.info(`[Beacon] Stopped.`);
|
|
247
260
|
if (fCallback) return fCallback(null);
|
|
248
261
|
}
|
|
249
262
|
});
|
|
@@ -291,7 +304,7 @@ class UltravisorBeaconClient
|
|
|
291
304
|
// Take the name=value portion (before the first semicolon)
|
|
292
305
|
let tmpCookieParts = tmpSetCookieHeaders[0].split(';');
|
|
293
306
|
this._SessionCookie = tmpCookieParts[0].trim();
|
|
294
|
-
|
|
307
|
+
this.log.info(`[Beacon] Session cookie acquired.`);
|
|
295
308
|
}
|
|
296
309
|
|
|
297
310
|
try
|
|
@@ -341,25 +354,25 @@ class UltravisorBeaconClient
|
|
|
341
354
|
|
|
342
355
|
this._SessionCookie = null;
|
|
343
356
|
|
|
344
|
-
|
|
357
|
+
this.log.info(`[Beacon] Reconnecting — re-authenticating...`);
|
|
345
358
|
|
|
346
359
|
this._authenticate((pAuthError) =>
|
|
347
360
|
{
|
|
348
361
|
if (pAuthError)
|
|
349
362
|
{
|
|
350
|
-
|
|
363
|
+
this.log.error(`[Beacon] Re-authentication failed: ${pAuthError.message}`);
|
|
351
364
|
this._Authenticating = false;
|
|
352
365
|
setTimeout(() => { this._reconnect(); }, 10000);
|
|
353
366
|
return;
|
|
354
367
|
}
|
|
355
368
|
|
|
356
|
-
|
|
369
|
+
this.log.info(`[Beacon] Re-authenticated, re-registering...`);
|
|
357
370
|
|
|
358
371
|
this._register((pRegError, pBeacon) =>
|
|
359
372
|
{
|
|
360
373
|
if (pRegError)
|
|
361
374
|
{
|
|
362
|
-
|
|
375
|
+
this.log.error(`[Beacon] Re-registration failed: ${pRegError.message}`);
|
|
363
376
|
this._Authenticating = false;
|
|
364
377
|
setTimeout(() => { this._reconnect(); }, 10000);
|
|
365
378
|
return;
|
|
@@ -368,7 +381,7 @@ class UltravisorBeaconClient
|
|
|
368
381
|
this._BeaconID = pBeacon.BeaconID;
|
|
369
382
|
this._Authenticating = false;
|
|
370
383
|
|
|
371
|
-
|
|
384
|
+
this.log.info(`[Beacon] Reconnected as ${this._BeaconID}`);
|
|
372
385
|
|
|
373
386
|
// Restart polling
|
|
374
387
|
this._PollInterval = setInterval(() =>
|
|
@@ -471,7 +484,7 @@ class UltravisorBeaconClient
|
|
|
471
484
|
_executeWorkItem(pWorkItem)
|
|
472
485
|
{
|
|
473
486
|
this._ActiveWorkItems++;
|
|
474
|
-
|
|
487
|
+
this.log.info(`[Beacon] Executing work item [${pWorkItem.WorkItemHash}] (${pWorkItem.Capability}/${pWorkItem.Action})`);
|
|
475
488
|
|
|
476
489
|
// Create a progress callback that sends updates to the server
|
|
477
490
|
let tmpWorkItemHash = pWorkItem.WorkItemHash;
|
|
@@ -493,7 +506,7 @@ class UltravisorBeaconClient
|
|
|
493
506
|
|
|
494
507
|
if (pError)
|
|
495
508
|
{
|
|
496
|
-
|
|
509
|
+
this.log.error(`[Beacon] Execution error for [${pWorkItem.WorkItemHash}]: ${pError.message}`);
|
|
497
510
|
if (this._UseWebSocket)
|
|
498
511
|
{
|
|
499
512
|
this._wsReportError(pWorkItem.WorkItemHash, pError.message, []);
|
|
@@ -509,11 +522,11 @@ class UltravisorBeaconClient
|
|
|
509
522
|
let tmpOutputs = pResult.Outputs || {};
|
|
510
523
|
if (tmpOutputs.ExitCode && tmpOutputs.ExitCode !== 0)
|
|
511
524
|
{
|
|
512
|
-
|
|
525
|
+
this.log.warn(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed with exit code ${tmpOutputs.ExitCode}`);
|
|
513
526
|
}
|
|
514
527
|
else
|
|
515
528
|
{
|
|
516
|
-
|
|
529
|
+
this.log.info(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed successfully.`);
|
|
517
530
|
}
|
|
518
531
|
|
|
519
532
|
// Upload output file if one was produced (Result is a local path)
|
|
@@ -529,7 +542,7 @@ class UltravisorBeaconClient
|
|
|
529
542
|
try
|
|
530
543
|
{
|
|
531
544
|
let tmpBuffer = tmpFS.readFileSync(tmpResultPath);
|
|
532
|
-
|
|
545
|
+
this.log.info(`[Beacon] Uploading result file ${tmpOutputFilename} (${tmpBuffer.length} bytes) for [${pWorkItem.WorkItemHash}]`);
|
|
533
546
|
this._wsSend({
|
|
534
547
|
Action: 'WorkResultUpload',
|
|
535
548
|
WorkItemHash: pWorkItem.WorkItemHash,
|
|
@@ -540,7 +553,7 @@ class UltravisorBeaconClient
|
|
|
540
553
|
}
|
|
541
554
|
catch (pUploadError)
|
|
542
555
|
{
|
|
543
|
-
|
|
556
|
+
this.log.error(`[Beacon] Failed to upload result file: ${pUploadError.message}`);
|
|
544
557
|
}
|
|
545
558
|
}
|
|
546
559
|
}
|
|
@@ -568,7 +581,7 @@ class UltravisorBeaconClient
|
|
|
568
581
|
{
|
|
569
582
|
if (pError)
|
|
570
583
|
{
|
|
571
|
-
|
|
584
|
+
this.log.error(`[Beacon] Failed to report completion for [${pWorkItemHash}]: ${pError.message}`);
|
|
572
585
|
}
|
|
573
586
|
});
|
|
574
587
|
}
|
|
@@ -581,7 +594,7 @@ class UltravisorBeaconClient
|
|
|
581
594
|
{
|
|
582
595
|
if (pError)
|
|
583
596
|
{
|
|
584
|
-
|
|
597
|
+
this.log.error(`[Beacon] Failed to report error for [${pWorkItemHash}]: ${pError.message}`);
|
|
585
598
|
}
|
|
586
599
|
});
|
|
587
600
|
}
|
|
@@ -600,7 +613,7 @@ class UltravisorBeaconClient
|
|
|
600
613
|
if (pError)
|
|
601
614
|
{
|
|
602
615
|
// Fire-and-forget — log but don't affect execution
|
|
603
|
-
|
|
616
|
+
this.log.warn(`[Beacon] Failed to report progress for [${pWorkItemHash}]: ${pError.message}`);
|
|
604
617
|
}
|
|
605
618
|
});
|
|
606
619
|
}
|
|
@@ -621,7 +634,7 @@ class UltravisorBeaconClient
|
|
|
621
634
|
{
|
|
622
635
|
if (pError)
|
|
623
636
|
{
|
|
624
|
-
|
|
637
|
+
this.log.warn(`[Beacon] Heartbeat failed: ${pError.message}`);
|
|
625
638
|
}
|
|
626
639
|
});
|
|
627
640
|
}
|
|
@@ -730,7 +743,7 @@ class UltravisorBeaconClient
|
|
|
730
743
|
|
|
731
744
|
this._WebSocket.on('open', () =>
|
|
732
745
|
{
|
|
733
|
-
|
|
746
|
+
this.log.info(`[Beacon] WebSocket connected to ${tmpWSURL}`);
|
|
734
747
|
|
|
735
748
|
// Register over WebSocket
|
|
736
749
|
let tmpWSRegPayload = {
|
|
@@ -779,7 +792,7 @@ class UltravisorBeaconClient
|
|
|
779
792
|
|
|
780
793
|
this._WebSocket.on('error', (pError) =>
|
|
781
794
|
{
|
|
782
|
-
|
|
795
|
+
this.log.error(`[Beacon] WebSocket error: ${pError.message}`);
|
|
783
796
|
if (!tmpCallbackFired)
|
|
784
797
|
{
|
|
785
798
|
tmpCallbackFired = true;
|
|
@@ -789,13 +802,13 @@ class UltravisorBeaconClient
|
|
|
789
802
|
|
|
790
803
|
this._WebSocket.on('close', () =>
|
|
791
804
|
{
|
|
792
|
-
|
|
805
|
+
this.log.info(`[Beacon] WebSocket connection closed.`);
|
|
793
806
|
this._WebSocket = null;
|
|
794
807
|
|
|
795
|
-
if (this._Running && !this._Authenticating)
|
|
808
|
+
if (this._Running && !this._Authenticating && !this._ReconnectPending)
|
|
796
809
|
{
|
|
797
810
|
// Connection lost while running — attempt reconnect
|
|
798
|
-
this.
|
|
811
|
+
this._scheduleReconnect();
|
|
799
812
|
}
|
|
800
813
|
});
|
|
801
814
|
}
|
|
@@ -821,7 +834,7 @@ class UltravisorBeaconClient
|
|
|
821
834
|
if (tmpData.EventType === 'BeaconRegistered')
|
|
822
835
|
{
|
|
823
836
|
this._BeaconID = tmpData.BeaconID;
|
|
824
|
-
|
|
837
|
+
this.log.info(`[Beacon] Registered via WebSocket as ${this._BeaconID}`);
|
|
825
838
|
if (typeof fRegistrationCallback === 'function')
|
|
826
839
|
{
|
|
827
840
|
fRegistrationCallback({ BeaconID: this._BeaconID });
|
|
@@ -831,14 +844,14 @@ class UltravisorBeaconClient
|
|
|
831
844
|
{
|
|
832
845
|
if (this._ActiveWorkItems >= this._Config.MaxConcurrent)
|
|
833
846
|
{
|
|
834
|
-
|
|
847
|
+
this.log.info(`[Beacon] At max concurrent capacity, ignoring pushed work item.`);
|
|
835
848
|
return;
|
|
836
849
|
}
|
|
837
850
|
this._executeWorkItem(tmpData.WorkItem);
|
|
838
851
|
}
|
|
839
852
|
else if (tmpData.EventType === 'Deregistered')
|
|
840
853
|
{
|
|
841
|
-
|
|
854
|
+
this.log.info(`[Beacon] Deregistered by server.`);
|
|
842
855
|
this._BeaconID = null;
|
|
843
856
|
}
|
|
844
857
|
}
|
|
@@ -932,6 +945,34 @@ class UltravisorBeaconClient
|
|
|
932
945
|
}
|
|
933
946
|
}
|
|
934
947
|
|
|
948
|
+
/**
|
|
949
|
+
* Schedule a reconnection with exponential backoff.
|
|
950
|
+
* Prevents multiple close events from triggering parallel reconnections.
|
|
951
|
+
*/
|
|
952
|
+
_scheduleReconnect()
|
|
953
|
+
{
|
|
954
|
+
if (this._ReconnectPending || this._Authenticating)
|
|
955
|
+
{
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
this._ReconnectPending = true;
|
|
960
|
+
this._ReconnectAttempts++;
|
|
961
|
+
|
|
962
|
+
// Exponential backoff: 10s, 20s, 40s, 80s, 160s, capped at 5 min
|
|
963
|
+
let tmpDelay = Math.min(
|
|
964
|
+
this._Config.ReconnectIntervalMs * Math.pow(2, this._ReconnectAttempts - 1),
|
|
965
|
+
this._MaxReconnectDelayMs);
|
|
966
|
+
|
|
967
|
+
this.log.info(`[Beacon] Scheduling reconnection attempt ${this._ReconnectAttempts} in ${Math.round(tmpDelay / 1000)}s`);
|
|
968
|
+
|
|
969
|
+
setTimeout(() =>
|
|
970
|
+
{
|
|
971
|
+
this._ReconnectPending = false;
|
|
972
|
+
this._wsReconnect();
|
|
973
|
+
}, tmpDelay);
|
|
974
|
+
}
|
|
975
|
+
|
|
935
976
|
/**
|
|
936
977
|
* Reconnect the WebSocket after unexpected disconnection.
|
|
937
978
|
* Falls back to HTTP polling if WebSocket can't be re-established.
|
|
@@ -951,15 +992,15 @@ class UltravisorBeaconClient
|
|
|
951
992
|
}
|
|
952
993
|
|
|
953
994
|
this._SessionCookie = null;
|
|
954
|
-
|
|
995
|
+
this.log.info(`[Beacon] Reconnecting — attempt ${this._ReconnectAttempts}...`);
|
|
955
996
|
|
|
956
997
|
this._authenticate((pAuthError) =>
|
|
957
998
|
{
|
|
958
999
|
if (pAuthError)
|
|
959
1000
|
{
|
|
960
|
-
|
|
1001
|
+
this.log.error(`[Beacon] Re-authentication failed: ${pAuthError.message}`);
|
|
961
1002
|
this._Authenticating = false;
|
|
962
|
-
|
|
1003
|
+
this._scheduleReconnect();
|
|
963
1004
|
return;
|
|
964
1005
|
}
|
|
965
1006
|
|
|
@@ -971,21 +1012,23 @@ class UltravisorBeaconClient
|
|
|
971
1012
|
if (pError)
|
|
972
1013
|
{
|
|
973
1014
|
// WebSocket failed — fall back to HTTP polling
|
|
974
|
-
|
|
1015
|
+
this.log.info(`[Beacon] WebSocket reconnection failed, falling back to HTTP polling.`);
|
|
975
1016
|
this._UseWebSocket = false;
|
|
976
1017
|
this._startHTTP((pHTTPError, pHTTPBeacon) =>
|
|
977
1018
|
{
|
|
978
1019
|
if (pHTTPError)
|
|
979
1020
|
{
|
|
980
|
-
|
|
981
|
-
|
|
1021
|
+
this.log.error(`[Beacon] HTTP fallback failed: ${pHTTPError.message}`);
|
|
1022
|
+
this._scheduleReconnect();
|
|
982
1023
|
return;
|
|
983
1024
|
}
|
|
984
|
-
|
|
1025
|
+
this._ReconnectAttempts = 0;
|
|
1026
|
+
this.log.info(`[Beacon] Reconnected via HTTP polling as ${pHTTPBeacon.BeaconID}`);
|
|
985
1027
|
});
|
|
986
1028
|
return;
|
|
987
1029
|
}
|
|
988
|
-
|
|
1030
|
+
this._ReconnectAttempts = 0;
|
|
1031
|
+
this.log.info(`[Beacon] WebSocket reconnected as ${pBeacon.BeaconID}`);
|
|
989
1032
|
});
|
|
990
1033
|
});
|
|
991
1034
|
}
|
|
@@ -24,7 +24,12 @@ class UltravisorBeaconExecutor
|
|
|
24
24
|
{
|
|
25
25
|
this._Config = pConfig || {};
|
|
26
26
|
this._StagingPath = this._Config.StagingPath || process.cwd();
|
|
27
|
-
this.
|
|
27
|
+
this.log = this._Config.Log || {
|
|
28
|
+
info: (...pArgs) => { console.log(...pArgs); },
|
|
29
|
+
warn: (...pArgs) => { this.log.warn(...pArgs); },
|
|
30
|
+
error: (...pArgs) => { console.error(...pArgs); }
|
|
31
|
+
};
|
|
32
|
+
this._ProviderRegistry = new libBeaconProviderRegistry(this.log);
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
/**
|
|
@@ -432,7 +437,7 @@ class UltravisorBeaconExecutor
|
|
|
432
437
|
catch (pError)
|
|
433
438
|
{
|
|
434
439
|
// Best-effort cleanup
|
|
435
|
-
|
|
440
|
+
this.log.warn(`[Beacon Executor] Could not clean up work directory: ${pError.message}`);
|
|
436
441
|
}
|
|
437
442
|
}
|
|
438
443
|
|
|
@@ -457,7 +462,7 @@ class UltravisorBeaconExecutor
|
|
|
457
462
|
}
|
|
458
463
|
catch (pError)
|
|
459
464
|
{
|
|
460
|
-
|
|
465
|
+
this.log.warn(`[Beacon Executor] Could not clean up affinity dir [${tmpEntries[i]}]: ${pError.message}`);
|
|
461
466
|
}
|
|
462
467
|
}
|
|
463
468
|
}
|
|
@@ -15,8 +15,14 @@ const libPath = require('path');
|
|
|
15
15
|
|
|
16
16
|
class UltravisorBeaconProviderRegistry
|
|
17
17
|
{
|
|
18
|
-
constructor()
|
|
18
|
+
constructor(pLog)
|
|
19
19
|
{
|
|
20
|
+
this.log = pLog || {
|
|
21
|
+
info: (...pArgs) => { console.log(...pArgs); },
|
|
22
|
+
warn: (...pArgs) => { console.warn(...pArgs); },
|
|
23
|
+
error: (...pArgs) => { console.error(...pArgs); }
|
|
24
|
+
};
|
|
25
|
+
|
|
20
26
|
// Map of 'Capability:Action' → { provider, actionDef }
|
|
21
27
|
this._ActionHandlers = {};
|
|
22
28
|
|
|
@@ -40,7 +46,7 @@ class UltravisorBeaconProviderRegistry
|
|
|
40
46
|
{
|
|
41
47
|
if (!pProvider || !pProvider.Capability)
|
|
42
48
|
{
|
|
43
|
-
|
|
49
|
+
this.log.error('[ProviderRegistry] Provider must have a Capability.');
|
|
44
50
|
return false;
|
|
45
51
|
}
|
|
46
52
|
|
|
@@ -49,7 +55,7 @@ class UltravisorBeaconProviderRegistry
|
|
|
49
55
|
|
|
50
56
|
if (tmpActionNames.length === 0)
|
|
51
57
|
{
|
|
52
|
-
|
|
58
|
+
this.log.warn(`[ProviderRegistry] Provider "${pProvider.Name}" declares no actions.`);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
// Index each action by composite key
|
|
@@ -83,7 +89,7 @@ class UltravisorBeaconProviderRegistry
|
|
|
83
89
|
|
|
84
90
|
this._Providers[pProvider.Name] = pProvider;
|
|
85
91
|
|
|
86
|
-
|
|
92
|
+
this.log.info(`[ProviderRegistry] Registered "${pProvider.Name}" → ` +
|
|
87
93
|
`${pProvider.Capability} [${tmpActionNames.join(', ')}]`);
|
|
88
94
|
|
|
89
95
|
return true;
|
|
@@ -190,7 +196,7 @@ class UltravisorBeaconProviderRegistry
|
|
|
190
196
|
|
|
191
197
|
if (!tmpSource)
|
|
192
198
|
{
|
|
193
|
-
|
|
199
|
+
this.log.error('[ProviderRegistry] Provider descriptor must have a Source.');
|
|
194
200
|
return false;
|
|
195
201
|
}
|
|
196
202
|
|
|
@@ -222,13 +228,13 @@ class UltravisorBeaconProviderRegistry
|
|
|
222
228
|
}
|
|
223
229
|
catch (pError)
|
|
224
230
|
{
|
|
225
|
-
|
|
231
|
+
this.log.error(`[ProviderRegistry] Failed to load provider from "${tmpSource}": ${pError.message}`);
|
|
226
232
|
return false;
|
|
227
233
|
}
|
|
228
234
|
|
|
229
235
|
if (!tmpProviderModule)
|
|
230
236
|
{
|
|
231
|
-
|
|
237
|
+
this.log.error(`[ProviderRegistry] Could not load provider from: ${tmpSource}`);
|
|
232
238
|
return false;
|
|
233
239
|
}
|
|
234
240
|
|
|
@@ -255,7 +261,7 @@ class UltravisorBeaconProviderRegistry
|
|
|
255
261
|
}
|
|
256
262
|
else
|
|
257
263
|
{
|
|
258
|
-
|
|
264
|
+
this.log.error(`[ProviderRegistry] Invalid provider export from "${tmpSource}": ` +
|
|
259
265
|
`must be a class, factory function, or object with execute().`);
|
|
260
266
|
return false;
|
|
261
267
|
}
|
|
@@ -254,6 +254,7 @@ class UltravisorBeaconService extends libFableServiceBase
|
|
|
254
254
|
Contexts: this.options.Contexts || {},
|
|
255
255
|
BindAddresses: this.options.BindAddresses || [],
|
|
256
256
|
Operations: this._Operations.length > 0 ? this._Operations : undefined,
|
|
257
|
+
Log: this.log,
|
|
257
258
|
// Pass empty Providers array — we'll register adapters directly
|
|
258
259
|
Providers: []
|
|
259
260
|
});
|