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
@@ -14,6 +14,16 @@ class UltravisorHypervisor extends libPictService
14
14
  this._Running = false;
15
15
  }
16
16
 
17
+ /**
18
+ * Get a service instance from the fable services map.
19
+ */
20
+ _getService(pTypeName)
21
+ {
22
+ return this.fable.servicesMap[pTypeName]
23
+ ? Object.values(this.fable.servicesMap[pTypeName])[0]
24
+ : null;
25
+ }
26
+
17
27
  get schedule()
18
28
  {
19
29
  return this.getSchedule();
@@ -25,46 +35,85 @@ class UltravisorHypervisor extends libPictService
25
35
  }
26
36
 
27
37
  /**
28
- * Add a task to the schedule.
38
+ * Load the schedule from the persistence provider.
29
39
  *
30
- * @param {string} pTaskGUID - The task GUID to schedule.
31
- * @param {string} pType - Schedule type (cron, daily, hourly).
32
- * @param {string} pParameters - Schedule parameters (e.g. cron expression).
33
- * @param {function} fCallback - Callback.
40
+ * If no provider is registered the in-memory schedule is left as-is.
41
+ *
42
+ * @param {function} fCallback - fCallback(pError)
34
43
  */
35
- scheduleTask(pTaskGUID, pType, pParameters, fCallback)
44
+ loadSchedule(fCallback)
36
45
  {
37
- let tmpScheduleEntry = {
38
- GUID: `sched-task-${pTaskGUID}-${Date.now()}`,
39
- TargetType: 'Task',
40
- TargetGUID: pTaskGUID,
41
- ScheduleType: pType || 'cron',
42
- Parameters: pParameters || '0 * * * *',
43
- CronExpression: this._resolveScheduleExpression(pType, pParameters),
44
- Active: false,
45
- CreatedAt: new Date().toISOString()
46
- };
46
+ let tmpPersistence = this._getService('UltravisorSchedulePersistence');
47
47
 
48
- this._Schedule.push(tmpScheduleEntry);
49
- this.log.info(`Ultravisor Hypervisor: scheduled task ${pTaskGUID} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
48
+ if (!tmpPersistence)
49
+ {
50
+ this.log.warn('Ultravisor Hypervisor: no schedule persistence provider registered; using in-memory schedule only.');
51
+ if (typeof(fCallback) === 'function')
52
+ {
53
+ return fCallback(null);
54
+ }
55
+ return;
56
+ }
50
57
 
51
- return fCallback(null, tmpScheduleEntry);
58
+ tmpPersistence.loadSchedule(
59
+ (pError, pSchedule) =>
60
+ {
61
+ if (pError)
62
+ {
63
+ this.log.error(`Ultravisor Hypervisor: failed to load schedule: ${pError.message}`);
64
+ }
65
+ else if (Array.isArray(pSchedule) && pSchedule.length > 0)
66
+ {
67
+ this._Schedule = pSchedule;
68
+ this.log.info(`Ultravisor Hypervisor: loaded ${pSchedule.length} schedule entries from persistence.`);
69
+ }
70
+
71
+ if (typeof(fCallback) === 'function')
72
+ {
73
+ return fCallback(pError);
74
+ }
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Persist the current schedule via the registered provider.
80
+ *
81
+ * Called internally after every mutation. Fails silently if no
82
+ * provider is registered so the Hypervisor works without persistence.
83
+ */
84
+ _persistSchedule()
85
+ {
86
+ let tmpPersistence = this._getService('UltravisorSchedulePersistence');
87
+
88
+ if (!tmpPersistence)
89
+ {
90
+ return;
91
+ }
92
+
93
+ tmpPersistence.saveSchedule(this._Schedule,
94
+ (pError) =>
95
+ {
96
+ if (pError)
97
+ {
98
+ this.log.error(`Ultravisor Hypervisor: failed to persist schedule: ${pError.message}`);
99
+ }
100
+ });
52
101
  }
53
102
 
54
103
  /**
55
104
  * Add an operation to the schedule.
56
105
  *
57
- * @param {string} pOperationGUID - The operation GUID to schedule.
106
+ * @param {string} pOperationHash - The operation hash to schedule.
58
107
  * @param {string} pType - Schedule type (cron, daily, hourly).
59
108
  * @param {string} pParameters - Schedule parameters.
60
109
  * @param {function} fCallback - Callback.
61
110
  */
62
- scheduleOperation(pOperationGUID, pType, pParameters, fCallback)
111
+ scheduleOperation(pOperationHash, pType, pParameters, fCallback)
63
112
  {
64
113
  let tmpScheduleEntry = {
65
- GUID: `sched-op-${pOperationGUID}-${Date.now()}`,
114
+ GUID: `sched-op-${pOperationHash}-${Date.now()}`,
66
115
  TargetType: 'Operation',
67
- TargetGUID: pOperationGUID,
116
+ TargetHash: pOperationHash,
68
117
  ScheduleType: pType || 'cron',
69
118
  Parameters: pParameters || '0 * * * *',
70
119
  CronExpression: this._resolveScheduleExpression(pType, pParameters),
@@ -73,7 +122,8 @@ class UltravisorHypervisor extends libPictService
73
122
  };
74
123
 
75
124
  this._Schedule.push(tmpScheduleEntry);
76
- this.log.info(`Ultravisor Hypervisor: scheduled operation ${pOperationGUID} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
125
+ this.log.info(`Ultravisor Hypervisor: scheduled operation ${pOperationHash} as ${tmpScheduleEntry.ScheduleType} (${tmpScheduleEntry.CronExpression})`);
126
+ this._persistSchedule();
77
127
 
78
128
  return fCallback(null, tmpScheduleEntry);
79
129
  }
@@ -103,10 +153,9 @@ class UltravisorHypervisor extends libPictService
103
153
  */
104
154
  startSchedule(fCallback)
105
155
  {
106
- let tmpCronService = this.fable['Ultravisor-Hypervisor-Event-Cron'];
107
- let tmpTaskService = this.fable['Ultravisor-Task'];
108
- let tmpOperationService = this.fable['Ultravisor-Operation'];
109
- let tmpStateService = this.fable['Ultravisor-Hypervisor-State'];
156
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
157
+ let tmpStateService = this._getService('UltravisorHypervisorState');
158
+ let tmpEngine = this._getService('UltravisorExecutionEngine');
110
159
 
111
160
  this._Running = true;
112
161
 
@@ -124,55 +173,28 @@ class UltravisorHypervisor extends libPictService
124
173
  tmpCronService.start(tmpEntry,
125
174
  (pScheduleEntry) =>
126
175
  {
127
- // On tick, execute the target
128
- if (pScheduleEntry.TargetType === 'Task')
129
- {
130
- tmpStateService.getTask(pScheduleEntry.TargetGUID,
131
- (pError, pTask) =>
176
+ // On tick, execute the target operation
177
+ tmpStateService.getOperation(pScheduleEntry.TargetHash,
178
+ (pError, pOperation) =>
179
+ {
180
+ if (pError)
132
181
  {
133
- if (pError)
182
+ this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} not found: ${pError.message}`);
183
+ return;
184
+ }
185
+ tmpEngine.executeOperation(pOperation,
186
+ (pExecError, pContext) =>
134
187
  {
135
- this.log.error(`Ultravisor Hypervisor: scheduled task ${pScheduleEntry.TargetGUID} not found: ${pError.message}`);
136
- return;
137
- }
138
- tmpTaskService.executeTask(pTask, {},
139
- (pTaskError, pResult) =>
188
+ if (pExecError)
140
189
  {
141
- if (pTaskError)
142
- {
143
- this.log.error(`Ultravisor Hypervisor: scheduled task execution error: ${pTaskError.message}`);
144
- }
145
- else
146
- {
147
- this.log.info(`Ultravisor Hypervisor: scheduled task ${pScheduleEntry.TargetGUID} completed: ${pResult.Status}`);
148
- }
149
- });
150
- });
151
- }
152
- else if (pScheduleEntry.TargetType === 'Operation')
153
- {
154
- tmpStateService.getOperation(pScheduleEntry.TargetGUID,
155
- (pError, pOperation) =>
156
- {
157
- if (pError)
158
- {
159
- this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetGUID} not found: ${pError.message}`);
160
- return;
161
- }
162
- tmpOperationService.executeOperation(pOperation,
163
- (pOpError, pManifest) =>
190
+ this.log.error(`Ultravisor Hypervisor: scheduled operation execution error: ${pExecError.message}`);
191
+ }
192
+ else
164
193
  {
165
- if (pOpError)
166
- {
167
- this.log.error(`Ultravisor Hypervisor: scheduled operation execution error: ${pOpError.message}`);
168
- }
169
- else
170
- {
171
- this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetGUID} completed: ${pManifest.Status}`);
172
- }
173
- });
174
- });
175
- }
194
+ this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} completed: ${pContext.Status}`);
195
+ }
196
+ });
197
+ });
176
198
  });
