ultravisor 1.0.3 → 1.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.
Files changed (248) hide show
  1. package/.claude/launch.json +11 -0
  2. package/.claude/ultravisor-dev-config.json +3 -0
  3. package/card-audit.json +1545 -0
  4. package/docs/_sidebar.md +17 -0
  5. package/docs/css/docuserve.css +73 -0
  6. package/docs/features/beacon-authentication.md +209 -0
  7. package/docs/features/beacon-providers.md +1299 -0
  8. package/docs/features/beacons.md +530 -0
  9. package/docs/features/capabilities.md +193 -0
  10. package/docs/features/llm-model-setup.md +505 -0
  11. package/docs/features/llm.md +262 -0
  12. package/docs/features/tasks-content-system.md +110 -0
  13. package/docs/features/tasks-data-transform.md +233 -0
  14. package/docs/features/tasks-extension.md +36 -0
  15. package/docs/features/tasks-file-system.md +203 -0
  16. package/docs/features/tasks-flow-control.md +125 -0
  17. package/docs/features/tasks-http-client.md +119 -0
  18. package/docs/features/tasks-llm.md +118 -0
  19. package/docs/features/tasks-meadow-api.md +163 -0
  20. package/docs/features/tasks-user-interaction.md +49 -0
  21. package/docs/features/tasks.md +14 -0
  22. package/docs/index.html +1 -1
  23. package/docs/retold-catalog.json +1 -1
  24. package/docs/retold-keyword-index.json +17374 -5
  25. package/operation-library/api-data-pipeline.json +143 -0
  26. package/operation-library/beacon-remote-command.json +72 -0
  27. package/operation-library/conditional-file-backup.json +143 -0
  28. package/operation-library/config-processor.json +136 -0
  29. package/operation-library/csv-data-analysis.json +228 -0
  30. package/operation-library/csv-to-json.json +160 -0
  31. package/operation-library/directory-inventory.json +128 -0
  32. package/operation-library/expression-calculator.json +114 -0
  33. package/operation-library/file-search-replace.json +175 -0
  34. package/operation-library/git-status-report.json +368 -0
  35. package/operation-library/health-check-ping.json +329 -0
  36. package/operation-library/json-config-merge.json +313 -0
  37. package/operation-library/llm-embedding-tool-use.json +314 -0
  38. package/operation-library/llm-multi-turn-analysis.json +322 -0
  39. package/operation-library/llm-summarize.json +264 -0
  40. package/operation-library/log-line-counter.json +185 -0
  41. package/operation-library/meadow-crud-lifecycle.json +264 -0
  42. package/operation-library/npm-project-validator.json +193 -0
  43. package/operation-library/rest-api-orchestrator.json +218 -0
  44. package/operation-library/shell-system-info.json +137 -0
  45. package/operation-library/simple-echo.json +95 -0
  46. package/operation-library/template-processor.json +299 -0
  47. package/operation-library/text-sanitizer.json +176 -0
  48. package/package.json +12 -8
  49. package/source/Ultravisor.cjs +30 -3
  50. package/source/beacon/Ultravisor-Beacon-CLI.cjs +143 -0
  51. package/source/beacon/Ultravisor-Beacon-CapabilityProvider.cjs +129 -0
  52. package/source/beacon/Ultravisor-Beacon-Client.cjs +568 -0
  53. package/source/beacon/Ultravisor-Beacon-Executor.cjs +500 -0
  54. package/source/beacon/Ultravisor-Beacon-ProviderRegistry.cjs +330 -0
  55. package/source/beacon/providers/Ultravisor-Beacon-Provider-FileSystem.cjs +331 -0
  56. package/source/beacon/providers/Ultravisor-Beacon-Provider-LLM.cjs +966 -0
  57. package/source/beacon/providers/Ultravisor-Beacon-Provider-Shell.cjs +95 -0
  58. package/source/cli/Ultravisor-CLIProgram.cjs +44 -22
  59. package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
  60. package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
  61. package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
  62. package/source/config/Ultravisor-Default-Command-Configuration.cjs +12 -3
  63. package/source/services/Ultravisor-Beacon-Coordinator.cjs +966 -0
  64. package/source/services/Ultravisor-ExecutionEngine.cjs +1093 -0
  65. package/source/services/Ultravisor-ExecutionManifest.cjs +629 -0
  66. package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
  67. package/source/services/Ultravisor-Hypervisor.cjs +185 -75
  68. package/source/services/Ultravisor-Schedule-Persistence-Base.cjs +45 -0
  69. package/source/services/Ultravisor-StateManager.cjs +253 -0
  70. package/source/services/Ultravisor-TaskTypeRegistry.cjs +213 -0
  71. package/source/services/persistence/Ultravisor-Schedule-Persistence-JSONFile.cjs +140 -0
  72. package/source/services/tasks/Ultravisor-BuiltIn-TaskConfigs.cjs +19 -0
  73. package/source/services/tasks/Ultravisor-TaskHelper-BeaconDispatch.cjs +107 -0
  74. package/source/services/tasks/Ultravisor-TaskType-Base.cjs +125 -0
  75. package/source/services/tasks/content-system/Ultravisor-TaskConfigs-ContentSystem.cjs +90 -0
  76. package/source/services/tasks/content-system/definitions/content-create-folder.json +24 -0
  77. package/source/services/tasks/content-system/definitions/content-list-files.json +27 -0
  78. package/source/services/tasks/content-system/definitions/content-read-file.json +26 -0
  79. package/source/services/tasks/content-system/definitions/content-save-file.json +26 -0
  80. package/source/services/tasks/data-transform/Ultravisor-TaskConfigs-DataTransform.cjs +577 -0
  81. package/source/services/tasks/data-transform/Ultravisor-TaskType-ReplaceString.cjs +80 -0
  82. package/source/services/tasks/data-transform/Ultravisor-TaskType-SetValues.cjs +74 -0
  83. package/source/services/tasks/data-transform/Ultravisor-TaskType-StringAppender.cjs +90 -0
  84. package/source/services/tasks/data-transform/definitions/comprehension-intersect.json +24 -0
  85. package/source/services/tasks/data-transform/definitions/csv-transform.json +24 -0
  86. package/source/services/tasks/data-transform/definitions/expression-solver.json +20 -0
  87. package/source/services/tasks/data-transform/definitions/histogram.json +23 -0
  88. package/source/services/tasks/data-transform/definitions/parse-csv.json +27 -0
  89. package/source/services/tasks/data-transform/definitions/replace-string.json +27 -0
  90. package/source/services/tasks/data-transform/definitions/set-values.json +17 -0
  91. package/source/services/tasks/data-transform/definitions/string-appender.json +22 -0
  92. package/source/services/tasks/data-transform/definitions/template-string.json +20 -0
  93. package/source/services/tasks/extension/Ultravisor-TaskConfigs-Extension.cjs +87 -0
  94. package/source/services/tasks/extension/definitions/beacon-dispatch.json +32 -0
  95. package/source/services/tasks/file-system/Ultravisor-TaskConfigs-FileSystem.cjs +509 -0
  96. package/source/services/tasks/file-system/Ultravisor-TaskType-ReadFile.cjs +96 -0
  97. package/source/services/tasks/file-system/Ultravisor-TaskType-ReadFileBuffered.cjs +123 -0
  98. package/source/services/tasks/file-system/Ultravisor-TaskType-WriteFile.cjs +106 -0
  99. package/source/services/tasks/file-system/definitions/copy-file.json +27 -0
  100. package/source/services/tasks/file-system/definitions/list-files.json +27 -0
  101. package/source/services/tasks/file-system/definitions/read-file-buffered.json +31 -0
  102. package/source/services/tasks/file-system/definitions/read-file.json +26 -0
  103. package/source/services/tasks/file-system/definitions/read-json.json +23 -0
  104. package/source/services/tasks/file-system/definitions/write-file.json +29 -0
  105. package/source/services/tasks/file-system/definitions/write-json.json +30 -0
  106. package/source/services/tasks/flow-control/Ultravisor-TaskConfigs-FlowControl.cjs +353 -0
  107. package/source/services/tasks/flow-control/Ultravisor-TaskType-IfConditional.cjs +125 -0
  108. package/source/services/tasks/flow-control/Ultravisor-TaskType-LaunchOperation.cjs +186 -0
  109. package/source/services/tasks/flow-control/Ultravisor-TaskType-SplitExecute.cjs +164 -0
  110. package/source/services/tasks/flow-control/definitions/if-conditional.json +25 -0
  111. package/source/services/tasks/flow-control/definitions/launch-operation.json +27 -0
  112. package/source/services/tasks/flow-control/definitions/split-execute.json +32 -0
  113. package/source/services/tasks/http-client/Ultravisor-TaskConfigs-HttpClient.cjs +281 -0
  114. package/source/services/tasks/http-client/definitions/get-json.json +26 -0
  115. package/source/services/tasks/http-client/definitions/get-text.json +26 -0
  116. package/source/services/tasks/http-client/definitions/rest-request.json +32 -0
  117. package/source/services/tasks/http-client/definitions/send-json.json +28 -0
  118. package/source/services/tasks/llm/Ultravisor-TaskConfigs-LLM.cjs +605 -0
  119. package/source/services/tasks/llm/definitions/llm-chat-completion.json +46 -0
  120. package/source/services/tasks/llm/definitions/llm-embedding.json +31 -0
  121. package/source/services/tasks/llm/definitions/llm-tool-use.json +42 -0
  122. package/source/services/tasks/meadow-api/Ultravisor-TaskConfigs-MeadowApi.cjs +341 -0
  123. package/source/services/tasks/meadow-api/definitions/meadow-count.json +26 -0
  124. package/source/services/tasks/meadow-api/definitions/meadow-create.json +26 -0
  125. package/source/services/tasks/meadow-api/definitions/meadow-delete.json +23 -0
  126. package/source/services/tasks/meadow-api/definitions/meadow-read.json +26 -0
  127. package/source/services/tasks/meadow-api/definitions/meadow-reads.json +29 -0
  128. package/source/services/tasks/meadow-api/definitions/meadow-update.json +26 -0
  129. package/source/services/tasks/shell/Ultravisor-TaskConfigs-Shell.cjs +63 -0
  130. package/source/services/tasks/shell/definitions/command.json +29 -0
  131. package/source/services/tasks/user-interaction/Ultravisor-TaskConfigs-UserInteraction.cjs +64 -0
  132. package/source/services/tasks/user-interaction/Ultravisor-TaskType-ErrorMessage.cjs +55 -0
  133. package/source/services/tasks/user-interaction/Ultravisor-TaskType-ValueInput.cjs +47 -0
  134. package/source/services/tasks/user-interaction/definitions/error-message.json +18 -0
  135. package/source/services/tasks/user-interaction/definitions/value-input.json +23 -0
  136. package/source/web_server/Ultravisor-API-Server.cjs +849 -124
  137. package/test/Ultravisor_browser_tests.js +2226 -0
  138. package/test/Ultravisor_operation_library_tests.js +894 -0
  139. package/test/Ultravisor_tests.js +4946 -5044
  140. package/test/workflows/test-content-concatenate-all.json +339 -0
  141. package/test/workflows/test-content-full-pipeline.json +159 -0
  142. package/test/workflows/test-content-list-files.json +76 -0
  143. package/test/workflows/test-content-save-and-read.json +106 -0
  144. package/webinterface/css/ultravisor-themes.css +668 -0
  145. package/webinterface/css/ultravisor.css +37 -14
  146. package/webinterface/docs/card-help/beacon-dispatch.md +30 -0
  147. package/webinterface/docs/card-help/command.md +27 -0
  148. package/webinterface/docs/card-help/comprehension-intersect.md +24 -0
  149. package/webinterface/docs/card-help/content-create-folder.md +22 -0
  150. package/webinterface/docs/card-help/content-list-files.md +25 -0
  151. package/webinterface/docs/card-help/content-read-file.md +24 -0
  152. package/webinterface/docs/card-help/content-save-file.md +24 -0
  153. package/webinterface/docs/card-help/copy-file.md +25 -0
  154. package/webinterface/docs/card-help/csv-transform.md +24 -0
  155. package/webinterface/docs/card-help/end.md +11 -0
  156. package/webinterface/docs/card-help/error-message.md +16 -0
  157. package/webinterface/docs/card-help/expression-solver.md +21 -0
  158. package/webinterface/docs/card-help/get-json.md +24 -0
  159. package/webinterface/docs/card-help/get-text.md +24 -0
  160. package/webinterface/docs/card-help/histogram.md +23 -0
  161. package/webinterface/docs/card-help/if-conditional.md +23 -0
  162. package/webinterface/docs/card-help/launch-operation.md +25 -0
  163. package/webinterface/docs/card-help/list-files.md +25 -0
  164. package/webinterface/docs/card-help/llm-chat-completion.md +43 -0
  165. package/webinterface/docs/card-help/llm-embedding.md +24 -0
  166. package/webinterface/docs/card-help/llm-tool-use.md +39 -0
  167. package/webinterface/docs/card-help/meadow-count.md +24 -0
  168. package/webinterface/docs/card-help/meadow-create.md +24 -0
  169. package/webinterface/docs/card-help/meadow-delete.md +19 -0
  170. package/webinterface/docs/card-help/meadow-read.md +24 -0
  171. package/webinterface/docs/card-help/meadow-reads.md +27 -0
  172. package/webinterface/docs/card-help/meadow-update.md +24 -0
  173. package/webinterface/docs/card-help/parse-csv.md +27 -0
  174. package/webinterface/docs/card-help/read-file-buffered.md +29 -0
  175. package/webinterface/docs/card-help/read-file.md +24 -0
  176. package/webinterface/docs/card-help/read-json.md +21 -0
  177. package/webinterface/docs/card-help/replace-string.md +25 -0
  178. package/webinterface/docs/card-help/rest-request.md +30 -0
  179. package/webinterface/docs/card-help/send-json.md +26 -0
  180. package/webinterface/docs/card-help/set-values.md +16 -0
  181. package/webinterface/docs/card-help/split-execute.md +35 -0
  182. package/webinterface/docs/card-help/start.md +11 -0
  183. package/webinterface/docs/card-help/string-appender.md +22 -0
  184. package/webinterface/docs/card-help/template-string.md +21 -0
  185. package/webinterface/docs/card-help/value-input.md +24 -0
  186. package/webinterface/docs/card-help/write-file.md +27 -0
  187. package/webinterface/docs/card-help/write-json.md +28 -0
  188. package/webinterface/html/index.html +5 -0
  189. package/webinterface/package.json +13 -4
  190. package/webinterface/source/Pict-Application-Ultravisor.js +564 -70
  191. package/webinterface/source/Ultravisor-TimingUtils.js +99 -0
  192. package/webinterface/source/card-help-content.js +46 -0
  193. package/webinterface/source/cards/Ultravisor-BuiltIn-CardConfigs.js +174 -0
  194. package/webinterface/source/cards/Ultravisor-CardConfigGenerator.js +431 -0
  195. package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
  196. package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
  197. package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
  198. package/webinterface/source/panels/Ultravisor-CardSettingsPanel.js +902 -0
  199. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +20 -8
  200. package/webinterface/source/views/PictView-Ultravisor-BeaconList.js +817 -0
  201. package/webinterface/source/views/PictView-Ultravisor-BottomBar.js +5 -5
  202. package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +25 -25
  203. package/webinterface/source/views/PictView-Ultravisor-Documentation.js +856 -0
  204. package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +777 -0
  205. package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +321 -58
  206. package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +36 -91
  207. package/webinterface/source/views/PictView-Ultravisor-OperationList.js +388 -16
  208. package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
  209. package/webinterface/source/views/PictView-Ultravisor-Schedule.js +521 -65
  210. package/webinterface/source/views/PictView-Ultravisor-TimingView.js +333 -71
  211. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +257 -21
  212. package/webinterface/theme-sampler.html +645 -0
  213. package/debug/Harness.js +0 -6
  214. package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
  215. package/source/services/Ultravisor-Operation.cjs +0 -200
  216. package/source/services/Ultravisor-Task.cjs +0 -349
  217. package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
  218. package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
  219. package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
  220. package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
  221. package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
  222. package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
  223. package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
  224. package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
  225. package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
  226. package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
  227. package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
  228. package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
  229. package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
  230. package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
  231. package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
  232. package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
  233. package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
  234. package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
  235. package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
  236. package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
  237. package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
  238. package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
  239. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
  240. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
  241. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
  242. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
  243. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
  244. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
  245. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
  246. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
  247. package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
  248. package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
