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,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,331 @@
1
+ /**
2
+ * Ultravisor Beacon Provider — FileSystem
3
+ *
4
+ * Built-in provider for local file operations on the Beacon worker.
5
+ *
6
+ * Capability: 'FileSystem'
7
+ * Actions: 'Read', 'Write', 'List', 'Copy'
8
+ *
9
+ * Provider config:
10
+ * AllowedPaths {string[]} — path prefixes the provider may access (empty = allow all)
11
+ * MaxFileSizeBytes {number} — max file size for read/write (default: 100MB)
12
+ */
13
+
14
+ const libFS = require('fs');
15
+ const libPath = require('path');
16
+
17
+ const libBeaconCapabilityProvider = require('../Ultravisor-Beacon-CapabilityProvider.cjs');
18
+
19
+ class UltravisorBeaconProviderFileSystem extends libBeaconCapabilityProvider
20
+ {
21
+ constructor(pProviderConfig)
22
+ {
23
+ super(pProviderConfig);
24
+
25
+ this.Name = 'FileSystem';
26
+ this.Capability = 'FileSystem';
27
+
28
+ this._AllowedPaths = this._ProviderConfig.AllowedPaths || [];
29
+ this._MaxFileSizeBytes = this._ProviderConfig.MaxFileSizeBytes || 104857600;
30
+ }
31
+
32
+ get actions()
33
+ {
34
+ return {
35
+ 'Read':
36
+ {
37
+ Description: 'Read a file from disk.',
38
+ SettingsSchema:
39
+ [
40
+ { Name: 'FilePath', DataType: 'String', Required: true, Description: 'Path to the file to read' },
41
+ { Name: 'Encoding', DataType: 'String', Required: false, Description: 'File encoding (default: utf8)' }
42
+ ]
43
+ },
44
+ 'Write':
45
+ {
46
+ Description: 'Write content to a file on disk.',
47
+ SettingsSchema:
48
+ [
49
+ { Name: 'FilePath', DataType: 'String', Required: true, Description: 'Path to the output file' },
50
+ { Name: 'Content', DataType: 'String', Required: true, Description: 'Content to write' },
51
+ { Name: 'Encoding', DataType: 'String', Required: false, Description: 'File encoding (default: utf8)' }
52
+ ]
53
+ },
54
+ 'List':
55
+ {
56
+ Description: 'List files in a directory.',
57
+ SettingsSchema:
58
+ [
59
+ { Name: 'Folder', DataType: 'String', Required: true, Description: 'Directory path to list' },
60
+ { Name: 'Pattern', DataType: 'String', Required: false, Description: 'Glob-style pattern filter (e.g. *.txt)' }
61
+ ]
62
+ },
63
+ 'Copy':
64
+ {
65
+ Description: 'Copy a file from source to target.',
66
+ SettingsSchema:
67
+ [
68
+ { Name: 'Source', DataType: 'String', Required: true, Description: 'Source file path' },
69
+ { Name: 'TargetFile', DataType: 'String', Required: true, Description: 'Target file path' }
70
+ ]
71
+ }
72
+ };
73
+ }
74
+
75
+ execute(pAction, pWorkItem, pContext, fCallback, fReportProgress)
76
+ {
77
+ switch (pAction)
78
+ {
79
+ case 'Read':
80
+ return this._executeRead(pWorkItem, pContext, fCallback);
81
+ case 'Write':
82
+ return this._executeWrite(pWorkItem, pContext, fCallback);
83
+ case 'List':
84
+ return this._executeList(pWorkItem, pContext, fCallback);
85
+ case 'Copy':
86
+ return this._executeCopy(pWorkItem, pContext, fCallback);
87
+ default:
88
+ return fCallback(null, {
89
+ Outputs: { StdOut: `Unknown FileSystem action: ${pAction}`, ExitCode: -1, Result: '' },
90
+ Log: [`FileSystem Provider: unsupported action [${pAction}].`]
91
+ });
92
+ }
93
+ }
94
+
95
+ // ================================================================
96
+ // Path helpers
97
+ // ================================================================
98
+
99
+ _resolvePath(pFilePath, pStagingPath)
100
+ {
101
+ if (!pFilePath) return '';
102
+ if (libPath.isAbsolute(pFilePath)) return pFilePath;
103
+ return libPath.resolve(pStagingPath || process.cwd(), pFilePath);
104
+ }
105
+
106
+ _isPathAllowed(pResolvedPath)
107
+ {
108
+ if (this._AllowedPaths.length === 0) return true;
109
+ for (let i = 0; i < this._AllowedPaths.length; i++)
110
+ {
111
+ if (pResolvedPath.startsWith(this._AllowedPaths[i])) return true;
112
+ }
113
+ return false;
114
+ }
115
+
116
+ // ================================================================
117
+ // Actions
118
+ // ================================================================
119
+
120
+ _executeRead(pWorkItem, pContext, fCallback)
121
+ {
122
+ let tmpSettings = pWorkItem.Settings || {};
123
+ let tmpFilePath = tmpSettings.FilePath || '';
124
+ let tmpEncoding = tmpSettings.Encoding || 'utf8';
125
+
126
+ if (!tmpFilePath)
127
+ {
128
+ return fCallback(null, {
129
+ Outputs: { StdOut: 'No FilePath specified.', ExitCode: -1, Result: '' },
130
+ Log: ['FileSystem Read: no FilePath specified.']
131
+ });
132
+ }
133
+
134
+ tmpFilePath = this._resolvePath(tmpFilePath, pContext.StagingPath);
135
+
136
+ if (!this._isPathAllowed(tmpFilePath))
137
+ {
138
+ return fCallback(null, {
139
+ Outputs: { StdOut: `Path not allowed: ${tmpFilePath}`, ExitCode: -1, Result: '' },
140
+ Log: [`FileSystem Read: path not in AllowedPaths: ${tmpFilePath}`]
141
+ });
142
+ }
143
+
144
+ try
145
+ {
146
+ let tmpContent = libFS.readFileSync(tmpFilePath, tmpEncoding);
147
+ let tmpBytesRead = Buffer.byteLength(tmpContent, tmpEncoding);
148
+
149
+ return fCallback(null, {
150
+ Outputs: {
151
+ StdOut: `Read ${tmpBytesRead} bytes from ${tmpFilePath}`,
152
+ ExitCode: 0,
153
+ Result: tmpContent
154
+ },
155
+ Log: [`FileSystem Read: read ${tmpBytesRead} bytes from ${tmpFilePath}`]
156
+ });
157
+ }
158
+ catch (pError)
159
+ {
160
+ return fCallback(null, {
161
+ Outputs: { StdOut: `Read failed: ${pError.message}`, ExitCode: 1, Result: '' },
162
+ Log: [`FileSystem Read: failed to read ${tmpFilePath}: ${pError.message}`]
163
+ });
164
+ }
165
+ }
166
+
167
+ _executeWrite(pWorkItem, pContext, fCallback)
168
+ {
169
+ let tmpSettings = pWorkItem.Settings || {};
170
+ let tmpFilePath = tmpSettings.FilePath || '';
171
+ let tmpContent = tmpSettings.Content;
172
+ let tmpEncoding = tmpSettings.Encoding || 'utf8';
173
+
174
+ if (!tmpFilePath)
175
+ {
176
+ return fCallback(null, {
177
+ Outputs: { StdOut: 'No FilePath specified.', ExitCode: -1, Result: '' },
178
+ Log: ['FileSystem Write: no FilePath specified.']
179
+ });
180
+ }
181
+
182
+ if (tmpContent === undefined || tmpContent === null) { tmpContent = ''; }
183
+ if (typeof tmpContent !== 'string') { tmpContent = JSON.stringify(tmpContent, null, '\t'); }
184
+
185
+ tmpFilePath = this._resolvePath(tmpFilePath, pContext.StagingPath);
186
+
187
+ if (!this._isPathAllowed(tmpFilePath))
188
+ {
189
+ return fCallback(null, {
190
+ Outputs: { StdOut: `Path not allowed: ${tmpFilePath}`, ExitCode: -1, Result: '' },
191
+ Log: [`FileSystem Write: path not in AllowedPaths: ${tmpFilePath}`]
192
+ });
193
+ }
194
+
195
+ try
196
+ {
197
+ let tmpDir = libPath.dirname(tmpFilePath);
198
+ if (!libFS.existsSync(tmpDir)) { libFS.mkdirSync(tmpDir, { recursive: true }); }
199
+ libFS.writeFileSync(tmpFilePath, tmpContent, tmpEncoding);
200
+ let tmpBytesWritten = Buffer.byteLength(tmpContent, tmpEncoding);
201
+
202
+ return fCallback(null, {
203
+ Outputs: {
204
+ StdOut: `Wrote ${tmpBytesWritten} bytes to ${tmpFilePath}`,
205
+ ExitCode: 0,
206
+ Result: tmpFilePath
207
+ },
208
+ Log: [`FileSystem Write: wrote ${tmpBytesWritten} bytes to ${tmpFilePath}`]
209
+ });
210
+ }
211
+ catch (pError)
212
+ {
213
+ return fCallback(null, {
214
+ Outputs: { StdOut: `Write failed: ${pError.message}`, ExitCode: 1, Result: '' },
215
+ Log: [`FileSystem Write: failed to write ${tmpFilePath}: ${pError.message}`]
216
+ });
217
+ }
218
+ }
219
+
220
+ _executeList(pWorkItem, pContext, fCallback)
221
+ {
222
+ let tmpSettings = pWorkItem.Settings || {};
223
+ let tmpFolder = tmpSettings.Folder || '';
224
+ let tmpPattern = tmpSettings.Pattern || '*';
225
+
226
+ if (!tmpFolder)
227
+ {
228
+ return fCallback(null, {
229
+ Outputs: { StdOut: 'No Folder specified.', ExitCode: -1, Result: '' },
230
+ Log: ['FileSystem List: no Folder specified.']
231
+ });
232
+ }
233
+
234
+ tmpFolder = this._resolvePath(tmpFolder, pContext.StagingPath);
235
+
236
+ if (!this._isPathAllowed(tmpFolder))
237
+ {
238
+ return fCallback(null, {
239
+ Outputs: { StdOut: `Path not allowed: ${tmpFolder}`, ExitCode: -1, Result: '' },
240
+ Log: [`FileSystem List: path not in AllowedPaths: ${tmpFolder}`]
241
+ });
242
+ }
243
+
244
+ try
245
+ {
246
+ let tmpFiles = libFS.readdirSync(tmpFolder);
247
+
248
+ // Simple glob: convert * and ? to regex
249
+ if (tmpPattern && tmpPattern !== '*')
250
+ {
251
+ let tmpRegex = new RegExp('^' + tmpPattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
252
+ tmpFiles = tmpFiles.filter(function (pFile) { return tmpRegex.test(pFile); });
253
+ }
254
+
255
+ return fCallback(null, {
256
+ Outputs: {
257
+ StdOut: `Found ${tmpFiles.length} files in ${tmpFolder}`,
258
+ ExitCode: 0,
259
+ Result: JSON.stringify(tmpFiles)
260
+ },
261
+ Log: [`FileSystem List: found ${tmpFiles.length} files in ${tmpFolder}`]
262
+ });
263
+ }
264
+ catch (pError)
265
+ {
266
+ return fCallback(null, {
267
+ Outputs: { StdOut: `List failed: ${pError.message}`, ExitCode: 1, Result: '' },
268
+ Log: [`FileSystem List: failed: ${pError.message}`]
269
+ });
270
+ }
271
+ }
272
+
273
+ _executeCopy(pWorkItem, pContext, fCallback)
274
+ {
275
+ let tmpSettings = pWorkItem.Settings || {};
276
+ let tmpSource = tmpSettings.Source || '';
277
+ let tmpTarget = tmpSettings.TargetFile || '';
278
+
279
+ if (!tmpSource || !tmpTarget)
280
+ {
281
+ return fCallback(null, {
282
+ Outputs: { StdOut: 'Source and TargetFile are required.', ExitCode: -1, Result: '' },
283
+ Log: ['FileSystem Copy: Source and TargetFile are required.']
284
+ });
285
+ }
286
+
287
+ tmpSource = this._resolvePath(tmpSource, pContext.StagingPath);
288
+ tmpTarget = this._resolvePath(tmpTarget, pContext.StagingPath);
289
+
290
+ if (!this._isPathAllowed(tmpSource))
291
+ {
292
+ return fCallback(null, {
293
+ Outputs: { StdOut: `Source path not allowed: ${tmpSource}`, ExitCode: -1, Result: '' },
294
+ Log: [`FileSystem Copy: source not in AllowedPaths: ${tmpSource}`]
295
+ });
296
+ }
297
+
298
+ if (!this._isPathAllowed(tmpTarget))
299
+ {
300
+ return fCallback(null, {
301
+ Outputs: { StdOut: `Target path not allowed: ${tmpTarget}`, ExitCode: -1, Result: '' },
302
+ Log: [`FileSystem Copy: target not in AllowedPaths: ${tmpTarget}`]
303
+ });
304
+ }
305
+
306
+ try
307
+ {
308
+ let tmpDir = libPath.dirname(tmpTarget);
309
+ if (!libFS.existsSync(tmpDir)) { libFS.mkdirSync(tmpDir, { recursive: true }); }
310
+ libFS.copyFileSync(tmpSource, tmpTarget);
311
+
312
+ return fCallback(null, {
313
+ Outputs: {
314
+ StdOut: `Copied ${tmpSource} → ${tmpTarget}`,
315
+ ExitCode: 0,
316
+ Result: tmpTarget
317
+ },
318
+ Log: [`FileSystem Copy: copied ${tmpSource} → ${tmpTarget}`]
319
+ });
320
+ }
321
+ catch (pError)
322
+ {
323
+ return fCallback(null, {
324
+ Outputs: { StdOut: `Copy failed: ${pError.message}`, ExitCode: 1, Result: '' },
325
+ Log: [`FileSystem Copy: failed: ${pError.message}`]
326
+ });
327
+ }
328
+ }
329
+ }
330
+
331
+ module.exports = UltravisorBeaconProviderFileSystem;