ultravisor-beacon 0.0.1
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 +30 -0
- package/source/Ultravisor-Beacon-CLI.cjs +143 -0
- package/source/Ultravisor-Beacon-CapabilityAdapter.cjs +116 -0
- package/source/Ultravisor-Beacon-CapabilityManager.cjs +132 -0
- package/source/Ultravisor-Beacon-CapabilityProvider.cjs +129 -0
- package/source/Ultravisor-Beacon-Client.cjs +568 -0
- package/source/Ultravisor-Beacon-ConnectivityHTTP.cjs +52 -0
- package/source/Ultravisor-Beacon-Executor.cjs +500 -0
- package/source/Ultravisor-Beacon-ProviderRegistry.cjs +330 -0
- package/source/Ultravisor-Beacon-Service.cjs +288 -0
- package/source/providers/Ultravisor-Beacon-Provider-FileSystem.cjs +331 -0
- package/source/providers/Ultravisor-Beacon-Provider-LLM.cjs +966 -0
- package/source/providers/Ultravisor-Beacon-Provider-Shell.cjs +95 -0
- package/test/Ultravisor-Beacon-Service_tests.js +608 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Provider Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages loaded capability providers, routes work items to the
|
|
5
|
+
* correct provider based on Capability:Action, and aggregates the
|
|
6
|
+
* capabilities list for beacon registration.
|
|
7
|
+
*
|
|
8
|
+
* Provider loading supports three sources:
|
|
9
|
+
* - Built-in name ('Shell', 'FileSystem') → resolves to ./providers/
|
|
10
|
+
* - Local file path ('./my-provider.cjs' or absolute) → require(resolved)
|
|
11
|
+
* - npm package name ('ultravisor-provider-ml') → require(name)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const libPath = require('path');
|
|
15
|
+
|
|
16
|
+
class UltravisorBeaconProviderRegistry
|
|
17
|
+
{
|
|
18
|
+
constructor()
|
|
19
|
+
{
|
|
20
|
+
// Map of 'Capability:Action' → { provider, actionDef }
|
|
21
|
+
this._ActionHandlers = {};
|
|
22
|
+
|
|
23
|
+
// Map of 'Capability' → { provider, defaultAction }
|
|
24
|
+
this._DefaultHandlers = {};
|
|
25
|
+
|
|
26
|
+
// All loaded providers by Name
|
|
27
|
+
this._Providers = {};
|
|
28
|
+
|
|
29
|
+
// Aggregate capabilities list (string[])
|
|
30
|
+
this._Capabilities = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Register a provider instance.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} pProvider - Provider instance (extends CapabilityProvider or duck-types it)
|
|
37
|
+
* @returns {boolean} true if registered successfully
|
|
38
|
+
*/
|
|
39
|
+
registerProvider(pProvider)
|
|
40
|
+
{
|
|
41
|
+
if (!pProvider || !pProvider.Capability)
|
|
42
|
+
{
|
|
43
|
+
console.error('[ProviderRegistry] Provider must have a Capability.');
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let tmpActions = pProvider.actions || {};
|
|
48
|
+
let tmpActionNames = Object.keys(tmpActions);
|
|
49
|
+
|
|
50
|
+
if (tmpActionNames.length === 0)
|
|
51
|
+
{
|
|
52
|
+
console.warn(`[ProviderRegistry] Provider "${pProvider.Name}" declares no actions.`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Index each action by composite key
|
|
56
|
+
for (let i = 0; i < tmpActionNames.length; i++)
|
|
57
|
+
{
|
|
58
|
+
let tmpKey = pProvider.Capability + ':' + tmpActionNames[i];
|
|
59
|
+
this._ActionHandlers[tmpKey] = {
|
|
60
|
+
provider: pProvider,
|
|
61
|
+
actionDef: tmpActions[tmpActionNames[i]]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// First declared action is the default for capability-only routing
|
|
66
|
+
if (tmpActionNames.length > 0)
|
|
67
|
+
{
|
|
68
|
+
this._DefaultHandlers[pProvider.Capability] = {
|
|
69
|
+
provider: pProvider,
|
|
70
|
+
defaultAction: tmpActionNames[0]
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Update aggregate capabilities list
|
|
75
|
+
let tmpCapabilities = pProvider.getCapabilities();
|
|
76
|
+
for (let i = 0; i < tmpCapabilities.length; i++)
|
|
77
|
+
{
|
|
78
|
+
if (this._Capabilities.indexOf(tmpCapabilities[i]) === -1)
|
|
79
|
+
{
|
|
80
|
+
this._Capabilities.push(tmpCapabilities[i]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this._Providers[pProvider.Name] = pProvider;
|
|
85
|
+
|
|
86
|
+
console.log(`[ProviderRegistry] Registered "${pProvider.Name}" → ` +
|
|
87
|
+
`${pProvider.Capability} [${tmpActionNames.join(', ')}]`);
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve a Capability+Action to a provider and action name.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} pCapability - The capability to match
|
|
96
|
+
* @param {string} [pAction] - Optional action within the capability
|
|
97
|
+
* @returns {{ provider: object, action: string }|null}
|
|
98
|
+
*/
|
|
99
|
+
resolve(pCapability, pAction)
|
|
100
|
+
{
|
|
101
|
+
// Try exact Capability:Action match first
|
|
102
|
+
if (pAction)
|
|
103
|
+
{
|
|
104
|
+
let tmpKey = pCapability + ':' + pAction;
|
|
105
|
+
let tmpHandler = this._ActionHandlers[tmpKey];
|
|
106
|
+
if (tmpHandler)
|
|
107
|
+
{
|
|
108
|
+
return { provider: tmpHandler.provider, action: pAction };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fall back to default action for the capability
|
|
113
|
+
let tmpDefault = this._DefaultHandlers[pCapability];
|
|
114
|
+
if (tmpDefault)
|
|
115
|
+
{
|
|
116
|
+
return { provider: tmpDefault.provider, action: tmpDefault.defaultAction };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the aggregate capabilities list for beacon registration.
|
|
124
|
+
*
|
|
125
|
+
* @returns {string[]}
|
|
126
|
+
*/
|
|
127
|
+
getCapabilities()
|
|
128
|
+
{
|
|
129
|
+
return this._Capabilities.slice();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get all loaded providers.
|
|
134
|
+
*
|
|
135
|
+
* @returns {object} Map of provider Name → instance
|
|
136
|
+
*/
|
|
137
|
+
getProviders()
|
|
138
|
+
{
|
|
139
|
+
return this._Providers;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Load a provider from a source descriptor.
|
|
144
|
+
*
|
|
145
|
+
* @param {object} pDescriptor - { Source, Config }
|
|
146
|
+
* Source: 'Shell' (built-in), './my-provider.cjs' (local), 'npm-pkg' (npm)
|
|
147
|
+
* Config: per-provider config object (passed to constructor)
|
|
148
|
+
* @returns {boolean} true if loaded and registered successfully
|
|
149
|
+
*/
|
|
150
|
+
loadProvider(pDescriptor)
|
|
151
|
+
{
|
|
152
|
+
let tmpSource = (pDescriptor && pDescriptor.Source) ? pDescriptor.Source : '';
|
|
153
|
+
let tmpConfig = (pDescriptor && pDescriptor.Config) ? pDescriptor.Config : {};
|
|
154
|
+
|
|
155
|
+
if (!tmpSource)
|
|
156
|
+
{
|
|
157
|
+
console.error('[ProviderRegistry] Provider descriptor must have a Source.');
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let tmpProviderModule = null;
|
|
162
|
+
|
|
163
|
+
// Built-in providers
|
|
164
|
+
let tmpBuiltIns = {
|
|
165
|
+
'Shell': libPath.join(__dirname, 'providers', 'Ultravisor-Beacon-Provider-Shell.cjs'),
|
|
166
|
+
'FileSystem': libPath.join(__dirname, 'providers', 'Ultravisor-Beacon-Provider-FileSystem.cjs'),
|
|
167
|
+
'LLM': libPath.join(__dirname, 'providers', 'Ultravisor-Beacon-Provider-LLM.cjs')
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
try
|
|
171
|
+
{
|
|
172
|
+
if (tmpBuiltIns[tmpSource])
|
|
173
|
+
{
|
|
174
|
+
tmpProviderModule = require(tmpBuiltIns[tmpSource]);
|
|
175
|
+
}
|
|
176
|
+
else if (tmpSource.startsWith('.') || tmpSource.startsWith('/'))
|
|
177
|
+
{
|
|
178
|
+
// Local file path — resolve relative to cwd
|
|
179
|
+
tmpProviderModule = require(libPath.resolve(tmpSource));
|
|
180
|
+
}
|
|
181
|
+
else
|
|
182
|
+
{
|
|
183
|
+
// npm package
|
|
184
|
+
tmpProviderModule = require(tmpSource);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (pError)
|
|
188
|
+
{
|
|
189
|
+
console.error(`[ProviderRegistry] Failed to load provider from "${tmpSource}": ${pError.message}`);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!tmpProviderModule)
|
|
194
|
+
{
|
|
195
|
+
console.error(`[ProviderRegistry] Could not load provider from: ${tmpSource}`);
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Support class exports, factory functions, and pre-instantiated singletons
|
|
200
|
+
let tmpProvider;
|
|
201
|
+
|
|
202
|
+
if (typeof tmpProviderModule === 'function' &&
|
|
203
|
+
tmpProviderModule.prototype &&
|
|
204
|
+
typeof tmpProviderModule.prototype.execute === 'function')
|
|
205
|
+
{
|
|
206
|
+
// Class with execute on prototype — instantiate it
|
|
207
|
+
tmpProvider = new tmpProviderModule(tmpConfig);
|
|
208
|
+
}
|
|
209
|
+
else if (typeof tmpProviderModule === 'function')
|
|
210
|
+
{
|
|
211
|
+
// Factory function
|
|
212
|
+
tmpProvider = tmpProviderModule(tmpConfig);
|
|
213
|
+
}
|
|
214
|
+
else if (typeof tmpProviderModule === 'object' &&
|
|
215
|
+
typeof tmpProviderModule.execute === 'function')
|
|
216
|
+
{
|
|
217
|
+
// Pre-instantiated singleton
|
|
218
|
+
tmpProvider = tmpProviderModule;
|
|
219
|
+
}
|
|
220
|
+
else
|
|
221
|
+
{
|
|
222
|
+
console.error(`[ProviderRegistry] Invalid provider export from "${tmpSource}": ` +
|
|
223
|
+
`must be a class, factory function, or object with execute().`);
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return this.registerProvider(tmpProvider);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Load all providers from a config array.
|
|
232
|
+
*
|
|
233
|
+
* @param {Array<{ Source: string, Config?: object }>} pDescriptors
|
|
234
|
+
* @returns {number} count of successfully loaded providers
|
|
235
|
+
*/
|
|
236
|
+
loadProviders(pDescriptors)
|
|
237
|
+
{
|
|
238
|
+
if (!Array.isArray(pDescriptors))
|
|
239
|
+
{
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let tmpCount = 0;
|
|
244
|
+
|
|
245
|
+
for (let i = 0; i < pDescriptors.length; i++)
|
|
246
|
+
{
|
|
247
|
+
if (this.loadProvider(pDescriptors[i]))
|
|
248
|
+
{
|
|
249
|
+
tmpCount++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return tmpCount;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Initialize all loaded providers sequentially.
|
|
258
|
+
* Called before the beacon starts polling.
|
|
259
|
+
*
|
|
260
|
+
* @param {function} fCallback - function(pError)
|
|
261
|
+
*/
|
|
262
|
+
initializeAll(fCallback)
|
|
263
|
+
{
|
|
264
|
+
let tmpProviderNames = Object.keys(this._Providers);
|
|
265
|
+
let tmpIndex = 0;
|
|
266
|
+
|
|
267
|
+
let fNext = (pError) =>
|
|
268
|
+
{
|
|
269
|
+
if (pError)
|
|
270
|
+
{
|
|
271
|
+
return fCallback(pError);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (tmpIndex >= tmpProviderNames.length)
|
|
275
|
+
{
|
|
276
|
+
return fCallback(null);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
let tmpProviderName = tmpProviderNames[tmpIndex++];
|
|
280
|
+
let tmpProvider = this._Providers[tmpProviderName];
|
|
281
|
+
|
|
282
|
+
if (typeof tmpProvider.initialize === 'function')
|
|
283
|
+
{
|
|
284
|
+
tmpProvider.initialize(fNext);
|
|
285
|
+
}
|
|
286
|
+
else
|
|
287
|
+
{
|
|
288
|
+
fNext(null);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
fNext(null);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Shut down all loaded providers sequentially.
|
|
297
|
+
* Called when the beacon is stopping.
|
|
298
|
+
*
|
|
299
|
+
* @param {function} fCallback - function(pError)
|
|
300
|
+
*/
|
|
301
|
+
shutdownAll(fCallback)
|
|
302
|
+
{
|
|
303
|
+
let tmpProviderNames = Object.keys(this._Providers);
|
|
304
|
+
let tmpIndex = 0;
|
|
305
|
+
|
|
306
|
+
let fNext = (pError) =>
|
|
307
|
+
{
|
|
308
|
+
if (tmpIndex >= tmpProviderNames.length)
|
|
309
|
+
{
|
|
310
|
+
return fCallback(pError || null);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let tmpProviderName = tmpProviderNames[tmpIndex++];
|
|
314
|
+
let tmpProvider = this._Providers[tmpProviderName];
|
|
315
|
+
|
|
316
|
+
if (typeof tmpProvider.shutdown === 'function')
|
|
317
|
+
{
|
|
318
|
+
tmpProvider.shutdown(fNext);
|
|
319
|
+
}
|
|
320
|
+
else
|
|
321
|
+
{
|
|
322
|
+
fNext(null);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
fNext(null);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports = UltravisorBeaconProviderRegistry;
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Service
|
|
3
|
+
*
|
|
4
|
+
* A Fable service that turns any Fable/Pict application into an
|
|
5
|
+
* Ultravisor beacon. Host applications register capabilities
|
|
6
|
+
* (with action handlers), then call enable() to connect to an
|
|
7
|
+
* Ultravisor server and begin accepting remote work items.
|
|
8
|
+
*
|
|
9
|
+
* Beacon mode is opt-in and disabled by default.
|
|
10
|
+
*
|
|
11
|
+
* This service composes three internal components:
|
|
12
|
+
* - CapabilityManager: stores registered capabilities
|
|
13
|
+
* - ConnectivityHTTP: HTTP transport configuration
|
|
14
|
+
* - BeaconClient (thin client): handles polling, auth, heartbeat
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* let libBeaconService = require('ultravisor-beacon');
|
|
18
|
+
* pFable.addAndInstantiateServiceType('UltravisorBeacon', libBeaconService, {
|
|
19
|
+
* ServerURL: 'http://localhost:54321',
|
|
20
|
+
* Name: 'my-app-beacon'
|
|
21
|
+
* });
|
|
22
|
+
* let tmpBeacon = pFable.services.UltravisorBeacon;
|
|
23
|
+
* tmpBeacon.registerCapability({ Capability: 'MyApp', actions: { ... } });
|
|
24
|
+
* tmpBeacon.enable(function(pError) { ... });
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const libFableServiceBase = require('fable-serviceproviderbase');
|
|
28
|
+
|
|
29
|
+
const libBeaconClient = require('./Ultravisor-Beacon-Client.cjs');
|
|
30
|
+
const libCapabilityManager = require('./Ultravisor-Beacon-CapabilityManager.cjs');
|
|
31
|
+
const libConnectivityHTTP = require('./Ultravisor-Beacon-ConnectivityHTTP.cjs');
|
|
32
|
+
|
|
33
|
+
class UltravisorBeaconService extends libFableServiceBase
|
|
34
|
+
{
|
|
35
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
36
|
+
{
|
|
37
|
+
super(pFable, pOptions, pServiceHash);
|
|
38
|
+
|
|
39
|
+
this.serviceType = 'UltravisorBeacon';
|
|
40
|
+
|
|
41
|
+
// Merge defaults
|
|
42
|
+
this.options = Object.assign({
|
|
43
|
+
Enabled: false,
|
|
44
|
+
ServerURL: 'http://localhost:54321',
|
|
45
|
+
Name: '',
|
|
46
|
+
Password: '',
|
|
47
|
+
MaxConcurrent: 1,
|
|
48
|
+
PollIntervalMs: 5000,
|
|
49
|
+
HeartbeatIntervalMs: 30000,
|
|
50
|
+
StagingPath: '',
|
|
51
|
+
Tags: {},
|
|
52
|
+
Transport: 'HTTP'
|
|
53
|
+
}, this.options || {});
|
|
54
|
+
|
|
55
|
+
// Internal components
|
|
56
|
+
this._CapabilityManager = new libCapabilityManager();
|
|
57
|
+
this._ConnectivityService = new libConnectivityHTTP(this.options);
|
|
58
|
+
this._ThinClient = null;
|
|
59
|
+
this._Enabled = false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ================================================================
|
|
63
|
+
// Public API
|
|
64
|
+
// ================================================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register a capability from the host application.
|
|
68
|
+
*
|
|
69
|
+
* @param {object} pDescriptor - Capability descriptor:
|
|
70
|
+
* {
|
|
71
|
+
* Capability: 'ContentSystem',
|
|
72
|
+
* Name: 'ContentSystemProvider',
|
|
73
|
+
* actions: {
|
|
74
|
+
* 'ReadFile': {
|
|
75
|
+
* Description: 'Read a content file',
|
|
76
|
+
* SettingsSchema: [{ Name: 'FilePath', DataType: 'String', Required: true }],
|
|
77
|
+
* Handler: function(pWorkItem, pContext, fCallback, fReportProgress) { ... }
|
|
78
|
+
* }
|
|
79
|
+
* },
|
|
80
|
+
* initialize: function(fCallback) { ... }, // optional
|
|
81
|
+
* shutdown: function(fCallback) { ... } // optional
|
|
82
|
+
* }
|
|
83
|
+
* @returns {object} this (for chaining)
|
|
84
|
+
*/
|
|
85
|
+
registerCapability(pDescriptor)
|
|
86
|
+
{
|
|
87
|
+
this._CapabilityManager.registerCapability(pDescriptor);
|
|
88
|
+
|
|
89
|
+
if (this.log)
|
|
90
|
+
{
|
|
91
|
+
this.log.info(`UltravisorBeacon: registered capability [${pDescriptor.Capability}]`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Remove a previously registered capability.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} pCapabilityName
|
|
101
|
+
* @returns {object} this (for chaining)
|
|
102
|
+
*/
|
|
103
|
+
removeCapability(pCapabilityName)
|
|
104
|
+
{
|
|
105
|
+
this._CapabilityManager.removeCapability(pCapabilityName);
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get the list of registered capability names.
|
|
111
|
+
*
|
|
112
|
+
* @returns {string[]}
|
|
113
|
+
*/
|
|
114
|
+
getCapabilityNames()
|
|
115
|
+
{
|
|
116
|
+
return this._CapabilityManager.getCapabilityNames();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Enable beacon mode: build providers, create thin client, connect.
|
|
121
|
+
*
|
|
122
|
+
* @param {function} fCallback - function(pError, pBeacon)
|
|
123
|
+
*/
|
|
124
|
+
enable(fCallback)
|
|
125
|
+
{
|
|
126
|
+
if (this._Enabled)
|
|
127
|
+
{
|
|
128
|
+
if (this.log)
|
|
129
|
+
{
|
|
130
|
+
this.log.warn('UltravisorBeacon: already enabled.');
|
|
131
|
+
}
|
|
132
|
+
return fCallback(null);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Determine beacon name
|
|
136
|
+
let tmpName = this.options.Name;
|
|
137
|
+
if (!tmpName && this.fable && this.fable.settings && this.fable.settings.Product)
|
|
138
|
+
{
|
|
139
|
+
tmpName = this.fable.settings.Product;
|
|
140
|
+
}
|
|
141
|
+
if (!tmpName)
|
|
142
|
+
{
|
|
143
|
+
tmpName = 'beacon-worker';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Build adapter instances from registered capabilities
|
|
147
|
+
let tmpAdapters = this._CapabilityManager.buildProviderDescriptors();
|
|
148
|
+
|
|
149
|
+
if (tmpAdapters.length === 0)
|
|
150
|
+
{
|
|
151
|
+
if (this.log)
|
|
152
|
+
{
|
|
153
|
+
this.log.warn('UltravisorBeacon: no capabilities registered. Beacon will have no providers.');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Get transport config
|
|
158
|
+
let tmpTransportConfig = this._ConnectivityService.getTransportConfig();
|
|
159
|
+
|
|
160
|
+
// Build thin client config
|
|
161
|
+
let tmpClientConfig = Object.assign({}, tmpTransportConfig, {
|
|
162
|
+
Name: tmpName,
|
|
163
|
+
MaxConcurrent: this.options.MaxConcurrent || 1,
|
|
164
|
+
StagingPath: this.options.StagingPath || process.cwd(),
|
|
165
|
+
Tags: this.options.Tags || {},
|
|
166
|
+
// Pass empty Providers array — we'll register adapters directly
|
|
167
|
+
Providers: []
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Create thin client
|
|
171
|
+
this._ThinClient = new libBeaconClient(tmpClientConfig);
|
|
172
|
+
|
|
173
|
+
// Register each adapter directly with the thin client's provider registry
|
|
174
|
+
for (let i = 0; i < tmpAdapters.length; i++)
|
|
175
|
+
{
|
|
176
|
+
this._ThinClient._Executor.providerRegistry.registerProvider(tmpAdapters[i]);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (this.log)
|
|
180
|
+
{
|
|
181
|
+
this.log.info(`UltravisorBeacon: enabling beacon "${tmpName}" → ${tmpTransportConfig.ServerURL}`);
|
|
182
|
+
this.log.info(`UltravisorBeacon: capabilities: [${this._CapabilityManager.getCapabilityNames().join(', ')}]`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Start the thin client (authenticate, register, begin polling)
|
|
186
|
+
this._ThinClient.start((pError, pBeacon) =>
|
|
187
|
+
{
|
|
188
|
+
if (pError)
|
|
189
|
+
{
|
|
190
|
+
this._ThinClient = null;
|
|
191
|
+
if (this.log)
|
|
192
|
+
{
|
|
193
|
+
this.log.error(`UltravisorBeacon: enable failed: ${pError.message}`);
|
|
194
|
+
}
|
|
195
|
+
return fCallback(pError);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this._Enabled = true;
|
|
199
|
+
|
|
200
|
+
if (this.log)
|
|
201
|
+
{
|
|
202
|
+
this.log.info(`UltravisorBeacon: enabled as ${pBeacon.BeaconID}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return fCallback(null, pBeacon);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Disable beacon mode: stop polling, deregister, disconnect.
|
|
211
|
+
*
|
|
212
|
+
* @param {function} fCallback - function(pError)
|
|
213
|
+
*/
|
|
214
|
+
disable(fCallback)
|
|
215
|
+
{
|
|
216
|
+
if (!this._Enabled || !this._ThinClient)
|
|
217
|
+
{
|
|
218
|
+
if (this.log)
|
|
219
|
+
{
|
|
220
|
+
this.log.warn('UltravisorBeacon: not enabled.');
|
|
221
|
+
}
|
|
222
|
+
return fCallback(null);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.log)
|
|
226
|
+
{
|
|
227
|
+
this.log.info('UltravisorBeacon: disabling...');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this._ThinClient.stop((pError) =>
|
|
231
|
+
{
|
|
232
|
+
this._Enabled = false;
|
|
233
|
+
this._ThinClient = null;
|
|
234
|
+
|
|
235
|
+
if (pError && this.log)
|
|
236
|
+
{
|
|
237
|
+
this.log.warn(`UltravisorBeacon: disable warning: ${pError.message}`);
|
|
238
|
+
}
|
|
239
|
+
else if (this.log)
|
|
240
|
+
{
|
|
241
|
+
this.log.info('UltravisorBeacon: disabled.');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return fCallback(pError || null);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Check if beacon mode is currently enabled.
|
|
250
|
+
*
|
|
251
|
+
* @returns {boolean}
|
|
252
|
+
*/
|
|
253
|
+
isEnabled()
|
|
254
|
+
{
|
|
255
|
+
return this._Enabled;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the underlying thin client instance (for advanced usage).
|
|
260
|
+
* Returns null if beacon is not enabled.
|
|
261
|
+
*
|
|
262
|
+
* @returns {object|null}
|
|
263
|
+
*/
|
|
264
|
+
getThinClient()
|
|
265
|
+
{
|
|
266
|
+
return this._ThinClient;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get the capability manager instance.
|
|
271
|
+
*
|
|
272
|
+
* @returns {object}
|
|
273
|
+
*/
|
|
274
|
+
getCapabilityManager()
|
|
275
|
+
{
|
|
276
|
+
return this._CapabilityManager;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = UltravisorBeaconService;
|
|
281
|
+
|
|
282
|
+
// Also export sub-components for direct usage
|
|
283
|
+
module.exports.BeaconClient = libBeaconClient;
|
|
284
|
+
module.exports.CapabilityManager = libCapabilityManager;
|
|
285
|
+
module.exports.CapabilityAdapter = require('./Ultravisor-Beacon-CapabilityAdapter.cjs');
|
|
286
|
+
module.exports.CapabilityProvider = require('./Ultravisor-Beacon-CapabilityProvider.cjs');
|
|
287
|
+
module.exports.ProviderRegistry = require('./Ultravisor-Beacon-ProviderRegistry.cjs');
|
|
288
|
+
module.exports.ConnectivityHTTP = libConnectivityHTTP;
|