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,629 @@
1
+ const libPictService = require('pict-serviceproviderbase');
2
+ const libFS = require('fs');
3
+ const libPath = require('path');
4
+
5
+ /**
6
+ * Manages operation execution manifests, staging folders, and run artifacts.
7
+ *
8
+ * Each operation run gets a staging folder containing:
9
+ * - Manifest JSON (always)
10
+ * - Task state snapshots (debug mode)
11
+ * - Operation state snapshot (debug mode)
12
+ * - Working files (task-created)
13
+ * - Run log file
14
+ */
15
+ class UltravisorExecutionManifest extends libPictService
16
+ {
17
+ constructor(pPict, pOptions, pServiceHash)
18
+ {
19
+ super(pPict, pOptions, pServiceHash);
20
+
21
+ this.serviceType = 'UltravisorExecutionManifest';
22
+
23
+ // In-memory store of recent run contexts keyed by RunHash
24
+ this._Runs = {};
25
+ }
26
+
27
+ /**
28
+ * Resolve the base staging root folder.
29
+ *
30
+ * @returns {string} Absolute path to the staging root.
31
+ */
32
+ resolveStagingRoot()
33
+ {
34
+ return (this.fable && this.fable.ProgramConfiguration && this.fable.ProgramConfiguration.UltravisorStagingRoot)
35
+ || libPath.resolve(process.cwd(), 'dist', 'ultravisor_staging');
36
+ }
37
+
38
+ /**
39
+ * Generate a timestamp suffix for unique folder naming.
40
+ *
41
+ * @returns {string} Formatted as YYYY-MM-DD-HH-MM-SS-MS_SALT.
42
+ */
43
+ generateTimestamp()
44
+ {
45
+ let tmpNow = new Date();
46
+ let tmpYear = tmpNow.getFullYear();
47
+ let tmpMonth = String(tmpNow.getMonth() + 1).padStart(2, '0');
48
+ let tmpDay = String(tmpNow.getDate()).padStart(2, '0');
49
+ let tmpHours = String(tmpNow.getHours()).padStart(2, '0');
50
+ let tmpMinutes = String(tmpNow.getMinutes()).padStart(2, '0');
51
+ let tmpSeconds = String(tmpNow.getSeconds()).padStart(2, '0');
52
+ let tmpMilliseconds = String(tmpNow.getMilliseconds()).padStart(3, '0');
53
+ let tmpSalt = String(Math.floor(Math.random() * 100)).padStart(2, '0');
54
+
55
+ return `${tmpYear}-${tmpMonth}-${tmpDay}-${tmpHours}-${tmpMinutes}-${tmpSeconds}-${tmpMilliseconds}_${tmpSalt}`;
56
+ }
57
+
58
+ /**
59
+ * Create a staging folder and execution context for a new operation run.
60
+ *
61
+ * @param {object} pOperationDefinition - The operation definition.
62
+ * @param {string} [pRunMode] - 'production' | 'standard' | 'debug'. Defaults to 'standard'.
63
+ * @returns {object} The new execution context.
64
+ */
65
+ createExecutionContext(pOperationDefinition, pRunMode)
66
+ {
67
+ let tmpTimestamp = this.generateTimestamp();
68
+ let tmpRunHash = `run-${pOperationDefinition.Hash}-${Date.now()}`;
69
+ let tmpRunMode = pRunMode || 'standard';
70
+
71
+ // Create staging folder
72
+ let tmpStagingRoot = this.resolveStagingRoot();
73
+ let tmpStagingPath = libPath.resolve(tmpStagingRoot, `${pOperationDefinition.Hash}-${tmpTimestamp}`);
74
+
75
+ try
76
+ {
77
+ if (!libFS.existsSync(tmpStagingPath))
78
+ {
79
+ libFS.mkdirSync(tmpStagingPath, { recursive: true });
80
+ }
81
+ }
82
+ catch (pError)
83
+ {
84
+ this.log.error(`UltravisorExecutionManifest: failed to create staging folder [${tmpStagingPath}]: ${pError.message}`);
85
+ }
86
+
87
+ let tmpContext = {
88
+ Hash: tmpRunHash,
89
+ OperationHash: pOperationDefinition.Hash,
90
+ OperationName: pOperationDefinition.Name || pOperationDefinition.Hash,
91
+ Status: 'Pending',
92
+ RunMode: tmpRunMode,
93
+ StagingPath: tmpStagingPath,
94
+
95
+ GlobalState: {},
96
+ OperationState: {},
97
+ TaskOutputs: {},
98
+ Output: {},
99
+
100
+ PendingEvents: [],
101
+ WaitingTasks: {},
102
+
103
+ TaskManifests: {},
104
+ EventLog: [],
105
+ Log: [],
106
+ Errors: [],
107
+
108
+ StartTime: null,
109
+ StopTime: null,
110
+ ElapsedMs: null,
111
+ TimingSummary: null
112
+ };
113
+
114
+ // Store in memory
115
+ this._Runs[tmpRunHash] = tmpContext;
116
+
117
+ return tmpContext;
118
+ }
119
+
120
+ /**
121
+ * Record a task execution start in the manifest.
122
+ *
123
+ * @param {object} pExecutionContext - The execution context.
124
+ * @param {string} pNodeHash - The task node hash.
125
+ * @param {string} pEventName - The triggering event name.
126
+ * @param {object} [pTaskMeta] - Optional task type metadata (DefinitionHash, TaskTypeName, Category, Capability, Action, Tier).
127
+ */
128
+ recordTaskStart(pExecutionContext, pNodeHash, pEventName, pTaskMeta)
129
+ {
130
+ let tmpMeta = pTaskMeta || {};
131
+
132
+ if (!pExecutionContext.TaskManifests[pNodeHash])
133
+ {
134
+ pExecutionContext.TaskManifests[pNodeHash] = {
135
+ NodeHash: pNodeHash,
136
+ DefinitionHash: tmpMeta.DefinitionHash || '',
137
+ TaskTypeName: tmpMeta.TaskTypeName || '',
138
+ Category: tmpMeta.Category || '',
139
+ Capability: tmpMeta.Capability || '',
140
+ Action: tmpMeta.Action || '',
141
+ Tier: tmpMeta.Tier || '',
142
+ Executions: []
143
+ };
144
+ }
145
+ else if (tmpMeta.DefinitionHash)
146
+ {
147
+ // Update metadata if not already set (e.g. first execution set it)
148
+ if (!pExecutionContext.TaskManifests[pNodeHash].DefinitionHash)
149
+ {
150
+ pExecutionContext.TaskManifests[pNodeHash].DefinitionHash = tmpMeta.DefinitionHash;
151
+ }
152
+ if (!pExecutionContext.TaskManifests[pNodeHash].TaskTypeName)
153
+ {
154
+ pExecutionContext.TaskManifests[pNodeHash].TaskTypeName = tmpMeta.TaskTypeName || '';
155
+ }
156
+ if (!pExecutionContext.TaskManifests[pNodeHash].Category)
157
+ {
158
+ pExecutionContext.TaskManifests[pNodeHash].Category = tmpMeta.Category || '';
159
+ }
160
+ if (!pExecutionContext.TaskManifests[pNodeHash].Capability)
161
+ {
162
+ pExecutionContext.TaskManifests[pNodeHash].Capability = tmpMeta.Capability || '';
163
+ }
164
+ if (!pExecutionContext.TaskManifests[pNodeHash].Action)
165
+ {
166
+ pExecutionContext.TaskManifests[pNodeHash].Action = tmpMeta.Action || '';
167
+ }
168
+ if (!pExecutionContext.TaskManifests[pNodeHash].Tier)
169
+ {
170
+ pExecutionContext.TaskManifests[pNodeHash].Tier = tmpMeta.Tier || '';
171
+ }
172
+ }
173
+
174
+ pExecutionContext.TaskManifests[pNodeHash].Executions.push({
175
+ TriggerEvent: pEventName,
176
+ StartTime: new Date().toISOString(),
177
+ StartTimeMs: Date.now(),
178
+ StopTime: null,
179
+ StopTimeMs: null,
180
+ ElapsedMs: null,
181
+ Status: 'Running',
182
+ Log: []
183
+ });
184
+ }
185
+
186
+ /**
187
+ * Record a task execution completion in the manifest.
188
+ *
189
+ * @param {object} pExecutionContext - The execution context.
190
+ * @param {string} pNodeHash - The task node hash.
191
+ * @param {object} pResult - The task result (EventToFire, Outputs, Log).
192
+ */
193
+ recordTaskComplete(pExecutionContext, pNodeHash, pResult)
194
+ {
195
+ let tmpTaskManifest = pExecutionContext.TaskManifests[pNodeHash];
196
+ if (!tmpTaskManifest || tmpTaskManifest.Executions.length === 0)
197
+ {
198
+ return;
199
+ }
200
+
201
+ let tmpExecution = tmpTaskManifest.Executions[tmpTaskManifest.Executions.length - 1];
202
+ tmpExecution.StopTime = new Date().toISOString();
203
+ tmpExecution.StopTimeMs = Date.now();
204
+ tmpExecution.ElapsedMs = (tmpExecution.StartTimeMs) ? (tmpExecution.StopTimeMs - tmpExecution.StartTimeMs) : 0;
205
+ tmpExecution.Status = 'Complete';
206
+ tmpExecution.EventFired = pResult.EventToFire || '';
207
+
208
+ if (Array.isArray(pResult.Log))
209
+ {
210
+ tmpExecution.Log = tmpExecution.Log.concat(pResult.Log);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Record a task execution error in the manifest.
216
+ *
217
+ * @param {object} pExecutionContext - The execution context.
218
+ * @param {string} pNodeHash - The task node hash.
219
+ * @param {Error} pError - The error that occurred.
220
+ */
221
+ recordTaskError(pExecutionContext, pNodeHash, pError)
222
+ {
223
+ let tmpTaskManifest = pExecutionContext.TaskManifests[pNodeHash];
224
+ if (!tmpTaskManifest || tmpTaskManifest.Executions.length === 0)
225
+ {
226
+ return;
227
+ }
228
+
229
+ let tmpExecution = tmpTaskManifest.Executions[tmpTaskManifest.Executions.length - 1];
230
+ tmpExecution.StopTime = new Date().toISOString();
231
+ tmpExecution.StopTimeMs = Date.now();
232
+ tmpExecution.ElapsedMs = (tmpExecution.StartTimeMs) ? (tmpExecution.StopTimeMs - tmpExecution.StartTimeMs) : 0;
233
+ tmpExecution.Status = 'Error';
234
+ tmpExecution.Log.push(`[${new Date().toISOString()}] Error: ${pError.message}`);
235
+
236
+ pExecutionContext.Errors.push({
237
+ NodeHash: pNodeHash,
238
+ Message: pError.message,
239
+ Timestamp: tmpExecution.StopTime
240
+ });
241
+ }
242
+
243
+ /**
244
+ * Record a telemetry event in the EventLog.
245
+ *
246
+ * @param {object} pExecutionContext - The execution context.
247
+ * @param {string} pNodeHash - The task node hash (or null for operation-level events).
248
+ * @param {string} pEventName - The event name.
249
+ * @param {string} pMessage - Human-readable description.
250
+ * @param {number} [pVerbosity] - 0 = normal (default), 1 = verbose, 2 = ultra-verbose.
251
+ */
252
+ recordEvent(pExecutionContext, pNodeHash, pEventName, pMessage, pVerbosity)
253
+ {
254
+ let tmpVerbosity = (typeof pVerbosity === 'number') ? pVerbosity : 0;
255
+
256
+ pExecutionContext.EventLog.push({
257
+ Timestamp: new Date().toISOString(),
258
+ TimestampMs: Date.now(),
259
+ NodeHash: pNodeHash || null,
260
+ EventName: pEventName || '',
261
+ Message: pMessage || '',
262
+ Verbosity: tmpVerbosity
263
+ });
264
+ }
265
+
266
+ /**
267
+ * Finalize the execution context after the operation completes.
268
+ * Writes manifest, state snapshots, and logs to the staging folder.
269
+ *
270
+ * @param {object} pExecutionContext - The execution context.
271
+ */
272
+ finalizeExecution(pExecutionContext)
273
+ {
274
+ pExecutionContext.StopTime = new Date().toISOString();
275
+ pExecutionContext.ElapsedMs = new Date(pExecutionContext.StopTime).getTime()
276
+ - new Date(pExecutionContext.StartTime).getTime();
277
+
278
+ if (pExecutionContext.Errors.length > 0)
279
+ {
280
+ pExecutionContext.Status = 'Error';
281
+ }
282
+ else if (pExecutionContext.Status !== 'WaitingForInput')
283
+ {
284
+ pExecutionContext.Status = 'Complete';
285
+ }
286
+
287
+ // Compute timing summary from task manifests
288
+ pExecutionContext.TimingSummary = this._computeTimingSummary(pExecutionContext);
289
+
290
+ let tmpStagingPath = pExecutionContext.StagingPath;
291
+
292
+ // Always write the manifest
293
+ this._writeManifest(pExecutionContext, tmpStagingPath);
294
+
295
+ // Write log file
296
+ this._writeLogFile(pExecutionContext, tmpStagingPath);
297
+
298
+ // Debug mode: write state snapshots
299
+ if (pExecutionContext.RunMode === 'debug')
300
+ {
301
+ this._writeStateSnapshots(pExecutionContext, tmpStagingPath);
302
+ }
303
+
304
+ // Production mode: clean up working files (but keep manifest and log)
305
+ if (pExecutionContext.RunMode === 'production')
306
+ {
307
+ this._cleanupWorkingFiles(pExecutionContext, tmpStagingPath);
308
+ }
309
+
310
+ this.log.info(`UltravisorExecutionManifest: operation [${pExecutionContext.OperationHash}] run [${pExecutionContext.Hash}] ${pExecutionContext.Status} in ${pExecutionContext.ElapsedMs}ms`);
311
+ }
312
+
313
+ /**
314
+ * Compute a timing summary from the task manifests.
315
+ *
316
+ * @param {object} pExecutionContext - The execution context.
317
+ * @returns {object} TimingSummary with ByCategory, ByCapability, ByTaskType, and Timeline.
318
+ */
319
+ _computeTimingSummary(pExecutionContext)
320
+ {
321
+ let tmpByCategory = {};
322
+ let tmpByCapability = {};
323
+ let tmpByTaskType = {};
324
+ let tmpTimeline = [];
325
+
326
+ let tmpNodeHashes = Object.keys(pExecutionContext.TaskManifests);
327
+
328
+ for (let i = 0; i < tmpNodeHashes.length; i++)
329
+ {
330
+ let tmpNodeHash = tmpNodeHashes[i];
331
+ let tmpTaskManifest = pExecutionContext.TaskManifests[tmpNodeHash];
332
+ let tmpDefHash = tmpTaskManifest.DefinitionHash || '';
333
+ let tmpName = tmpTaskManifest.TaskTypeName || tmpDefHash || tmpNodeHash;
334
+ let tmpCategory = tmpTaskManifest.Category || 'Uncategorized';
335
+ let tmpCapability = tmpTaskManifest.Capability || 'Uncategorized';
336
+
337
+ for (let j = 0; j < tmpTaskManifest.Executions.length; j++)
338
+ {
339
+ let tmpExec = tmpTaskManifest.Executions[j];
340
+ let tmpElapsed = tmpExec.ElapsedMs || 0;
341
+
342
+ // Timeline entry
343
+ tmpTimeline.push({
344
+ NodeHash: tmpNodeHash,
345
+ DefinitionHash: tmpDefHash,
346
+ Name: tmpName,
347
+ Category: tmpCategory,
348
+ Capability: tmpCapability,
349
+ StartTimeMs: tmpExec.StartTimeMs || 0,
350
+ ElapsedMs: tmpElapsed,
351
+ Status: tmpExec.Status || ''
352
+ });
353
+
354
+ // Aggregate by category
355
+ if (!tmpByCategory[tmpCategory])
356
+ {
357
+ tmpByCategory[tmpCategory] = { Count: 0, TotalMs: 0, MinMs: Infinity, MaxMs: 0, AvgMs: 0 };
358
+ }
359
+ tmpByCategory[tmpCategory].Count++;
360
+ tmpByCategory[tmpCategory].TotalMs += tmpElapsed;
361
+ if (tmpElapsed < tmpByCategory[tmpCategory].MinMs)
362
+ {
363
+ tmpByCategory[tmpCategory].MinMs = tmpElapsed;
364
+ }
365
+ if (tmpElapsed > tmpByCategory[tmpCategory].MaxMs)
366
+ {
367
+ tmpByCategory[tmpCategory].MaxMs = tmpElapsed;
368
+ }
369
+
370
+ // Aggregate by capability
371
+ if (!tmpByCapability[tmpCapability])
372
+ {
373
+ tmpByCapability[tmpCapability] = { Count: 0, TotalMs: 0, MinMs: Infinity, MaxMs: 0, AvgMs: 0 };
374
+ }
375
+ tmpByCapability[tmpCapability].Count++;
376
+ tmpByCapability[tmpCapability].TotalMs += tmpElapsed;
377
+ if (tmpElapsed < tmpByCapability[tmpCapability].MinMs)
378
+ {
379
+ tmpByCapability[tmpCapability].MinMs = tmpElapsed;
380
+ }
381
+ if (tmpElapsed > tmpByCapability[tmpCapability].MaxMs)
382
+ {
383
+ tmpByCapability[tmpCapability].MaxMs = tmpElapsed;
384
+ }
385
+
386
+ // Aggregate by task type
387
+ if (tmpDefHash)
388
+ {
389
+ if (!tmpByTaskType[tmpDefHash])
390
+ {
391
+ tmpByTaskType[tmpDefHash] = { Name: tmpName, Category: tmpCategory, Capability: tmpCapability, Count: 0, TotalMs: 0, MinMs: Infinity, MaxMs: 0, AvgMs: 0 };
392
+ }
393
+ tmpByTaskType[tmpDefHash].Count++;
394
+ tmpByTaskType[tmpDefHash].TotalMs += tmpElapsed;
395
+ if (tmpElapsed < tmpByTaskType[tmpDefHash].MinMs)
396
+ {
397
+ tmpByTaskType[tmpDefHash].MinMs = tmpElapsed;
398
+ }
399
+ if (tmpElapsed > tmpByTaskType[tmpDefHash].MaxMs)
400
+ {
401
+ tmpByTaskType[tmpDefHash].MaxMs = tmpElapsed;
402
+ }
403
+ }
404
+ }
405
+ }
406
+
407
+ // Compute averages and fix Infinity mins
408
+ let tmpCategoryKeys = Object.keys(tmpByCategory);
409
+ for (let i = 0; i < tmpCategoryKeys.length; i++)
410
+ {
411
+ let tmpCat = tmpByCategory[tmpCategoryKeys[i]];
412
+ tmpCat.AvgMs = (tmpCat.Count > 0) ? (tmpCat.TotalMs / tmpCat.Count) : 0;
413
+ if (tmpCat.MinMs === Infinity)
414
+ {
415
+ tmpCat.MinMs = 0;
416
+ }
417
+ }
418
+
419
+ let tmpCapabilityKeys = Object.keys(tmpByCapability);
420
+ for (let i = 0; i < tmpCapabilityKeys.length; i++)
421
+ {
422
+ let tmpCap = tmpByCapability[tmpCapabilityKeys[i]];
423
+ tmpCap.AvgMs = (tmpCap.Count > 0) ? (tmpCap.TotalMs / tmpCap.Count) : 0;
424
+ if (tmpCap.MinMs === Infinity)
425
+ {
426
+ tmpCap.MinMs = 0;
427
+ }
428
+ }
429
+
430
+ let tmpTaskTypeKeys = Object.keys(tmpByTaskType);
431
+ for (let i = 0; i < tmpTaskTypeKeys.length; i++)
432
+ {
433
+ let tmpType = tmpByTaskType[tmpTaskTypeKeys[i]];
434
+ tmpType.AvgMs = (tmpType.Count > 0) ? (tmpType.TotalMs / tmpType.Count) : 0;
435
+ if (tmpType.MinMs === Infinity)
436
+ {
437
+ tmpType.MinMs = 0;
438
+ }
439
+ }
440
+
441
+ // Sort timeline by start time
442
+ tmpTimeline.sort(function (pA, pB)
443
+ {
444
+ return (pA.StartTimeMs || 0) - (pB.StartTimeMs || 0);
445
+ });
446
+
447
+ return {
448
+ ByCategory: tmpByCategory,
449
+ ByCapability: tmpByCapability,
450
+ ByTaskType: tmpByTaskType,
451
+ Timeline: tmpTimeline
452
+ };
453
+ }
454
+
455
+ /**
456
+ * Get an execution context by run hash.
457
+ *
458
+ * @param {string} pRunHash - The run hash.
459
+ * @returns {object|null} The execution context, or null if not found.
460
+ */
461
+ getRun(pRunHash)
462
+ {
463
+ return this._Runs[pRunHash] || null;
464
+ }
465
+
466
+ /**
467
+ * List all execution runs.
468
+ *
469
+ * @returns {Array} Array of execution context summaries.
470
+ */
471
+ listRuns()
472
+ {
473
+ let tmpRuns = [];
474
+ let tmpKeys = Object.keys(this._Runs);
475
+
476
+ for (let i = 0; i < tmpKeys.length; i++)
477
+ {
478
+ let tmpRun = this._Runs[tmpKeys[i]];
479
+ tmpRuns.push({
480
+ Hash: tmpRun.Hash,
481
+ OperationHash: tmpRun.OperationHash,
482
+ OperationName: tmpRun.OperationName,
483
+ Status: tmpRun.Status,
484
+ RunMode: tmpRun.RunMode,
485
+ StartTime: tmpRun.StartTime,
486
+ StopTime: tmpRun.StopTime,
487
+ ElapsedMs: tmpRun.ElapsedMs,
488
+ ErrorCount: tmpRun.Errors.length,
489
+ StagingPath: tmpRun.StagingPath
490
+ });
491
+ }
492
+
493
+ return tmpRuns;
494
+ }
495
+
496
+ // --- Internal: file writing ---
497
+
498
+ _writeManifest(pExecutionContext, pStagingPath)
499
+ {
500
+ try
501
+ {
502
+ let tmpManifestPath = libPath.resolve(pStagingPath, `Manifest_${pExecutionContext.OperationHash}.json`);
503
+
504
+ // Build a serializable manifest (exclude large state data in production mode)
505
+ let tmpManifest = {
506
+ Hash: pExecutionContext.Hash,
507
+ OperationHash: pExecutionContext.OperationHash,
508
+ OperationName: pExecutionContext.OperationName,
509
+ Status: pExecutionContext.Status,
510
+ RunMode: pExecutionContext.RunMode,
511
+ StartTime: pExecutionContext.StartTime,
512
+ StopTime: pExecutionContext.StopTime,
513
+ ElapsedMs: pExecutionContext.ElapsedMs,
514
+ Output: pExecutionContext.Output || {},
515
+ TaskManifests: pExecutionContext.TaskManifests,
516
+ TimingSummary: pExecutionContext.TimingSummary,
517
+ EventLog: pExecutionContext.EventLog,
518
+ Errors: pExecutionContext.Errors,
519
+ Log: pExecutionContext.Log
520
+ };
521
+
522
+ // Debug mode: embed full state in the manifest
523
+ if (pExecutionContext.RunMode === 'debug')
524
+ {
525
+ tmpManifest.GlobalState = pExecutionContext.GlobalState;
526
+ tmpManifest.OperationState = pExecutionContext.OperationState;
527
+ tmpManifest.TaskOutputs = pExecutionContext.TaskOutputs;
528
+ }
529
+
530
+ libFS.writeFileSync(tmpManifestPath, JSON.stringify(tmpManifest, null, '\t'), 'utf8');
531
+ pExecutionContext.Log.push(`[${new Date().toISOString()}] Manifest written to ${tmpManifestPath}`);
532
+ }
533
+ catch (pError)
534
+ {
535
+ this.log.error(`UltravisorExecutionManifest: failed to write manifest: ${pError.message}`);
536
+ }
537
+ }
538
+
539
+ _writeLogFile(pExecutionContext, pStagingPath)
540
+ {
541
+ try
542
+ {
543
+ let tmpLogPath = libPath.resolve(pStagingPath, 'run.log');
544
+ let tmpLogContent = pExecutionContext.Log.join('\n') + '\n';
545
+ libFS.writeFileSync(tmpLogPath, tmpLogContent, 'utf8');
546
+ }
547
+ catch (pError)
548
+ {
549
+ this.log.error(`UltravisorExecutionManifest: failed to write log file: ${pError.message}`);
550
+ }
551
+ }
552
+
553
+ _writeStateSnapshots(pExecutionContext, pStagingPath)
554
+ {
555
+ try
556
+ {
557
+ let tmpStatePath = libPath.resolve(pStagingPath, 'state');
558
+ if (!libFS.existsSync(tmpStatePath))
559
+ {
560
+ libFS.mkdirSync(tmpStatePath, { recursive: true });
561
+ }
562
+
563
+ // Write operation state
564
+ let tmpOpStatePath = libPath.resolve(tmpStatePath, 'operation.json');
565
+ libFS.writeFileSync(tmpOpStatePath, JSON.stringify(pExecutionContext.OperationState, null, '\t'), 'utf8');
566
+
567
+ // Write per-task output state
568
+ let tmpTaskKeys = Object.keys(pExecutionContext.TaskOutputs);
569
+ for (let i = 0; i < tmpTaskKeys.length; i++)
570
+ {
571
+ let tmpNodeHash = tmpTaskKeys[i];
572
+ let tmpTaskStatePath = libPath.resolve(tmpStatePath, `${tmpNodeHash}.json`);
573
+ libFS.writeFileSync(tmpTaskStatePath,
574
+ JSON.stringify(pExecutionContext.TaskOutputs[tmpNodeHash], null, '\t'), 'utf8');
575
+ }
576
+
577
+ // Write operation output state
578
+ let tmpOutputPath = libPath.resolve(tmpStatePath, 'output.json');
579
+ libFS.writeFileSync(tmpOutputPath, JSON.stringify(pExecutionContext.Output || {}, null, '\t'), 'utf8');
580
+
581
+ pExecutionContext.Log.push(`[${new Date().toISOString()}] State snapshots written to ${tmpStatePath}`);
582
+ }
583
+ catch (pError)
584
+ {
585
+ this.log.error(`UltravisorExecutionManifest: failed to write state snapshots: ${pError.message}`);
586
+ }
587
+ }
588
+
589
+ _cleanupWorkingFiles(pExecutionContext, pStagingPath)
590
+ {
591
+ // In production mode, we keep the manifest and log but remove working files.
592
+ // Working files are anything in the staging folder that isn't the manifest or log.
593
+ try
594
+ {
595
+ let tmpFiles = libFS.readdirSync(pStagingPath);
596
+
597
+ for (let i = 0; i < tmpFiles.length; i++)
598
+ {
599
+ let tmpFile = tmpFiles[i];
600
+
601
+ // Keep manifest and log files
602
+ if (tmpFile.startsWith('Manifest_') || tmpFile === 'run.log')
603
+ {
604
+ continue;
605
+ }
606
+
607
+ let tmpFilePath = libPath.resolve(pStagingPath, tmpFile);
608
+ let tmpStat = libFS.statSync(tmpFilePath);
609
+
610
+ if (tmpStat.isDirectory())
611
+ {
612
+ libFS.rmSync(tmpFilePath, { recursive: true, force: true });
613
+ }
614
+ else
615
+ {
616
+ libFS.unlinkSync(tmpFilePath);
617
+ }
618
+ }
619
+
620
+ pExecutionContext.Log.push(`[${new Date().toISOString()}] Working files cleaned up from ${pStagingPath}`);
621
+ }
622
+ catch (pError)
623
+ {
624
+ this.log.error(`UltravisorExecutionManifest: failed to clean up working files: ${pError.message}`);
625
+ }
626
+ }
627
+ }
628
+
629
+ module.exports = UltravisorExecutionManifest;