@@ -0,0 +1,568 @@
1
+ /**
2
+ * Ultravisor Beacon Client
3
+ *
4
+ * A lightweight worker node that connects to an Ultravisor server,
5
+ * registers its capabilities, polls for work, executes tasks locally,
6
+ * and reports results back to the orchestrator.
7
+ *
8
+ * Capabilities are provided by pluggable CapabilityProviders loaded
9
+ * via the ProviderRegistry. The beacon advertises the aggregate set
10
+ * of capabilities from all loaded providers.
11
+ *
12
+ * Communication is HTTP-based (transport-agnostic design means this
13
+ * can be swapped for WebSocket, MQTT, etc. in the future).
14
+ */
15
+
16
+ const libHTTP = require('http');
17
+
18
+ const libBeaconExecutor = require('./Ultravisor-Beacon-Executor.cjs');
19
+
20
+ class UltravisorBeaconClient
21
+ {
22
+ constructor(pConfig)
23
+ {
24
+ this._Config = Object.assign({
25
+ ServerURL: 'http://localhost:54321',
26
+ Name: 'beacon-worker',
27
+ Password: '',
28
+ Capabilities: ['Shell'],
29
+ MaxConcurrent: 1,
30
+ PollIntervalMs: 5000,
31
+ HeartbeatIntervalMs: 30000,
32
+ StagingPath: process.cwd(),
33
+ Tags: {}
34
+ }, pConfig || {});
35
+
36
+ this._BeaconID = null;
37
+ this._PollInterval = null;
38
+ this._HeartbeatInterval = null;
39
+ this._Running = false;
40
+ this._ActiveWorkItems = 0;
41
+ this._SessionCookie = null;
42
+ this._Authenticating = false;
43
+
44
+ this._Executor = new libBeaconExecutor({
45
+ StagingPath: this._Config.StagingPath
46
+ });
47
+
48
+ // Load capability providers
49
+ this._loadProviders();
50
+ }
51
+
52
+ // ================================================================
53
+ // Provider Loading
54
+ // ================================================================
55
+
56
+ _loadProviders()
57
+ {
58
+ let tmpProviders = this._Config.Providers;
59
+
60
+ if (!tmpProviders)
61
+ {
62
+ // Backward compatibility: convert Capabilities array to Provider descriptors
63
+ let tmpCapabilities = this._Config.Capabilities || ['Shell'];
64
+ tmpProviders = tmpCapabilities.map(function (pCap)
65
+ {
66
+ return { Source: pCap, Config: {} };
67
+ });
68
+ }
69
+
70
+ let tmpCount = this._Executor.providerRegistry.loadProviders(tmpProviders);
71
+ console.log(`[Beacon] Loaded ${tmpCount} capability provider(s).`);
72
+ }
73
+
74
+ // ================================================================
75
+ // Lifecycle
76
+ // ================================================================
77
+
78
+ /**
79
+ * Start the Beacon client: initialize providers, register, then begin polling.
80
+ */
81
+ start(fCallback)
82
+ {
83
+ console.log(`[Beacon] Starting "${this._Config.Name}"...`);
84
+ console.log(`[Beacon] Server: ${this._Config.ServerURL}`);
85
+
86
+ // Initialize all providers before registering
87
+ this._Executor.providerRegistry.initializeAll((pInitError) =>
88
+ {
89
+ if (pInitError)
90
+ {
91
+ console.error(`[Beacon] Provider initialization failed: ${pInitError.message}`);
92
+ return fCallback(pInitError);
93
+ }
94
+
95
+ let tmpCapabilities = this._Executor.providerRegistry.getCapabilities();
96
+ console.log(`[Beacon] Capabilities: ${tmpCapabilities.join(', ')}`);
97
+
98
+ // Authenticate before registering
99
+ this._authenticate((pAuthError) =>
100
+ {
101
+ if (pAuthError)
102
+ {
103
+ console.error(`[Beacon] Authentication failed: ${pAuthError.message}`);
104
+ return fCallback(pAuthError);
105
+ }
106
+
107
+ console.log(`[Beacon] Authenticated successfully.`);
108
+
109
+ this._register((pError, pBeacon) =>
110
+ {
111
+ if (pError)
112
+ {
113
+ console.error(`[Beacon] Registration failed: ${pError.message}`);
114
+ return fCallback(pError);
115
+ }
116
+
117
+ this._BeaconID = pBeacon.BeaconID;
118
+ this._Running = true;
119
+
120
+ console.log(`[Beacon] Registered as ${this._BeaconID}`);
121
+
122
+ // Start polling for work
123
+ this._PollInterval = setInterval(() =>
124
+ {
125
+ this._poll();
126
+ }, this._Config.PollIntervalMs);
127
+
128
+ // Start heartbeat
129
+ this._HeartbeatInterval = setInterval(() =>
130
+ {
131
+ this._heartbeat();
132
+ }, this._Config.HeartbeatIntervalMs);
133
+
134
+ // Do an immediate poll
135
+ this._poll();
136
+
137
+ return fCallback(null, pBeacon);
138
+ });
139
+ });
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Stop the Beacon client: stop polling, shutdown providers, deregister.
145
+ */
146
+ stop(fCallback)
147
+ {
148
+ console.log(`[Beacon] Stopping...`);
149
+ this._Running = false;
150
+
151
+ if (this._PollInterval)
152
+ {
153
+ clearInterval(this._PollInterval);
154
+ this._PollInterval = null;
155
+ }
156
+
157
+ if (this._HeartbeatInterval)
158
+ {
159
+ clearInterval(this._HeartbeatInterval);
160
+ this._HeartbeatInterval = null;
161
+ }
162
+
163
+ // Clean up affinity staging directories
164
+ this._Executor.cleanupAffinityDirs();
165
+
166
+ // Shutdown providers
167
+ this._Executor.providerRegistry.shutdownAll((pShutdownError) =>
168
+ {
169
+ if (pShutdownError)
170
+ {
171
+ console.warn(`[Beacon] Provider shutdown warning: ${pShutdownError.message}`);
172
+ }
173
+
174
+ if (this._BeaconID)
175
+ {
176
+ this._deregister((pError) =>
177
+ {
178
+ if (pError)
179
+ {
180
+ console.warn(`[Beacon] Deregistration warning: ${pError.message}`);
181
+ }
182
+ console.log(`[Beacon] Stopped.`);
183
+ if (fCallback) return fCallback(null);
184
+ });
185
+ }
186
+ else
187
+ {
188
+ console.log(`[Beacon] Stopped.`);
189
+ if (fCallback) return fCallback(null);
190
+ }
191
+ });
192
+ }
193
+
194
+ // ================================================================
195
+ // Authentication
196
+ // ================================================================
197
+
198
+ _authenticate(fCallback)
199
+ {
200
+ let tmpBody = {
201
+ UserName: this._Config.Name,
202
+ Password: this._Config.Password || ''
203
+ };
204
+
205
+ let tmpBodyString = JSON.stringify(tmpBody);
206
+ let tmpParsedURL = new URL(this._Config.ServerURL);
207
+ let tmpOptions = {
208
+ hostname: tmpParsedURL.hostname,
209
+ port: tmpParsedURL.port || 80,
210
+ path: '/1.0/Authenticate',
211
+ method: 'POST',
212
+ headers: {
213
+ 'Content-Type': 'application/json',
214
+ 'Content-Length': Buffer.byteLength(tmpBodyString)
215
+ }
216
+ };
217
+
218
+ let tmpReq = libHTTP.request(tmpOptions, (pResponse) =>
219
+ {
220
+ let tmpData = '';
221
+ pResponse.on('data', (pChunk) => { tmpData += pChunk; });
222
+ pResponse.on('end', () =>
223
+ {
224
+ if (pResponse.statusCode >= 400)
225
+ {
226
+ return fCallback(new Error(`Authentication failed with HTTP ${pResponse.statusCode}`));
227
+ }
228
+
229
+ // Extract session cookie from Set-Cookie headers
230
+ let tmpSetCookieHeaders = pResponse.headers['set-cookie'];
231
+ if (tmpSetCookieHeaders && tmpSetCookieHeaders.length > 0)
232
+ {
233
+ // Take the name=value portion (before the first semicolon)
234
+ let tmpCookieParts = tmpSetCookieHeaders[0].split(';');
235
+ this._SessionCookie = tmpCookieParts[0].trim();
236
+ console.log(`[Beacon] Session cookie acquired.`);
237
+ }
238
+
239
+ try
240
+ {
241
+ let tmpParsed = JSON.parse(tmpData);
242
+ return fCallback(null, tmpParsed);
243
+ }
244
+ catch (pParseError)
245
+ {
246
+ return fCallback(new Error(`Invalid JSON in auth response: ${tmpData.substring(0, 200)}`));
247
+ }
248
+ });
249
+ });
250
+
251
+ tmpReq.on('error', (pError) =>
252
+ {
253
+ return fCallback(pError);
254
+ });
255
+
256
+ tmpReq.write(tmpBodyString);
257
+ tmpReq.end();
258
+ }
259
+
260
+ // ================================================================
261
+ // Reconnection
262
+ // ================================================================
263
+
264
+ _reconnect()
265
+ {
266
+ if (this._Authenticating)
267
+ {
268
+ return;
269
+ }
270
+ this._Authenticating = true;
271
+
272
+ // Clear existing intervals
273
+ if (this._PollInterval)
274
+ {
275
+ clearInterval(this._PollInterval);
276
+ this._PollInterval = null;
277
+ }
278
+ if (this._HeartbeatInterval)
279
+ {
280
+ clearInterval(this._HeartbeatInterval);
281
+ this._HeartbeatInterval = null;
282
+ }
283
+
284
+ this._SessionCookie = null;
285
+
286
+ console.log(`[Beacon] Reconnecting — re-authenticating...`);
287
+
288
+ this._authenticate((pAuthError) =>
289
+ {
290
+ if (pAuthError)
291
+ {
292
+ console.error(`[Beacon] Re-authentication failed: ${pAuthError.message}`);
293
+ this._Authenticating = false;
294
+ setTimeout(() => { this._reconnect(); }, 10000);
295
+ return;
296
+ }
297
+
298
+ console.log(`[Beacon] Re-authenticated, re-registering...`);
299
+
300
+ this._register((pRegError, pBeacon) =>
301
+ {
302
+ if (pRegError)
303
+ {
304
+ console.error(`[Beacon] Re-registration failed: ${pRegError.message}`);
305
+ this._Authenticating = false;
306
+ setTimeout(() => { this._reconnect(); }, 10000);
307
+ return;
308
+ }
309
+
310
+ this._BeaconID = pBeacon.BeaconID;
311
+ this._Authenticating = false;
312
+
313
+ console.log(`[Beacon] Reconnected as ${this._BeaconID}`);
314
+
315
+ // Restart polling
316
+ this._PollInterval = setInterval(() =>
317
+ {
318
+ this._poll();
319
+ }, this._Config.PollIntervalMs);
320
+
321
+ // Restart heartbeat
322
+ this._HeartbeatInterval = setInterval(() =>
323
+ {
324
+ this._heartbeat();
325
+ }, this._Config.HeartbeatIntervalMs);
326
+
327
+ // Immediate poll
328
+ this._poll();
329
+ });
330
+ });
331
+ }
332
+
333
+ // ================================================================
334
+ // Registration
335
+ // ================================================================
336
+
337
+ _register(fCallback)
338
+ {
339
+ let tmpBody = {
340
+ Name: this._Config.Name,
341
+ Capabilities: this._Executor.providerRegistry.getCapabilities(),
342
+ MaxConcurrent: this._Config.MaxConcurrent,
343
+ Tags: this._Config.Tags
344
+ };
345
+
346
+ this._httpRequest('POST', '/Beacon/Register', tmpBody, fCallback);
347
+ }
348
+
349
+ _deregister(fCallback)
350
+ {
351
+ this._httpRequest('DELETE', `/Beacon/${this._BeaconID}`, null, fCallback);
352
+ }
353
+
354
+ // ================================================================
355
+ // Polling
356
+ // ================================================================
357
+
358
+ _poll()
359
+ {
360
+ if (!this._Running || !this._BeaconID)
361
+ {
362
+ return;
363
+ }
364
+
365
+ if (this._ActiveWorkItems >= this._Config.MaxConcurrent)
366
+ {
367
+ return;
368
+ }
369
+
370
+ this._httpRequest('POST', '/Beacon/Work/Poll', { BeaconID: this._BeaconID },
371
+ (pError, pResponse) =>
372
+ {
373
+ if (pError)
374
+ {
375
+ // Silent on poll errors — just retry next interval
376
+ return;
377
+ }
378
+
379
+ if (!pResponse || !pResponse.WorkItem)
380
+ {
381
+ // No work available
382
+ return;
383
+ }
384
+
385
+ // Execute the work item
386
+ this._executeWorkItem(pResponse.WorkItem);
387
+ });
388
+ }
389
+
390
+ // ================================================================
391
+ // Work Execution
392
+ // ================================================================
393
+
394
+ _executeWorkItem(pWorkItem)
395
+ {
396
+ this._ActiveWorkItems++;
397
+ console.log(`[Beacon] Executing work item [${pWorkItem.WorkItemHash}] (${pWorkItem.Capability}/${pWorkItem.Action})`);
398
+
399
+ // Create a progress callback that sends updates to the server
400
+ let tmpWorkItemHash = pWorkItem.WorkItemHash;
401
+ let fReportProgress = (pProgressData) =>
402
+ {
403
+ this._reportProgress(tmpWorkItemHash, pProgressData);
404
+ };
405
+
406
+ this._Executor.execute(pWorkItem, (pError, pResult) =>
407
+ {
408
+ this._ActiveWorkItems--;
409
+
410
+ if (pError)
411
+ {
412
+ console.error(`[Beacon] Execution error for [${pWorkItem.WorkItemHash}]: ${pError.message}`);
413
+ this._reportError(pWorkItem.WorkItemHash, pError.message, []);
414
+ return;
415
+ }
416
+
417
+ // Check if the result indicates an error (non-zero exit code)
418
+ let tmpOutputs = pResult.Outputs || {};
419
+ if (tmpOutputs.ExitCode && tmpOutputs.ExitCode !== 0)
420
+ {
421
+ console.warn(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed with exit code ${tmpOutputs.ExitCode}`);
422
+ }
423
+ else
424
+ {
425
+ console.log(`[Beacon] Work item [${pWorkItem.WorkItemHash}] completed successfully.`);
426
+ }
427
+
428
+ this._reportComplete(pWorkItem.WorkItemHash, tmpOutputs, pResult.Log || []);
429
+ }, fReportProgress);
430
+ }
431
+
432
+ // ================================================================
433
+ // Reporting
434
+ // ================================================================
435
+
436
+ _reportComplete(pWorkItemHash, pOutputs, pLog)
437
+ {
438
+ this._httpRequest('POST', `/Beacon/Work/${pWorkItemHash}/Complete`,
439
+ { Outputs: pOutputs, Log: pLog },
440
+ (pError) =>
441
+ {
442
+ if (pError)
443
+ {
444
+ console.error(`[Beacon] Failed to report completion for [${pWorkItemHash}]: ${pError.message}`);
445
+ }
446
+ });
447
+ }
448
+
449
+ _reportError(pWorkItemHash, pErrorMessage, pLog)
450
+ {
451
+ this._httpRequest('POST', `/Beacon/Work/${pWorkItemHash}/Error`,
452
+ { ErrorMessage: pErrorMessage, Log: pLog },
453
+ (pError) =>
454
+ {
455
+ if (pError)
456
+ {
457
+ console.error(`[Beacon] Failed to report error for [${pWorkItemHash}]: ${pError.message}`);
458
+ }
459
+ });
460
+ }
461
+
462
+ _reportProgress(pWorkItemHash, pProgressData)
463
+ {
464
+ if (!pProgressData || !this._Running)
465
+ {
466
+ return;
467
+ }
468
+
469
+ this._httpRequest('POST', `/Beacon/Work/${pWorkItemHash}/Progress`,
470
+ pProgressData,
471
+ (pError) =>
472
+ {
473
+ if (pError)
474
+ {
475
+ // Fire-and-forget — log but don't affect execution
476
+ console.warn(`[Beacon] Failed to report progress for [${pWorkItemHash}]: ${pError.message}`);
477
+ }
478
+ });
479
+ }
480
+
481
+ // ================================================================
482
+ // Heartbeat
483
+ // ================================================================
484
+
485
+ _heartbeat()
486
+ {
487
+ if (!this._Running || !this._BeaconID)
488
+ {
489
+ return;
490
+ }
491
+
492
+ this._httpRequest('POST', `/Beacon/${this._BeaconID}/Heartbeat`, {},
493
+ (pError) =>
494
+ {
495
+ if (pError)
496
+ {
497
+ console.warn(`[Beacon] Heartbeat failed: ${pError.message}`);
498
+ }
499
+ });
500
+ }
501
+
502
+ // ================================================================
503
+ // HTTP Transport
504
+ // ================================================================
505
+
506
+ _httpRequest(pMethod, pPath, pBody, fCallback)
507
+ {
508
+ let tmpParsedURL = new URL(this._Config.ServerURL);
509
+ let tmpOptions = {
510
+ hostname: tmpParsedURL.hostname,
511
+ port: tmpParsedURL.port || 80,
512
+ path: pPath,
513
+ method: pMethod,
514
+ headers: {
515
+ 'Content-Type': 'application/json'
516
+ }
517
+ };
518
+
519
+ // Attach session cookie if available
520
+ if (this._SessionCookie)
521
+ {
522
+ tmpOptions.headers['Cookie'] = this._SessionCookie;
523
+ }
524
+
525
+ let tmpReq = libHTTP.request(tmpOptions, (pResponse) =>
526
+ {
527
+ let tmpData = '';
528
+ pResponse.on('data', (pChunk) => { tmpData += pChunk; });
529
+ pResponse.on('end', () =>
530
+ {
531
+ // Detect 401 and trigger reconnection
532
+ if (pResponse.statusCode === 401)
533
+ {
534
+ this._reconnect();
535
+ return fCallback(new Error('Unauthorized — reconnecting'));
536
+ }
537
+
538
+ try
539
+ {
540
+ let tmpParsed = JSON.parse(tmpData);
541
+ if (pResponse.statusCode >= 400)
542
+ {
543
+ return fCallback(new Error(tmpParsed.Error || `HTTP ${pResponse.statusCode}`));
544
+ }
545
+ return fCallback(null, tmpParsed);
546
+ }
547
+ catch (pParseError)
548
+ {
549
+ return fCallback(new Error(`Invalid JSON response: ${tmpData.substring(0, 200)}`));
550
+ }
551
+ });
552
+ });
553
+
554
+ tmpReq.on('error', (pError) =>
555
+ {
556
+ return fCallback(pError);
557
+ });
558
+
559
+ if (pBody && (pMethod === 'POST' || pMethod === 'PUT'))
560
+ {
561
+ tmpReq.write(JSON.stringify(pBody));
562
+ }
563
+
564
+ tmpReq.end();
565
+ }
566
+ }
567
+
568
+ module.exports = UltravisorBeaconClient;