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,509 @@
1
+ /**
2
+ * File System task configurations for Ultravisor.
3
+ *
4
+ * Contains task types for reading, writing, listing, and copying files:
5
+ * read-file, write-file, read-json, write-json, list-files, copy-file, read-file-buffered
6
+ *
7
+ * Each entry defines a task type as a config object with:
8
+ * Definition {object} - Port schema, metadata, default settings
9
+ * Execute {function} - Runtime logic: function(pTask, pSettings, pContext, fCb, fFireEvent)
10
+ */
11
+
12
+ const libFS = require('fs');
13
+ const libPath = require('path');
14
+
15
+ /**
16
+ * Recursively sort all object keys alphabetically (deep).
17
+ */
18
+ function _sortObjectKeys(pObj)
19
+ {
20
+ if (Array.isArray(pObj)) { return pObj.map(_sortObjectKeys); }
21
+ if (pObj !== null && typeof(pObj) === 'object')
22
+ {
23
+ let tmpSorted = {};
24
+ Object.keys(pObj).sort().forEach(function(pKey) { tmpSorted[pKey] = _sortObjectKeys(pObj[pKey]); });
25
+ return tmpSorted;
26
+ }
27
+ return pObj;
28
+ }
29
+
30
+
31
+ // ═══════════════════════════════════════════════════════════════════
32
+ // FILE SYSTEM TASK CONFIGS
33
+ // ═══════════════════════════════════════════════════════════════════
34
+
35
+ module.exports =
36
+ [
37
+ // ── read-file ──────────────────────────────────────────────
38
+ {
39
+ Definition: require('./definitions/read-file.json'),
40
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
41
+ {
42
+ let tmpFilePath = pResolvedSettings.FilePath || '';
43
+ let tmpEncoding = pResolvedSettings.Encoding || 'utf8';
44
+ let tmpMaxBytes = parseInt(pResolvedSettings.MaxBytes, 10) || 0;
45
+
46
+ if (!tmpFilePath)
47
+ {
48
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['ReadFile: no FilePath specified.'] });
49
+ }
50
+
51
+ tmpFilePath = pTask.resolveFilePath(tmpFilePath, pExecutionContext.StagingPath);
52
+
53
+ try
54
+ {
55
+ let tmpContent;
56
+
57
+ if (tmpMaxBytes > 0)
58
+ {
59
+ let tmpStat = libFS.statSync(tmpFilePath);
60
+ if (tmpStat.size > tmpMaxBytes)
61
+ {
62
+ let tmpFd = libFS.openSync(tmpFilePath, 'r');
63
+ let tmpBuffer = Buffer.alloc(tmpMaxBytes);
64
+ libFS.readSync(tmpFd, tmpBuffer, 0, tmpMaxBytes, 0);
65
+ libFS.closeSync(tmpFd);
66
+ tmpContent = tmpBuffer.toString(tmpEncoding);
67
+ }
68
+ else
69
+ {
70
+ tmpContent = libFS.readFileSync(tmpFilePath, tmpEncoding);
71
+ }
72
+ }
73
+ else
74
+ {
75
+ tmpContent = libFS.readFileSync(tmpFilePath, tmpEncoding);
76
+ }
77
+
78
+ let tmpStateWrites = {};
79
+ if (pResolvedSettings.OutputAddress)
80
+ {
81
+ tmpStateWrites[pResolvedSettings.OutputAddress] = tmpContent;
82
+ }
83
+
84
+ return fCallback(null, {
85
+ EventToFire: 'ReadComplete',
86
+ Outputs: { FileContent: tmpContent, BytesRead: Buffer.byteLength(tmpContent, tmpEncoding), FileName: libPath.basename(tmpFilePath) },
87
+ StateWrites: tmpStateWrites,
88
+ Log: [`ReadFile: read ${Buffer.byteLength(tmpContent, tmpEncoding)} bytes from ${tmpFilePath}`]
89
+ });
90
+ }
91
+ catch (pError)
92
+ {
93
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`ReadFile: failed to read ${tmpFilePath}: ${pError.message}`] });
94
+ }
95
+ }
96
+ },
97
+
98
+ // ── write-file ─────────────────────────────────────────────
99
+ {
100
+ Definition: require('./definitions/write-file.json'),
101
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
102
+ {
103
+ let tmpFileLocation = pResolvedSettings.FilePath || '';
104
+ let tmpContent = pResolvedSettings.Content;
105
+ let tmpEncoding = pResolvedSettings.Encoding || 'utf8';
106
+
107
+ if (!tmpFileLocation)
108
+ {
109
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['WriteFile: no FilePath specified.'] });
110
+ }
111
+
112
+ if (tmpContent === undefined || tmpContent === null) { tmpContent = ''; }
113
+ if (typeof(tmpContent) !== 'string') { tmpContent = JSON.stringify(tmpContent, null, '\t'); }
114
+
115
+ // Apply line ending conversion
116
+ let tmpLineEnding = pResolvedSettings.LineEnding || '';
117
+ if (tmpLineEnding === 'crlf')
118
+ {
119
+ tmpContent = tmpContent.replace(/(?<!\r)\n/g, '\r\n');
120
+ }
121
+ else if (tmpLineEnding === 'lf')
122
+ {
123
+ tmpContent = tmpContent.replace(/\r\n/g, '\n');
124
+ }
125
+
126
+ let tmpFilePath = pTask.resolveFilePath(tmpFileLocation, pExecutionContext.StagingPath);
127
+
128
+ try
129
+ {
130
+ let tmpDir = libPath.dirname(tmpFilePath);
131
+ if (!libFS.existsSync(tmpDir)) { libFS.mkdirSync(tmpDir, { recursive: true }); }
132
+
133
+ if (pResolvedSettings.Append)
134
+ {
135
+ libFS.appendFileSync(tmpFilePath, tmpContent, tmpEncoding);
136
+ }
137
+ else
138
+ {
139
+ libFS.writeFileSync(tmpFilePath, tmpContent, tmpEncoding);
140
+ }
141
+
142
+ let tmpBytesWritten = Buffer.byteLength(tmpContent, tmpEncoding);
143
+
144
+ return fCallback(null, {
145
+ EventToFire: 'WriteComplete',
146
+ Outputs: {
147
+ FileLocation: tmpFileLocation,
148
+ FileName: libPath.basename(tmpFilePath),
149
+ FilePath: tmpFilePath,
150
+ BytesWritten: tmpBytesWritten
151
+ },
152
+ Log: [`WriteFile: wrote ${tmpBytesWritten} bytes to ${tmpFilePath}`]
153
+ });
154
+ }
155
+ catch (pError)
156
+ {
157
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`WriteFile: failed to write ${tmpFilePath}: ${pError.message}`] });
158
+ }
159
+ }
160
+ },
161
+
162
+ // ── read-json ──────────────────────────────────────────────
163
+ {
164
+ Definition: require('./definitions/read-json.json'),
165
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
166
+ {
167
+ let tmpFilePath = pResolvedSettings.FilePath || '';
168
+
169
+ if (!tmpFilePath)
170
+ {
171
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['ReadJSON: no FilePath specified.'] });
172
+ }
173
+
174
+ tmpFilePath = pTask.resolveFilePath(tmpFilePath, pExecutionContext.StagingPath);
175
+
176
+ try
177
+ {
178
+ let tmpRawContent = libFS.readFileSync(tmpFilePath, 'utf8');
179
+ let tmpData = JSON.parse(tmpRawContent);
180
+ let tmpStateWrites = {};
181
+
182
+ if (pResolvedSettings.Destination)
183
+ {
184
+ tmpStateWrites[pResolvedSettings.Destination] = tmpData;
185
+ }
186
+
187
+ return fCallback(null, {
188
+ EventToFire: 'Complete',
189
+ Outputs: { Data: tmpData },
190
+ StateWrites: tmpStateWrites,
191
+ Log: [`ReadJSON: parsed ${tmpRawContent.length} bytes from ${tmpFilePath}`]
192
+ });
193
+ }
194
+ catch (pError)
195
+ {
196
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`ReadJSON: failed: ${pError.message}`] });
197
+ }
198
+ }
199
+ },
200
+
201
+ // ── write-json ─────────────────────────────────────────────
202
+ {
203
+ Definition: require('./definitions/write-json.json'),
204
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
205
+ {
206
+ let tmpFileLocation = pResolvedSettings.FilePath || '';
207
+
208
+ if (!tmpFileLocation)
209
+ {
210
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['WriteJSON: no FilePath specified.'] });
211
+ }
212
+
213
+ let tmpFilePath = pTask.resolveFilePath(tmpFileLocation, pExecutionContext.StagingPath);
214
+
215
+ let tmpDataAddress = pResolvedSettings.DataAddress || '';
216
+ let tmpData = null;
217
+ if (tmpDataAddress && pExecutionContext.StateManager)
218
+ {
219
+ tmpData = pExecutionContext.StateManager.resolveAddress(tmpDataAddress, pExecutionContext, pExecutionContext.NodeHash);
220
+ }
221
+
222
+ if (tmpData === undefined || tmpData === null)
223
+ {
224
+ tmpData = {};
225
+ }
226
+
227
+ try
228
+ {
229
+ if (pResolvedSettings.SortKeys)
230
+ {
231
+ tmpData = _sortObjectKeys(tmpData);
232
+ }
233
+
234
+ let tmpContent;
235
+ if (pResolvedSettings.PrettyFormat === false)
236
+ {
237
+ tmpContent = JSON.stringify(tmpData);
238
+ }
239
+ else
240
+ {
241
+ let tmpIndent;
242
+ if (pResolvedSettings.IndentType === 'space')
243
+ {
244
+ tmpIndent = ' '.repeat(pResolvedSettings.IndentCount || 2);
245
+ }
246
+ else
247
+ {
248
+ tmpIndent = '\t'.repeat(pResolvedSettings.IndentCount || 1);
249
+ }
250
+ tmpContent = JSON.stringify(tmpData, null, tmpIndent);
251
+ }
252
+
253
+ let tmpDir = libPath.dirname(tmpFilePath);
254
+ if (!libFS.existsSync(tmpDir)) { libFS.mkdirSync(tmpDir, { recursive: true }); }
255
+ libFS.writeFileSync(tmpFilePath, tmpContent, 'utf8');
256
+
257
+ return fCallback(null, {
258
+ EventToFire: 'Done',
259
+ Outputs: {
260
+ FileLocation: tmpFileLocation,
261
+ FileName: libPath.basename(tmpFilePath),
262
+ FilePath: tmpFilePath,
263
+ BytesWritten: Buffer.byteLength(tmpContent)
264
+ },
265
+ Log: [`WriteJSON: wrote ${Buffer.byteLength(tmpContent)} bytes to ${tmpFilePath}`]
266
+ });
267
+ }
268
+ catch (pError)
269
+ {
270
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`WriteJSON: failed: ${pError.message}`] });
271
+ }
272
+ }
273
+ },
274
+
275
+ // ── list-files ─────────────────────────────────────────────
276
+ {
277
+ Definition: require('./definitions/list-files.json'),
278
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
279
+ {
280
+ let tmpFolder = pResolvedSettings.Folder || '';
281
+
282
+ if (!tmpFolder)
283
+ {
284
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['ListFiles: no Folder specified.'] });
285
+ }
286
+
287
+ tmpFolder = pTask.resolveFilePath(tmpFolder, pExecutionContext.StagingPath);
288
+
289
+ function _listFilesRecursive(pDir, pPattern)
290
+ {
291
+ let tmpResults = [];
292
+ let tmpEntries = libFS.readdirSync(pDir);
293
+
294
+ for (let i = 0; i < tmpEntries.length; i++)
295
+ {
296
+ let tmpFullPath = libPath.join(pDir, tmpEntries[i]);
297
+ let tmpStat = libFS.statSync(tmpFullPath);
298
+
299
+ if (tmpStat.isDirectory())
300
+ {
301
+ let tmpSubFiles = _listFilesRecursive(tmpFullPath, pPattern);
302
+ tmpResults = tmpResults.concat(tmpSubFiles);
303
+ // Include the directory entry itself as a relative path
304
+ let tmpRelative = libPath.relative(tmpFolder, tmpFullPath);
305
+ tmpResults.push(tmpRelative);
306
+ }
307
+ else
308
+ {
309
+ let tmpRelative = libPath.relative(tmpFolder, tmpFullPath);
310
+ tmpResults.push(tmpRelative);
311
+ }
312
+ }
313
+
314
+ return tmpResults;
315
+ }
316
+
317
+ try
318
+ {
319
+ let tmpFiles;
320
+
321
+ if (pResolvedSettings.Recursive)
322
+ {
323
+ tmpFiles = _listFilesRecursive(tmpFolder);
324
+ }
325
+ else
326
+ {
327
+ tmpFiles = libFS.readdirSync(tmpFolder);
328
+ }
329
+
330
+ let tmpPattern = pResolvedSettings.Pattern || '*';
331
+
332
+ // Simple glob: convert * to regex
333
+ if (tmpPattern && tmpPattern !== '*')
334
+ {
335
+ let tmpRegex = new RegExp('^' + tmpPattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
336
+ tmpFiles = tmpFiles.filter(function (pFile)
337
+ {
338
+ // For recursive results, match against the basename
339
+ let tmpBaseName = libPath.basename(pFile);
340
+ return tmpRegex.test(tmpBaseName);
341
+ });
342
+ }
343
+
344
+ if (!pResolvedSettings.IncludeDirectories)
345
+ {
346
+ tmpFiles = tmpFiles.filter(function (pFile)
347
+ {
348
+ let tmpFullPath = libPath.join(tmpFolder, pFile);
349
+ try
350
+ {
351
+ return libFS.statSync(tmpFullPath).isFile();
352
+ }
353
+ catch (pErr)
354
+ {
355
+ return false;
356
+ }
357
+ });
358
+ }
359
+
360
+ let tmpStateWrites = {};
361
+ if (pResolvedSettings.Destination)
362
+ {
363
+ tmpStateWrites[pResolvedSettings.Destination] = tmpFiles;
364
+ }
365
+
366
+ return fCallback(null, {
367
+ EventToFire: 'Complete',
368
+ Outputs: { Files: tmpFiles, FileCount: tmpFiles.length },
369
+ StateWrites: tmpStateWrites,
370
+ Log: [`ListFiles: found ${tmpFiles.length} files in ${tmpFolder}`]
371
+ });
372
+ }
373
+ catch (pError)
374
+ {
375
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`ListFiles: failed: ${pError.message}`] });
376
+ }
377
+ }
378
+ },
379
+
380
+ // ── copy-file ──────────────────────────────────────────────
381
+ {
382
+ Definition: require('./definitions/copy-file.json'),
383
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
384
+ {
385
+ let tmpSource = pResolvedSettings.Source || '';
386
+ let tmpTargetLocation = pResolvedSettings.TargetFile || '';
387
+
388
+ if (!tmpSource || !tmpTargetLocation)
389
+ {
390
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['CopyFile: Source and TargetFile are required.'] });
391
+ }
392
+
393
+ tmpSource = pTask.resolveFilePath(tmpSource, pExecutionContext.StagingPath);
394
+ let tmpTarget = pTask.resolveFilePath(tmpTargetLocation, pExecutionContext.StagingPath);
395
+
396
+ try
397
+ {
398
+ if (pResolvedSettings.Overwrite === false && libFS.existsSync(tmpTarget))
399
+ {
400
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`CopyFile: target file already exists and Overwrite is false: ${tmpTarget}`] });
401
+ }
402
+
403
+ let tmpDir = libPath.dirname(tmpTarget);
404
+ if (!libFS.existsSync(tmpDir)) { libFS.mkdirSync(tmpDir, { recursive: true }); }
405
+ libFS.copyFileSync(tmpSource, tmpTarget);
406
+
407
+ let tmpStat = libFS.statSync(tmpTarget);
408
+
409
+ return fCallback(null, {
410
+ EventToFire: 'Done',
411
+ Outputs: {
412
+ FileLocation: tmpTargetLocation,
413
+ FileName: libPath.basename(tmpTarget),
414
+ FilePath: tmpTarget,
415
+ BytesCopied: tmpStat.size
416
+ },
417
+ Log: [`CopyFile: copied ${tmpSource} -> ${tmpTarget}`]
418
+ });
419
+ }
420
+ catch (pError)
421
+ {
422
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`CopyFile: failed: ${pError.message}`] });
423
+ }
424
+ }
425
+ },
426
+
427
+ // ── read-file-buffered ────────────────────────────────────
428
+ {
429
+ Definition: require('./definitions/read-file-buffered.json'),
430
+ Execute: function (pTask, pResolvedSettings, pExecutionContext, fCallback)
431
+ {
432
+ let tmpFilePath = pResolvedSettings.FilePath || '';
433
+ let tmpEncoding = pResolvedSettings.Encoding || 'utf8';
434
+ let tmpMaxBufferSize = parseInt(pResolvedSettings.MaxBufferSize, 10) || 65536;
435
+ let tmpSplitChar = pResolvedSettings.SplitCharacter;
436
+ let tmpByteOffset = parseInt(pResolvedSettings.ByteOffset, 10) || 0;
437
+
438
+ if (tmpSplitChar === undefined || tmpSplitChar === null)
439
+ {
440
+ tmpSplitChar = '\n';
441
+ }
442
+
443
+ if (!tmpFilePath)
444
+ {
445
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: ['ReadFileBuffered: no FilePath specified.'] });
446
+ }
447
+
448
+ tmpFilePath = pTask.resolveFilePath(tmpFilePath, pExecutionContext.StagingPath);
449
+
450
+ try
451
+ {
452
+ let tmpStat = libFS.statSync(tmpFilePath);
453
+ let tmpTotalSize = tmpStat.size;
454
+ let tmpRemainingBytes = tmpTotalSize - tmpByteOffset;
455
+
456
+ if (tmpRemainingBytes <= 0)
457
+ {
458
+ return fCallback(null, {
459
+ EventToFire: 'ReadComplete',
460
+ Outputs: { FileContent: '', BytesRead: 0, ByteOffset: tmpByteOffset, IsComplete: true, FileName: libPath.basename(tmpFilePath), TotalFileSize: tmpTotalSize },
461
+ Log: ['ReadFileBuffered: already at end of file.']
462
+ });
463
+ }
464
+
465
+ let tmpReadSize = Math.min(tmpMaxBufferSize, tmpRemainingBytes);
466
+ let tmpBuffer = Buffer.alloc(tmpReadSize);
467
+ let tmpFd = libFS.openSync(tmpFilePath, 'r');
468
+ let tmpActualBytesRead = libFS.readSync(tmpFd, tmpBuffer, 0, tmpReadSize, tmpByteOffset);
469
+ libFS.closeSync(tmpFd);
470
+
471
+ let tmpContent = tmpBuffer.slice(0, tmpActualBytesRead).toString(tmpEncoding);
472
+ let tmpIsComplete = (tmpByteOffset + tmpActualBytesRead) >= tmpTotalSize;
473
+ let tmpNewOffset = tmpByteOffset + tmpActualBytesRead;
474
+
475
+ // If not at EOF and we have a split character, find the last occurrence
476
+ if (!tmpIsComplete && tmpSplitChar && tmpSplitChar.length > 0)
477
+ {
478
+ let tmpLastSplitIndex = tmpContent.lastIndexOf(tmpSplitChar);
479
+
480
+ if (tmpLastSplitIndex > 0)
481
+ {
482
+ // Keep content up to and including the split character
483
+ tmpContent = tmpContent.substring(0, tmpLastSplitIndex + tmpSplitChar.length);
484
+ tmpNewOffset = tmpByteOffset + Buffer.byteLength(tmpContent, tmpEncoding);
485
+ }
486
+ }
487
+
488
+ let tmpBytesRead = Buffer.byteLength(tmpContent, tmpEncoding);
489
+
490
+ return fCallback(null, {
491
+ EventToFire: 'ReadComplete',
492
+ Outputs: {
493
+ FileContent: tmpContent,
494
+ BytesRead: tmpBytesRead,
495
+ ByteOffset: tmpNewOffset,
496
+ IsComplete: tmpIsComplete,
497
+ FileName: libPath.basename(tmpFilePath),
498
+ TotalFileSize: tmpTotalSize
499
+ },
500
+ Log: [`ReadFileBuffered: read ${tmpBytesRead} bytes from offset ${tmpByteOffset} (${tmpIsComplete ? 'complete' : 'more data available'})`]
501
+ });
502
+ }
503
+ catch (pError)
504
+ {
505
+ return fCallback(null, { EventToFire: 'Error', Outputs: {}, Log: [`ReadFileBuffered: failed: ${pError.message}`] });
506
+ }
507
+ }
508
+ }
509
+ ];
@@ -0,0 +1,96 @@
1
+ const libUltravisorTaskType = require('../Ultravisor-TaskType-Base.cjs');
2
+ const libFS = require('fs');
3
+ const libPath = require('path');
4
+
5
+ /**
6
+ * Read File task type.
7
+ *
8
+ * Reads a file from disk into state. The file path can be absolute or
9
+ * relative to the operation's staging folder.
10
+ */
11
+ class UltravisorTaskTypeReadFile extends libUltravisorTaskType
12
+ {
13
+ constructor(pFable, pOptions, pServiceHash)
14
+ {
15
+ super(pFable, pOptions, pServiceHash);
16
+ }
17
+
18
+ get definition()
19
+ {
20
+ return require('./definitions/read-file.json');
21
+ }
22
+
23
+ execute(pResolvedSettings, pExecutionContext, fCallback, fFireIntermediateEvent)
24
+ {
25
+ let tmpFilePath = pResolvedSettings.FilePath || '';
26
+ let tmpEncoding = pResolvedSettings.Encoding || 'utf8';
27
+ let tmpMaxBytes = parseInt(pResolvedSettings.MaxBytes, 10) || 0;
28
+
29
+ if (!tmpFilePath)
30
+ {
31
+ return fCallback(null, {
32
+ EventToFire: 'Error',
33
+ Outputs: {},
34
+ Log: ['ReadFile: no FilePath specified.']
35
+ });
36
+ }
37
+
38
+ // Resolve relative paths against the staging folder
39
+ tmpFilePath = this.resolveFilePath(tmpFilePath, pExecutionContext.StagingPath);
40
+
41
+ try
42
+ {
43
+ let tmpContent;
44
+
45
+ if (tmpMaxBytes > 0)
46
+ {
47
+ let tmpStat = libFS.statSync(tmpFilePath);
48
+ if (tmpStat.size > tmpMaxBytes)
49
+ {
50
+ let tmpFd = libFS.openSync(tmpFilePath, 'r');
51
+ let tmpBuffer = Buffer.alloc(tmpMaxBytes);
52
+ libFS.readSync(tmpFd, tmpBuffer, 0, tmpMaxBytes, 0);
53
+ libFS.closeSync(tmpFd);
54
+ tmpContent = tmpBuffer.toString(tmpEncoding);
55
+ }
56
+ else
57
+ {
58
+ tmpContent = libFS.readFileSync(tmpFilePath, tmpEncoding);
59
+ }
60
+ }
61
+ else
62
+ {
63
+ tmpContent = libFS.readFileSync(tmpFilePath, tmpEncoding);
64
+ }
65
+
66
+ // Write output to the configured address if specified
67
+ let tmpStateWrites = {};
68
+ if (pResolvedSettings.OutputAddress)
69
+ {
70
+ tmpStateWrites[pResolvedSettings.OutputAddress] = tmpContent;
71
+ }
72
+
73
+ return fCallback(null, {
74
+ EventToFire: 'ReadComplete',
75
+ Outputs:
76
+ {
77
+ FileContent: tmpContent,
78
+ BytesRead: Buffer.byteLength(tmpContent, tmpEncoding),
79
+ FileName: libPath.basename(tmpFilePath)
80
+ },
81
+ StateWrites: tmpStateWrites,
82
+ Log: [`ReadFile: read ${Buffer.byteLength(tmpContent, tmpEncoding)} bytes from ${tmpFilePath}`]
83
+ });
84
+ }
85
+ catch (pError)
86
+ {
87
+ return fCallback(null, {
88
+ EventToFire: 'Error',
89
+ Outputs: {},
90
+ Log: [`ReadFile: failed to read ${tmpFilePath}: ${pError.message}`]
91
+ });
92
+ }
93
+ }
94
+ }
95
+
96
+ module.exports = UltravisorTaskTypeReadFile;