177
199
  }
178
200
 
@@ -189,7 +211,7 @@ class UltravisorHypervisor extends libPictService
189
211
  */
190
212
  stopSchedule(fCallback)
191
213
  {
192
- let tmpCronService = this.fable['Ultravisor-Hypervisor-Event-Cron'];
214
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
193
215
 
194
216
  tmpCronService.stop();
195
217
  this._Running = false;
@@ -199,6 +221,7 @@ class UltravisorHypervisor extends libPictService
199
221
  this._Schedule[i].Active = false;
200
222
  }
201
223
 
224
+ this._persistSchedule();
202
225
  this.log.info(`Ultravisor Hypervisor: schedule stopped.`);
203
226
 
204
227
  if (typeof(fCallback) === 'function')
@@ -207,12 +230,98 @@ class UltravisorHypervisor extends libPictService
207
230
  }
208
231
  }
209
232
 
233
+ /**
234
+ * Start a single schedule entry by GUID.
235
+ */
236
+ startScheduleEntry(pGUID, fCallback)
237
+ {
238
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
239
+ let tmpStateService = this._getService('UltravisorHypervisorState');
240
+ let tmpEngine = this._getService('UltravisorExecutionEngine');
241
+
242
+ for (let i = 0; i < this._Schedule.length; i++)
243
+ {
244
+ let tmpEntry = this._Schedule[i];
245
+
246
+ if (tmpEntry.GUID === pGUID)
247
+ {
248
+ if (tmpEntry.Active)
249
+ {
250
+ return fCallback(null, tmpEntry);
251
+ }
252
+
253
+ tmpEntry.Active = true;
254
+
255
+ tmpCronService.start(tmpEntry,
256
+ (pScheduleEntry) =>
257
+ {
258
+ tmpStateService.getOperation(pScheduleEntry.TargetHash,
259
+ (pError, pOperation) =>
260
+ {
261
+ if (pError)
262
+ {
263
+ this.log.error(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} not found: ${pError.message}`);
264
+ return;
265
+ }
266
+ tmpEngine.executeOperation(pOperation,
267
+ (pExecError, pContext) =>
268
+ {
269
+ if (pExecError)
270
+ {
271
+ this.log.error(`Ultravisor Hypervisor: scheduled operation execution error: ${pExecError.message}`);
272
+ }
273
+ else
274
+ {
275
+ this.log.info(`Ultravisor Hypervisor: scheduled operation ${pScheduleEntry.TargetHash} completed: ${pContext.Status}`);
276
+ }
277
+ });
278
+ });
279
+ });
280
+
281
+ this._persistSchedule();
282
+ this.log.info(`Ultravisor Hypervisor: started schedule entry ${pGUID}`);
283
+ return fCallback(null, tmpEntry);
284
+ }
285
+ }
286
+
287
+ return fCallback(new Error(`Schedule entry ${pGUID} not found.`));
288
+ }
289
+
290
+ /**
291
+ * Stop a single schedule entry by GUID.
292
+ */
293
+ stopScheduleEntry(pGUID, fCallback)
294
+ {
295
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
296
+
297
+ for (let i = 0; i < this._Schedule.length; i++)
298
+ {
299
+ let tmpEntry = this._Schedule[i];
300
+
301
+ if (tmpEntry.GUID === pGUID)
302
+ {
303
+ if (!tmpEntry.Active)
304
+ {
305
+ return fCallback(null, tmpEntry);
306
+ }
307
+
308
+ tmpEntry.Active = false;
309
+ tmpCronService.stopJob(pGUID);
310
+ this._persistSchedule();
311
+ this.log.info(`Ultravisor Hypervisor: stopped schedule entry ${pGUID}`);
312
+ return fCallback(null, tmpEntry);
313
+ }
314
+ }
315
+
316
+ return fCallback(new Error(`Schedule entry ${pGUID} not found.`));
317
+ }
318
+
210
319
  /**
211
320
  * Remove a schedule entry by GUID.
212
321
  */
213
322
  removeScheduleEntry(pGUID, fCallback)
214
323
  {
215
- let tmpCronService = this.fable['Ultravisor-Hypervisor-Event-Cron'];
324
+ let tmpCronService = this._getService('UltravisorHypervisorEventCron');
216
325
 
217
326
  for (let i = 0; i < this._Schedule.length; i++)
218
327
  {
@@ -223,6 +332,7 @@ class UltravisorHypervisor extends libPictService
223
332
  tmpCronService.stopJob(pGUID);
224
333
  }
225
334
  this._Schedule.splice(i, 1);
335
+ this._persistSchedule();
226
336
  this.log.info(`Ultravisor Hypervisor: removed schedule entry ${pGUID}`);
227
337
  return fCallback(null, true);
228
338
  }
@@ -0,0 +1,45 @@
1
+ const libPictService = require('pict-serviceproviderbase');
2
+
3
+ /**
4
+ * Base class for schedule persistence providers.
5
+ *
6
+ * Defines the interface that all persistence backends must implement.
7
+ * The default implementations return errors so that downstream providers
8
+ * that only partially implement the interface get clear feedback.
9
+ *
10
+ * To create a custom provider (e.g. remote API), extend this class and
11
+ * override loadSchedule() and saveSchedule(), then register your class
12
+ * under the service type 'UltravisorSchedulePersistence'.
13
+ */
14
+ class UltravisorSchedulePersistenceBase extends libPictService
15
+ {
16
+ constructor(pPict, pOptions, pServiceHash)
17
+ {
18
+ super(pPict, pOptions, pServiceHash);
19
+
20
+ this.serviceType = 'UltravisorSchedulePersistenceBase';
21
+ }
22
+
23
+ /**
24
+ * Load the schedule from the backing store.
25
+ *
26
+ * @param {function} fCallback - fCallback(pError, pScheduleArray)
27
+ */
28
+ loadSchedule(fCallback)
29
+ {
30
+ return fCallback(new Error('UltravisorSchedulePersistenceBase: loadSchedule() not implemented.'));
31
+ }
32
+
33
+ /**
34
+ * Save the full schedule to the backing store.
35
+ *
36
+ * @param {Array} pSchedule - The schedule array to persist.
37
+ * @param {function} fCallback - fCallback(pError)
38
+ */
39
+ saveSchedule(pSchedule, fCallback)
40
+ {
41
+ return fCallback(new Error('UltravisorSchedulePersistenceBase: saveSchedule() not implemented.'));
42
+ }
43
+ }
44
+
45
+ module.exports = UltravisorSchedulePersistenceBase;
@@ -0,0 +1,253 @@
1
+ const libPictService = require('pict-serviceproviderbase');
2
+
3
+ /**
4
+ * Manages three-level state (Global, Operation, Task) and provides
5
+ * address resolution through Manyfest.
6
+ *
7
+ * Address prefixes:
8
+ * Global.X -> GlobalState.X
9
+ * Operation.X -> OperationState.X
10
+ * Task.X -> TaskOutputs[currentNodeHash].X
11
+ * TaskOutput.{Hash}.X -> TaskOutputs[Hash].X
12
+ * Output.X -> Output.X
13
+ * Staging.Path -> StagingPath string
14
+ */
15
+ class UltravisorStateManager extends libPictService
16
+ {
17
+ constructor(pPict, pOptions, pServiceHash)
18
+ {
19
+ super(pPict, pOptions, pServiceHash);
20
+
21
+ this.serviceType = 'UltravisorStateManager';
22
+
23
+ this._Manyfest = this.fable.newManyfest();
24
+ }
25
+
26
+ /**
27
+ * Resolve an address against the execution context.
28
+ *
29
+ * @param {string} pAddress - The address to resolve (e.g. 'Operation.InputFilePath').
30
+ * @param {object} pExecutionContext - The runtime execution context.
31
+ * @param {string} [pCurrentNodeHash] - The current task node's hash (for Task.X resolution).
32
+ * @returns {*} The resolved value, or undefined if not found.
33
+ */
34
+ resolveAddress(pAddress, pExecutionContext, pCurrentNodeHash)
35
+ {
36
+ if (!pAddress || typeof(pAddress) !== 'string')
37
+ {
38
+ return undefined;
39
+ }
40
+
41
+ let tmpDotIndex = pAddress.indexOf('.');
42
+ let tmpPrefix;
43
+ let tmpRemainder;
44
+
45
+ if (tmpDotIndex > -1)
46
+ {
47
+ tmpPrefix = pAddress.substring(0, tmpDotIndex);
48
+ tmpRemainder = pAddress.substring(tmpDotIndex + 1);
49
+ }
50
+ else
51
+ {
52
+ tmpPrefix = pAddress;
53
+ tmpRemainder = '';
54
+ }
55
+
56
+ switch (tmpPrefix)
57
+ {
58
+ case 'Global':
59
+ return this._resolveFromObject(pExecutionContext.GlobalState, tmpRemainder);
60
+
61
+ case 'Operation':
62
+ return this._resolveFromObject(pExecutionContext.OperationState, tmpRemainder);
63
+
64
+ case 'Task':
65
+ if (!pCurrentNodeHash)
66
+ {
67
+ this.log.warn(`UltravisorStateManager: resolveAddress for Task.X requires a current node hash.`);
68
+ return undefined;
69
+ }
70
+ return this._resolveFromObject(
71
+ pExecutionContext.TaskOutputs[pCurrentNodeHash] || {},
72
+ tmpRemainder);
73
+
74
+ case 'TaskOutput':
75
+ {
76
+ // TaskOutput.{NodeHash}.{Path}
77
+ let tmpSecondDot = tmpRemainder.indexOf('.');
78
+ if (tmpSecondDot < 0)
79
+ {
80
+ // Just TaskOutput.{NodeHash} -- return the whole output object
81
+ return pExecutionContext.TaskOutputs[tmpRemainder] || {};
82
+ }
83
+ let tmpNodeHash = tmpRemainder.substring(0, tmpSecondDot);
84
+ let tmpPath = tmpRemainder.substring(tmpSecondDot + 1);
85
+ return this._resolveFromObject(
86
+ pExecutionContext.TaskOutputs[tmpNodeHash] || {},
87
+ tmpPath);
88
+ }
89
+
90
+ case 'Output':
91
+ return this._resolveFromObject(pExecutionContext.Output, tmpRemainder);
92
+
93
+ case 'Staging':
94
+ if (tmpRemainder === 'Path' || tmpRemainder === '')
95
+ {
96
+ return pExecutionContext.StagingPath || '';
97
+ }
98
+ return undefined;
99
+
100
+ default:
101
+ // Fall through: try the full address against OperationState first,
102
+ // then GlobalState. This allows shorthand like 'InputFilePath'
103
+ // to resolve from Operation state.
104
+ let tmpValue = this._resolveFromObject(pExecutionContext.OperationState, pAddress);
105
+ if (tmpValue !== undefined)
106
+ {
107
+ return tmpValue;
108
+ }
109
+ return this._resolveFromObject(pExecutionContext.GlobalState, pAddress);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Set a value at an address in the execution context.
115
+ *
116
+ * @param {string} pAddress - The address to set (e.g. 'Operation.InputFilePath').
117
+ * @param {*} pValue - The value to set.
118
+ * @param {object} pExecutionContext - The runtime execution context.
119
+ * @param {string} [pCurrentNodeHash] - The current task node's hash (for Task.X).
120
+ * @returns {boolean} True if the value was set successfully.
121
+ */
122
+ setAddress(pAddress, pValue, pExecutionContext, pCurrentNodeHash)
123
+ {
124
+ if (!pAddress || typeof(pAddress) !== 'string')
125
+ {
126
+ return false;
127
+ }
128
+
129
+ let tmpDotIndex = pAddress.indexOf('.');
130
+ let tmpPrefix;
131
+ let tmpRemainder;
132
+
133
+ if (tmpDotIndex > -1)
134
+ {
135
+ tmpPrefix = pAddress.substring(0, tmpDotIndex);
136
+ tmpRemainder = pAddress.substring(tmpDotIndex + 1);
137
+ }
138
+ else
139
+ {
140
+ tmpPrefix = pAddress;
141
+ tmpRemainder = '';
142
+ }
143
+
144
+ switch (tmpPrefix)
145
+ {
146
+ case 'Global':
147
+ return this._setOnObject(pExecutionContext.GlobalState, tmpRemainder, pValue);
148
+
149
+ case 'Operation':
150
+ return this._setOnObject(pExecutionContext.OperationState, tmpRemainder, pValue);
151
+
152
+ case 'Task':
153
+ if (!pCurrentNodeHash)
154
+ {
155
+ this.log.warn(`UltravisorStateManager: setAddress for Task.X requires a current node hash.`);
156
+ return false;
157
+ }
158
+ if (!pExecutionContext.TaskOutputs[pCurrentNodeHash])
159
+ {
160
+ pExecutionContext.TaskOutputs[pCurrentNodeHash] = {};
161
+ }
162
+ return this._setOnObject(
163
+ pExecutionContext.TaskOutputs[pCurrentNodeHash],
164
+ tmpRemainder, pValue);
165
+
166
+ case 'TaskOutput':
167
+ {
168
+ let tmpSecondDot = tmpRemainder.indexOf('.');
169
+ if (tmpSecondDot < 0)
170
+ {
171
+ // Setting the entire TaskOutput for a node
172
+ pExecutionContext.TaskOutputs[tmpRemainder] = pValue;
173
+ return true;
174
+ }
175
+ let tmpNodeHash = tmpRemainder.substring(0, tmpSecondDot);
176
+ let tmpPath = tmpRemainder.substring(tmpSecondDot + 1);
177
+ if (!pExecutionContext.TaskOutputs[tmpNodeHash])
178
+ {
179
+ pExecutionContext.TaskOutputs[tmpNodeHash] = {};
180
+ }
181
+ return this._setOnObject(
182
+ pExecutionContext.TaskOutputs[tmpNodeHash],
183
+ tmpPath, pValue);
184
+ }
185
+
186
+ case 'Output':
187
+ if (!pExecutionContext.Output)
188
+ {
189
+ pExecutionContext.Output = {};
190
+ }
191
+ return this._setOnObject(pExecutionContext.Output, tmpRemainder, pValue);
192
+
193
+ default:
194
+ // Default: write to OperationState
195
+ return this._setOnObject(pExecutionContext.OperationState, pAddress, pValue);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Build a root data object for Pict template resolution.
201
+ * This object can be passed to pict.parseTemplate as the record.
202
+ *
203
+ * @param {object} pExecutionContext - The runtime execution context.
204
+ * @param {*} [pSourceValue] - An optional source value (e.g. for state connection resolution).
205
+ * @returns {object} Root data object with all state levels.
206
+ */
207
+ buildTemplateContext(pExecutionContext, pSourceValue)
208
+ {
209
+ return {
210
+ Value: pSourceValue,
211
+ Global: pExecutionContext.GlobalState || {},
212
+ Operation: pExecutionContext.OperationState || {},
213
+ TaskOutput: pExecutionContext.TaskOutputs || {},
214
+ Output: pExecutionContext.Output || {},
215
+ Staging: { Path: pExecutionContext.StagingPath || '' }
216
+ };
217
+ }
218
+
219
+ // --- Internal helpers ---
220
+
221
+ _resolveFromObject(pObject, pPath)
222
+ {
223
+ if (!pObject || typeof(pObject) !== 'object')
224
+ {
225
+ return undefined;
226
+ }
227
+
228
+ if (!pPath || pPath === '')
229
+ {
230
+ return pObject;
231
+ }
232
+
233
+ return this._Manyfest.getValueAtAddress(pObject, pPath);
234
+ }
235
+
236
+ _setOnObject(pObject, pPath, pValue)
237
+ {
238
+ if (!pObject || typeof(pObject) !== 'object')
239
+ {
240
+ return false;
241
+ }
242
+
243
+ if (!pPath || pPath === '')
244
+ {
245
+ return false;
246
+ }
247
+
248
+ this._Manyfest.setValueAtAddress(pObject, pPath, pValue);
249
+ return true;
250
+ }
251
+ }
252
+
253
+ module.exports = UltravisorStateManager;