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
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ultravisor-beacon",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Ultravisor Beacon: lightweight beacon client and Fable service for remote task execution",
|
|
5
|
+
"main": "source/Ultravisor-Beacon-Service.cjs",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "npx mocha -u tdd --exit --timeout 10000 ./test/*_tests*",
|
|
8
|
+
"coverage": "npx nyc --reporter=lcov --reporter=text-summary npx mocha -- -u tdd --exit --timeout 10000 ./test/*_tests*"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/stevenvelozo/ultravisor-beacon.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ultravisor",
|
|
16
|
+
"beacon",
|
|
17
|
+
"remote",
|
|
18
|
+
"task",
|
|
19
|
+
"orchestration",
|
|
20
|
+
"fable"
|
|
21
|
+
],
|
|
22
|
+
"author": "Steven Velozo <steven@velozo.com>",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"fable-serviceproviderbase": "^3.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"mocha": "^10.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ultravisor Beacon CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node Ultravisor-Beacon-CLI.cjs --server http://localhost:54321 --name GPU-Worker-1 --capabilities Shell,FileSystem
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* --server URL Ultravisor server URL (default: http://localhost:54321)
|
|
10
|
+
* --name NAME Beacon worker name (default: beacon-worker)
|
|
11
|
+
* --capabilities LIST Comma-separated capabilities (default: Shell)
|
|
12
|
+
* --password PASSWORD Authentication password for server connection
|
|
13
|
+
*
|
|
14
|
+
* For advanced provider configuration, use a .ultravisor-beacon.json file
|
|
15
|
+
* with a "Providers" array instead of --capabilities.
|
|
16
|
+
* --max-concurrent N Max concurrent work items (default: 1)
|
|
17
|
+
* --poll-interval MS Poll interval in ms (default: 5000)
|
|
18
|
+
* --staging-path PATH Local staging directory (default: cwd)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const libPath = require('path');
|
|
22
|
+
const libFS = require('fs');
|
|
23
|
+
|
|
24
|
+
const libBeaconClient = require('./Ultravisor-Beacon-Client.cjs');
|
|
25
|
+
|
|
26
|
+
// Parse command-line arguments
|
|
27
|
+
let tmpConfig = {
|
|
28
|
+
ServerURL: 'http://localhost:54321',
|
|
29
|
+
Name: 'beacon-worker',
|
|
30
|
+
Capabilities: ['Shell'],
|
|
31
|
+
MaxConcurrent: 1,
|
|
32
|
+
PollIntervalMs: 5000,
|
|
33
|
+
HeartbeatIntervalMs: 30000,
|
|
34
|
+
StagingPath: process.cwd(),
|
|
35
|
+
Password: '',
|
|
36
|
+
Tags: {}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Check for config file
|
|
40
|
+
let tmpConfigFilePath = libPath.resolve(process.cwd(), '.ultravisor-beacon.json');
|
|
41
|
+
if (libFS.existsSync(tmpConfigFilePath))
|
|
42
|
+
{
|
|
43
|
+
try
|
|
44
|
+
{
|
|
45
|
+
let tmpFileConfig = JSON.parse(libFS.readFileSync(tmpConfigFilePath, 'utf8'));
|
|
46
|
+
tmpConfig = Object.assign(tmpConfig, tmpFileConfig);
|
|
47
|
+
console.log(`[Beacon CLI] Loaded config from ${tmpConfigFilePath}`);
|
|
48
|
+
}
|
|
49
|
+
catch (pError)
|
|
50
|
+
{
|
|
51
|
+
console.warn(`[Beacon CLI] Warning: could not parse ${tmpConfigFilePath}: ${pError.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Parse CLI arguments (override config file)
|
|
56
|
+
for (let i = 2; i < process.argv.length; i++)
|
|
57
|
+
{
|
|
58
|
+
switch (process.argv[i])
|
|
59
|
+
{
|
|
60
|
+
case '--server':
|
|
61
|
+
tmpConfig.ServerURL = process.argv[++i] || tmpConfig.ServerURL;
|
|
62
|
+
break;
|
|
63
|
+
case '--name':
|
|
64
|
+
tmpConfig.Name = process.argv[++i] || tmpConfig.Name;
|
|
65
|
+
break;
|
|
66
|
+
case '--capabilities':
|
|
67
|
+
tmpConfig.Capabilities = (process.argv[++i] || 'Shell').split(',').map(s => s.trim());
|
|
68
|
+
break;
|
|
69
|
+
case '--max-concurrent':
|
|
70
|
+
tmpConfig.MaxConcurrent = parseInt(process.argv[++i]) || 1;
|
|
71
|
+
break;
|
|
72
|
+
case '--poll-interval':
|
|
73
|
+
tmpConfig.PollIntervalMs = parseInt(process.argv[++i]) || 5000;
|
|
74
|
+
break;
|
|
75
|
+
case '--staging-path':
|
|
76
|
+
tmpConfig.StagingPath = process.argv[++i] || process.cwd();
|
|
77
|
+
break;
|
|
78
|
+
case '--password':
|
|
79
|
+
tmpConfig.Password = process.argv[++i] || '';
|
|
80
|
+
break;
|
|
81
|
+
case '--help':
|
|
82
|
+
case '-h':
|
|
83
|
+
console.log('Ultravisor Beacon Worker');
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log('Usage: node Ultravisor-Beacon-CLI.cjs [options]');
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log('Options:');
|
|
88
|
+
console.log(' --server URL Ultravisor server URL (default: http://localhost:54321)');
|
|
89
|
+
console.log(' --name NAME Beacon worker name (default: beacon-worker)');
|
|
90
|
+
console.log(' --capabilities LIST Comma-separated capabilities (default: Shell)');
|
|
91
|
+
console.log(' --max-concurrent N Max concurrent work items (default: 1)');
|
|
92
|
+
console.log(' --poll-interval MS Poll interval in ms (default: 5000)');
|
|
93
|
+
console.log(' --staging-path PATH Local staging directory (default: cwd)');
|
|
94
|
+
console.log(' --password PASSWORD Authentication password for server connection');
|
|
95
|
+
console.log(' --help, -h Show this help');
|
|
96
|
+
console.log('');
|
|
97
|
+
console.log('Provider Configuration:');
|
|
98
|
+
console.log(' For advanced provider configuration, create a .ultravisor-beacon.json');
|
|
99
|
+
console.log(' file with a "Providers" array:');
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(' {');
|
|
102
|
+
console.log(' "Providers": [');
|
|
103
|
+
console.log(' { "Source": "Shell" },');
|
|
104
|
+
console.log(' { "Source": "FileSystem", "Config": { "AllowedPaths": ["/data"] } },');
|
|
105
|
+
console.log(' { "Source": "./my-custom-provider.cjs", "Config": {} }');
|
|
106
|
+
console.log(' ]');
|
|
107
|
+
console.log(' }');
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Create and start the Beacon client
|
|
113
|
+
let tmpClient = new libBeaconClient(tmpConfig);
|
|
114
|
+
|
|
115
|
+
tmpClient.start((pError) =>
|
|
116
|
+
{
|
|
117
|
+
if (pError)
|
|
118
|
+
{
|
|
119
|
+
console.error(`[Beacon CLI] Failed to start: ${pError.message}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(`[Beacon CLI] Beacon is running. Polling every ${tmpConfig.PollIntervalMs}ms.`);
|
|
124
|
+
console.log(`[Beacon CLI] Press Ctrl+C to stop.`);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Handle graceful shutdown
|
|
128
|
+
process.on('SIGINT', () =>
|
|
129
|
+
{
|
|
130
|
+
console.log('\n[Beacon CLI] Shutting down...');
|
|
131
|
+
tmpClient.stop(() =>
|
|
132
|
+
{
|
|
133
|
+
process.exit(0);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
process.on('SIGTERM', () =>
|
|
138
|
+
{
|
|
139
|
+
tmpClient.stop(() =>
|
|
140
|
+
{
|
|
141
|
+
process.exit(0);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Capability Adapter
|
|
3
|
+
*
|
|
4
|
+
* Bridges Fable-world capability descriptors to the thin client's
|
|
5
|
+
* CapabilityProvider interface. One adapter instance is created per
|
|
6
|
+
* capability registered with the Beacon Service.
|
|
7
|
+
*
|
|
8
|
+
* The adapter extends the thin-client base class so it can be loaded
|
|
9
|
+
* by the ProviderRegistry exactly like a built-in provider (Shell,
|
|
10
|
+
* FileSystem, etc.).
|
|
11
|
+
*
|
|
12
|
+
* Lifecycle:
|
|
13
|
+
* 1. Constructed by CapabilityManager with a capability descriptor
|
|
14
|
+
* 2. Registered with the thin client's ProviderRegistry
|
|
15
|
+
* 3. execute() delegates to the descriptor's Handler functions
|
|
16
|
+
* 4. initialize()/shutdown() delegate to descriptor hooks if present
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const libCapabilityProvider = require('./Ultravisor-Beacon-CapabilityProvider.cjs');
|
|
20
|
+
|
|
21
|
+
class UltravisorBeaconCapabilityAdapter extends libCapabilityProvider
|
|
22
|
+
{
|
|
23
|
+
constructor(pDescriptor)
|
|
24
|
+
{
|
|
25
|
+
super(pDescriptor.Config || {});
|
|
26
|
+
|
|
27
|
+
this._Descriptor = pDescriptor;
|
|
28
|
+
|
|
29
|
+
// Set identity from the descriptor
|
|
30
|
+
this.Name = pDescriptor.Name || pDescriptor.Capability || 'AdaptedProvider';
|
|
31
|
+
this.Capability = pDescriptor.Capability || 'Unknown';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Return the actions map from the descriptor, stripping Handler
|
|
36
|
+
* functions (which are internal) and keeping only Description
|
|
37
|
+
* and SettingsSchema for introspection.
|
|
38
|
+
*/
|
|
39
|
+
get actions()
|
|
40
|
+
{
|
|
41
|
+
let tmpDescriptorActions = (this._Descriptor && this._Descriptor.actions) ? this._Descriptor.actions : {};
|
|
42
|
+
let tmpActionNames = Object.keys(tmpDescriptorActions);
|
|
43
|
+
let tmpActions = {};
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < tmpActionNames.length; i++)
|
|
46
|
+
{
|
|
47
|
+
let tmpName = tmpActionNames[i];
|
|
48
|
+
let tmpSrc = tmpDescriptorActions[tmpName];
|
|
49
|
+
|
|
50
|
+
tmpActions[tmpName] = {
|
|
51
|
+
Description: tmpSrc.Description || '',
|
|
52
|
+
SettingsSchema: tmpSrc.SettingsSchema || []
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return tmpActions;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Execute a work item by delegating to the descriptor's Handler.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} pAction - Action name (e.g. 'ReadFile')
|
|
63
|
+
* @param {object} pWorkItem - Full work item from the server
|
|
64
|
+
* @param {object} pContext - Execution context: { StagingPath }
|
|
65
|
+
* @param {function} fCallback - function(pError, pResult)
|
|
66
|
+
* @param {function} [fReportProgress] - Optional progress callback
|
|
67
|
+
*/
|
|
68
|
+
execute(pAction, pWorkItem, pContext, fCallback, fReportProgress)
|
|
69
|
+
{
|
|
70
|
+
let tmpDescriptorActions = (this._Descriptor && this._Descriptor.actions) ? this._Descriptor.actions : {};
|
|
71
|
+
let tmpActionDef = tmpDescriptorActions[pAction];
|
|
72
|
+
|
|
73
|
+
if (!tmpActionDef || typeof tmpActionDef.Handler !== 'function')
|
|
74
|
+
{
|
|
75
|
+
return fCallback(new Error(
|
|
76
|
+
`CapabilityAdapter "${this.Name}" has no Handler for action "${pAction}".`));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try
|
|
80
|
+
{
|
|
81
|
+
tmpActionDef.Handler(pWorkItem, pContext, fCallback, fReportProgress);
|
|
82
|
+
}
|
|
83
|
+
catch (pError)
|
|
84
|
+
{
|
|
85
|
+
return fCallback(pError);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Delegate initialization to the descriptor if present.
|
|
91
|
+
*/
|
|
92
|
+
initialize(fCallback)
|
|
93
|
+
{
|
|
94
|
+
if (this._Descriptor && typeof this._Descriptor.initialize === 'function')
|
|
95
|
+
{
|
|
96
|
+
return this._Descriptor.initialize(fCallback);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return fCallback(null);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Delegate shutdown to the descriptor if present.
|
|
104
|
+
*/
|
|
105
|
+
shutdown(fCallback)
|
|
106
|
+
{
|
|
107
|
+
if (this._Descriptor && typeof this._Descriptor.shutdown === 'function')
|
|
108
|
+
{
|
|
109
|
+
return this._Descriptor.shutdown(fCallback);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return fCallback(null);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = UltravisorBeaconCapabilityAdapter;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Capability Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages capabilities registered by a host application. Each
|
|
5
|
+
* capability descriptor declares a Capability name, actions with
|
|
6
|
+
* Handler functions, and optional lifecycle hooks.
|
|
7
|
+
*
|
|
8
|
+
* When the Beacon Service calls enable(), the CapabilityManager
|
|
9
|
+
* converts all registered descriptors into provider descriptors
|
|
10
|
+
* that the thin client's ProviderRegistry can load.
|
|
11
|
+
*
|
|
12
|
+
* Capability Descriptor shape:
|
|
13
|
+
* {
|
|
14
|
+
* Capability: 'ContentSystem',
|
|
15
|
+
* Name: 'ContentSystemProvider',
|
|
16
|
+
* actions: {
|
|
17
|
+
* 'ReadFile': {
|
|
18
|
+
* Description: 'Read a content file',
|
|
19
|
+
* SettingsSchema: [{ Name: 'FilePath', DataType: 'String', Required: true }],
|
|
20
|
+
* Handler: function(pWorkItem, pContext, fCallback, fReportProgress) { ... }
|
|
21
|
+
* }
|
|
22
|
+
* },
|
|
23
|
+
* initialize: function(fCallback) { ... }, // optional
|
|
24
|
+
* shutdown: function(fCallback) { ... } // optional
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const libCapabilityAdapter = require('./Ultravisor-Beacon-CapabilityAdapter.cjs');
|
|
29
|
+
|
|
30
|
+
class UltravisorBeaconCapabilityManager
|
|
31
|
+
{
|
|
32
|
+
constructor()
|
|
33
|
+
{
|
|
34
|
+
// Map of Capability name -> descriptor
|
|
35
|
+
this._Capabilities = {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register a capability from the host application.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} pDescriptor - Capability descriptor
|
|
42
|
+
* @returns {boolean} true if registered successfully
|
|
43
|
+
*/
|
|
44
|
+
registerCapability(pDescriptor)
|
|
45
|
+
{
|
|
46
|
+
if (!pDescriptor || !pDescriptor.Capability)
|
|
47
|
+
{
|
|
48
|
+
console.error('[CapabilityManager] Descriptor must have a Capability name.');
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!pDescriptor.actions || Object.keys(pDescriptor.actions).length === 0)
|
|
53
|
+
{
|
|
54
|
+
console.warn(`[CapabilityManager] Capability "${pDescriptor.Capability}" has no actions.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this._Capabilities[pDescriptor.Capability] = pDescriptor;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Remove a previously registered capability.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} pCapabilityName - The capability to remove
|
|
65
|
+
* @returns {boolean} true if removed
|
|
66
|
+
*/
|
|
67
|
+
removeCapability(pCapabilityName)
|
|
68
|
+
{
|
|
69
|
+
if (this._Capabilities[pCapabilityName])
|
|
70
|
+
{
|
|
71
|
+
delete this._Capabilities[pCapabilityName];
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get the list of registered capability names.
|
|
80
|
+
*
|
|
81
|
+
* @returns {string[]}
|
|
82
|
+
*/
|
|
83
|
+
getCapabilityNames()
|
|
84
|
+
{
|
|
85
|
+
return Object.keys(this._Capabilities);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get all registered capability descriptors.
|
|
90
|
+
*
|
|
91
|
+
* @returns {object} Map of capability name -> descriptor
|
|
92
|
+
*/
|
|
93
|
+
getCapabilities()
|
|
94
|
+
{
|
|
95
|
+
return this._Capabilities;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Build provider descriptors for the thin client.
|
|
100
|
+
*
|
|
101
|
+
* Creates a CapabilityAdapter instance per registered capability
|
|
102
|
+
* and returns them as provider descriptors compatible with
|
|
103
|
+
* ProviderRegistry.loadProvider().
|
|
104
|
+
*
|
|
105
|
+
* The descriptors use a pre-instantiated object format (the adapter
|
|
106
|
+
* instances already have execute() on them), which ProviderRegistry
|
|
107
|
+
* supports as a direct registration path.
|
|
108
|
+
*
|
|
109
|
+
* @returns {Array<object>} Provider descriptors for the thin client
|
|
110
|
+
*/
|
|
111
|
+
buildProviderDescriptors()
|
|
112
|
+
{
|
|
113
|
+
let tmpDescriptors = [];
|
|
114
|
+
let tmpCapabilityNames = Object.keys(this._Capabilities);
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < tmpCapabilityNames.length; i++)
|
|
117
|
+
{
|
|
118
|
+
let tmpCapName = tmpCapabilityNames[i];
|
|
119
|
+
let tmpCapDescriptor = this._Capabilities[tmpCapName];
|
|
120
|
+
|
|
121
|
+
// Create an adapter instance that bridges this descriptor
|
|
122
|
+
// to the CapabilityProvider interface
|
|
123
|
+
let tmpAdapter = new libCapabilityAdapter(tmpCapDescriptor);
|
|
124
|
+
|
|
125
|
+
tmpDescriptors.push(tmpAdapter);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return tmpDescriptors;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = UltravisorBeaconCapabilityManager;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultravisor Beacon Capability Provider — Base Class
|
|
3
|
+
*
|
|
4
|
+
* Providers extend this class to implement a specific capability
|
|
5
|
+
* (Shell, FileSystem, MediaProcessing, etc.) that the Beacon can
|
|
6
|
+
* advertise and execute work items for.
|
|
7
|
+
*
|
|
8
|
+
* This is a plain JavaScript class — no Fable dependency — keeping
|
|
9
|
+
* the Beacon lightweight and deployable with minimal dependencies.
|
|
10
|
+
*
|
|
11
|
+
* Lifecycle:
|
|
12
|
+
* 1. constructor(pProviderConfig) — receive per-provider config
|
|
13
|
+
* 2. initialize(fCallback) — async init (validate prereqs)
|
|
14
|
+
* 3. execute(...) — called per work item
|
|
15
|
+
* 4. shutdown(fCallback) — cleanup on beacon stop
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
class UltravisorBeaconCapabilityProvider
|
|
19
|
+
{
|
|
20
|
+
constructor(pProviderConfig)
|
|
21
|
+
{
|
|
22
|
+
this._ProviderConfig = pProviderConfig || {};
|
|
23
|
+
|
|
24
|
+
// Subclasses MUST set these
|
|
25
|
+
this.Name = 'BaseProvider';
|
|
26
|
+
this.Capability = 'Unknown';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Return the actions this provider supports.
|
|
31
|
+
*
|
|
32
|
+
* Override in subclasses. Each key is an action name, value is
|
|
33
|
+
* an object with Description and optional SettingsSchema.
|
|
34
|
+
*
|
|
35
|
+
* @returns {object} Map of ActionName → { Description, SettingsSchema? }
|
|
36
|
+
*
|
|
37
|
+
* Example:
|
|
38
|
+
* {
|
|
39
|
+
* 'Execute': { Description: 'Run a shell command.' },
|
|
40
|
+
* 'Script': { Description: 'Run a script file.',
|
|
41
|
+
* SettingsSchema: [{ Name: 'ScriptPath', DataType: 'String', Required: true }] }
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
get actions()
|
|
45
|
+
{
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Return the list of capability strings this provider advertises.
|
|
51
|
+
*
|
|
52
|
+
* Usually just [this.Capability]. Override for multi-capability
|
|
53
|
+
* providers.
|
|
54
|
+
*
|
|
55
|
+
* @returns {string[]}
|
|
56
|
+
*/
|
|
57
|
+
getCapabilities()
|
|
58
|
+
{
|
|
59
|
+
return [this.Capability];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Return a structured description of all supported actions.
|
|
64
|
+
* Used for logging and introspection.
|
|
65
|
+
*
|
|
66
|
+
* @returns {Array<{ Capability: string, Action: string, Description: string }>}
|
|
67
|
+
*/
|
|
68
|
+
describeActions()
|
|
69
|
+
{
|
|
70
|
+
let tmpResult = [];
|
|
71
|
+
let tmpActions = this.actions;
|
|
72
|
+
let tmpActionNames = Object.keys(tmpActions);
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < tmpActionNames.length; i++)
|
|
75
|
+
{
|
|
76
|
+
tmpResult.push({
|
|
77
|
+
Capability: this.Capability,
|
|
78
|
+
Action: tmpActionNames[i],
|
|
79
|
+
Description: tmpActions[tmpActionNames[i]].Description || ''
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return tmpResult;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Execute a work item for the given action.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} pAction - The action to perform (e.g. 'Execute', 'Read')
|
|
90
|
+
* @param {object} pWorkItem - The full work item from the server:
|
|
91
|
+
* { WorkItemHash, Capability, Action, Settings, TimeoutMs, OperationHash }
|
|
92
|
+
* @param {object} pContext - Execution context: { StagingPath }
|
|
93
|
+
* @param {function} fCallback - function(pError, pResult)
|
|
94
|
+
* pResult = { Outputs: { ... }, Log: [...] }
|
|
95
|
+
* @param {function} [fReportProgress] - Optional progress callback:
|
|
96
|
+
* function({ Percent, Message, Step, TotalSteps, Log })
|
|
97
|
+
* All fields optional. Call during long-running operations.
|
|
98
|
+
*/
|
|
99
|
+
execute(pAction, pWorkItem, pContext, fCallback, fReportProgress)
|
|
100
|
+
{
|
|
101
|
+
return fCallback(new Error(
|
|
102
|
+
`Provider "${this.Name}" has not implemented execute() for action "${pAction}".`));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Optional lifecycle hook: called after provider is loaded,
|
|
107
|
+
* before the beacon starts polling. Use for async initialization
|
|
108
|
+
* (e.g. verifying that ffmpeg exists, connecting to a local API).
|
|
109
|
+
*
|
|
110
|
+
* @param {function} fCallback - function(pError)
|
|
111
|
+
*/
|
|
112
|
+
initialize(fCallback)
|
|
113
|
+
{
|
|
114
|
+
return fCallback(null);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Optional lifecycle hook: called when the beacon is shutting down.
|
|
119
|
+
* Use for cleanup (e.g. closing connections, flushing buffers).
|
|
120
|
+
*
|
|
121
|
+
* @param {function} fCallback - function(pError)
|
|
122
|
+
*/
|
|
123
|
+
shutdown(fCallback)
|
|
124
|
+
{
|
|
125
|
+
return fCallback(null);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = UltravisorBeaconCapabilityProvider;
|