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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultravisor-beacon",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Ultravisor Beacon: lightweight beacon client and Fable service for remote task execution",
5
5
  "main": "source/Ultravisor-Beacon-Service.cjs",
6
6
  "scripts": {
@@ -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
- console.error('[CapabilityManager] Descriptor must have a Capability name.');
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
- console.warn(`[CapabilityManager] Capability "${pDescriptor.Capability}" has no actions.`);
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
- console.log(`[Beacon] Loaded ${tmpCount} capability provider(s).`);
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
- console.log(`[Beacon] Starting "${this._Config.Name}"...`);
100
- console.log(`[Beacon] Server: ${this._Config.ServerURL}`);
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
- console.error(`[Beacon] Provider initialization failed: ${pInitError.message}`);
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
- console.log(`[Beacon] Capabilities: ${tmpCapabilities.join(', ')}`);
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
- console.error(`[Beacon] Authentication failed: ${pAuthError.message}`);
132
+ this.log.error(`[Beacon] Authentication failed: ${pAuthError.message}`);
120
133
  return fCallback(pAuthError);
121
134
  }
122
135
 
123
- console.log(`[Beacon] Authenticated successfully.`);
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
- console.log(`[Beacon] WebSocket unavailable (${pWSError.message}), using HTTP polling.`);
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
- console.error(`[Beacon] Registration failed: ${pError.message}`);
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
- console.log(`[Beacon] Registered as ${this._BeaconID}`);
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
- console.log(`[Beacon] Stopping...`);
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
- console.warn(`[Beacon] Provider shutdown warning: ${pShutdownError.message}`);
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
- console.warn(`[Beacon] Deregistration warning: ${pError.message}`);
251
+ this.log.warn(`[Beacon] Deregistration warning: ${pError.message}`);
239
252
  }
240
- console.log(`[Beacon] Stopped.`);
253
+ this.log.info(`[Beacon] Stopped.`);
241
254
  if (fCallback) return fCallback(null);
242
255
  });
243
256
  }
244
257
  else
245
258
  {
246
- console.log(`[Beacon] Stopped.`);
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
- console.log(`[Beacon] Session cookie acquired.`);
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
- console.log(`[Beacon] Reconnecting — re-authenticating...`);
357
+ this.log.info(`[Beacon] Reconnecting — re-authenticating...`);
345
358
 
346
359
  this._authenticate((pAuthError) =>
347
360
  {
348
361
  if (pAuthError)
349
362
  {
350
- console.error(`[Beacon] Re-authentication failed: ${pAuthError.message}`);
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
- console.log(`[Beacon] Re-authenticated, re-registering...`);
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
- console.error(`[Beacon] Re-registration failed: ${pRegError.message}`);
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
- console.log(`[Beacon] Reconnected as ${this._BeaconID}`);
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
- console.log(`[Beacon] Executing work item [${pWorkItem.WorkItemHash}] (${pWorkItem.Capability}/${pWorkItem.Action})`);
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
- console.error(`[Beacon] Execution error for [${pWorkItem.WorkItemHash}]: ${pError.message}`);
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
- console.warn(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed with exit code ${tmpOutputs.ExitCode}`);
525
+ this.log.warn(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed with exit code ${tmpOutputs.ExitCode}`);
513
526
  }
514
527
  else
515
528
  {
516
- console.log(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed successfully.`);
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
- console.log(`[Beacon] Uploading result file ${tmpOutputFilename} (${tmpBuffer.length} bytes) for [${pWorkItem.WorkItemHash}]`);
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
- console.error(`[Beacon] Failed to upload result file: ${pUploadError.message}`);
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
- console.error(`[Beacon] Failed to report completion for [${pWorkItemHash}]: ${pError.message}`);
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
- console.error(`[Beacon] Failed to report error for [${pWorkItemHash}]: ${pError.message}`);
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
- console.warn(`[Beacon] Failed to report progress for [${pWorkItemHash}]: ${pError.message}`);
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
- console.warn(`[Beacon] Heartbeat failed: ${pError.message}`);
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
- console.log(`[Beacon] WebSocket connected to ${tmpWSURL}`);
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
- console.error(`[Beacon] WebSocket error: ${pError.message}`);
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
- console.log(`[Beacon] WebSocket connection closed.`);
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._wsReconnect();
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
- console.log(`[Beacon] Registered via WebSocket as ${this._BeaconID}`);
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
- console.log(`[Beacon] At max concurrent capacity, ignoring pushed work item.`);
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
- console.log(`[Beacon] Deregistered by server.`);
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
- console.log(`[Beacon] WebSocket disconnected re-authenticating...`);
995
+ this.log.info(`[Beacon] Reconnectingattempt ${this._ReconnectAttempts}...`);
955
996
 
956
997
  this._authenticate((pAuthError) =>
957
998
  {
958
999
  if (pAuthError)
959
1000
  {
960
- console.error(`[Beacon] Re-authentication failed: ${pAuthError.message}`);
1001
+ this.log.error(`[Beacon] Re-authentication failed: ${pAuthError.message}`);
961
1002
  this._Authenticating = false;
962
- setTimeout(() => { this._wsReconnect(); }, this._Config.ReconnectIntervalMs);
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
- console.log(`[Beacon] WebSocket reconnection failed, falling back to HTTP polling.`);
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
- console.error(`[Beacon] HTTP fallback failed: ${pHTTPError.message}`);
981
- setTimeout(() => { this._wsReconnect(); }, this._Config.ReconnectIntervalMs);
1021
+ this.log.error(`[Beacon] HTTP fallback failed: ${pHTTPError.message}`);
1022
+ this._scheduleReconnect();
982
1023
  return;
983
1024
  }
984
- console.log(`[Beacon] Reconnected via HTTP polling as ${pHTTPBeacon.BeaconID}`);
1025
+ this._ReconnectAttempts = 0;
1026
+ this.log.info(`[Beacon] Reconnected via HTTP polling as ${pHTTPBeacon.BeaconID}`);
985
1027
  });
986
1028
  return;
987
1029
  }
988
- console.log(`[Beacon] WebSocket reconnected as ${pBeacon.BeaconID}`);
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._ProviderRegistry = new libBeaconProviderRegistry();
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
- console.warn(`[Beacon Executor] Could not clean up work directory: ${pError.message}`);
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
- console.warn(`[Beacon Executor] Could not clean up affinity dir [${tmpEntries[i]}]: ${pError.message}`);
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
- console.error('[ProviderRegistry] Provider must have a Capability.');
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
- console.warn(`[ProviderRegistry] Provider "${pProvider.Name}" declares no actions.`);
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
- console.log(`[ProviderRegistry] Registered "${pProvider.Name}" → ` +
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
- console.error('[ProviderRegistry] Provider descriptor must have a Source.');
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
- console.error(`[ProviderRegistry] Failed to load provider from "${tmpSource}": ${pError.message}`);
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
- console.error(`[ProviderRegistry] Could not load provider from: ${tmpSource}`);
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
- console.error(`[ProviderRegistry] Invalid provider export from "${tmpSource}": ` +
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
  });