ultravisor 1.0.2 → 1.0.4
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.
- package/.claude/launch.json +11 -0
- package/.claude/ultravisor-dev-config.json +3 -0
- package/.ultravisor.json +426 -0
- package/docs/README.md +63 -0
- package/package.json +12 -8
- package/source/Ultravisor.cjs +22 -3
- package/source/cli/Ultravisor-CLIProgram.cjs +35 -23
- package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
- package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
- package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
- package/source/config/Ultravisor-Default-Command-Configuration.cjs +5 -3
- package/source/services/Ultravisor-ExecutionEngine.cjs +1039 -0
- package/source/services/Ultravisor-ExecutionManifest.cjs +399 -0
- package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
- package/source/services/Ultravisor-Hypervisor.cjs +38 -83
- package/source/services/Ultravisor-StateManager.cjs +241 -0
- package/source/services/Ultravisor-TaskTypeRegistry.cjs +143 -0
- package/source/services/tasks/Ultravisor-TaskType-Base.cjs +105 -0
- package/source/services/tasks/control/Ultravisor-TaskType-IfConditional.cjs +148 -0
- package/source/services/tasks/control/Ultravisor-TaskType-LaunchOperation.cjs +187 -0
- package/source/services/tasks/control/Ultravisor-TaskType-SplitExecute.cjs +184 -0
- package/source/services/tasks/data/Ultravisor-TaskType-ReplaceString.cjs +82 -0
- package/source/services/tasks/data/Ultravisor-TaskType-SetValues.cjs +81 -0
- package/source/services/tasks/data/Ultravisor-TaskType-StringAppender.cjs +101 -0
- package/source/services/tasks/file-io/Ultravisor-TaskType-ReadFile.cjs +103 -0
- package/source/services/tasks/file-io/Ultravisor-TaskType-WriteFile.cjs +117 -0
- package/source/services/tasks/interaction/Ultravisor-TaskType-ErrorMessage.cjs +54 -0
- package/source/services/tasks/interaction/Ultravisor-TaskType-ValueInput.cjs +62 -0
- package/source/web_server/Ultravisor-API-Server.cjs +237 -124
- package/test/Ultravisor_browser_tests.js +2226 -0
- package/test/Ultravisor_tests.js +1143 -5830
- package/webinterface/css/ultravisor.css +23 -0
- package/webinterface/package.json +6 -3
- package/webinterface/source/Pict-Application-Ultravisor.js +93 -73
- package/webinterface/source/cards/FlowCard-CSVTransform.js +43 -0
- package/webinterface/source/cards/FlowCard-Command.js +86 -0
- package/webinterface/source/cards/FlowCard-ComprehensionIntersect.js +40 -0
- package/webinterface/source/cards/FlowCard-Conditional.js +87 -0
- package/webinterface/source/cards/FlowCard-CopyFile.js +55 -0
- package/webinterface/source/cards/FlowCard-End.js +29 -0
- package/webinterface/source/cards/FlowCard-GetJSON.js +55 -0
- package/webinterface/source/cards/FlowCard-GetText.js +54 -0
- package/webinterface/source/cards/FlowCard-Histogram.js +176 -0
- package/webinterface/source/cards/FlowCard-LaunchOperation.js +82 -0
- package/webinterface/source/cards/FlowCard-ListFiles.js +55 -0
- package/webinterface/source/cards/FlowCard-MeadowCount.js +44 -0
- package/webinterface/source/cards/FlowCard-MeadowCreate.js +44 -0
- package/webinterface/source/cards/FlowCard-MeadowDelete.js +45 -0
- package/webinterface/source/cards/FlowCard-MeadowRead.js +46 -0
- package/webinterface/source/cards/FlowCard-MeadowReads.js +46 -0
- package/webinterface/source/cards/FlowCard-MeadowUpdate.js +44 -0
- package/webinterface/source/cards/FlowCard-ParseCSV.js +85 -0
- package/webinterface/source/cards/FlowCard-ReadJSON.js +54 -0
- package/webinterface/source/cards/FlowCard-ReadText.js +54 -0
- package/webinterface/source/cards/FlowCard-RestRequest.js +59 -0
- package/webinterface/source/cards/FlowCard-SendJSON.js +57 -0
- package/webinterface/source/cards/FlowCard-Solver.js +77 -0
- package/webinterface/source/cards/FlowCard-Start.js +29 -0
- package/webinterface/source/cards/FlowCard-TemplateString.js +77 -0
- package/webinterface/source/cards/FlowCard-WriteJSON.js +54 -0
- package/webinterface/source/cards/FlowCard-WriteText.js +54 -0
- package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
- package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
- package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
- package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +8 -8
- package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +6 -6
- package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +436 -0
- package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +45 -43
- package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +34 -89
- package/webinterface/source/views/PictView-Ultravisor-OperationList.js +128 -13
- package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
- package/webinterface/source/views/PictView-Ultravisor-Schedule.js +18 -53
- package/webinterface/source/views/PictView-Ultravisor-TimingView.js +27 -14
- package/webinterface/source/views/PictView-Ultravisor-TopBar.js +2 -1
- package/.babelrc +0 -6
- package/.browserslistrc +0 -1
- package/.browserslistrc-BACKUP +0 -1
- package/.gulpfile-quackage-config.json +0 -7
- package/.gulpfile-quackage.js +0 -2
- package/debug/Harness.js +0 -5
- package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
- package/source/services/Ultravisor-Operation.cjs +0 -200
- package/source/services/Ultravisor-Task.cjs +0 -349
- package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
- package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
- package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
- package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
- package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
- package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
- package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
- package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
- package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
- package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
- package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
- package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
- package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
- package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
- package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
- package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
- package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
- package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
- package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
- package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
- package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
- package/webinterface/.babelrc +0 -6
- package/webinterface/.browserslistrc +0 -1
- package/webinterface/.browserslistrc-BACKUP +0 -1
- package/webinterface/.gulpfile-quackage-config.json +0 -7
- package/webinterface/.gulpfile-quackage.js +0 -2
- package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
- package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
- /package/docs/{cover.md → _cover.md} +0 -0
|
@@ -0,0 +1,399 @@
|
|
|
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
|
+
|
|
99
|
+
PendingEvents: [],
|
|
100
|
+
WaitingTasks: {},
|
|
101
|
+
|
|
102
|
+
TaskManifests: {},
|
|
103
|
+
Log: [],
|
|
104
|
+
Errors: [],
|
|
105
|
+
|
|
106
|
+
StartTime: null,
|
|
107
|
+
StopTime: null,
|
|
108
|
+
ElapsedMs: null
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Store in memory
|
|
112
|
+
this._Runs[tmpRunHash] = tmpContext;
|
|
113
|
+
|
|
114
|
+
return tmpContext;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Record a task execution start in the manifest.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} pExecutionContext - The execution context.
|
|
121
|
+
* @param {string} pNodeHash - The task node hash.
|
|
122
|
+
* @param {string} pEventName - The triggering event name.
|
|
123
|
+
*/
|
|
124
|
+
recordTaskStart(pExecutionContext, pNodeHash, pEventName)
|
|
125
|
+
{
|
|
126
|
+
if (!pExecutionContext.TaskManifests[pNodeHash])
|
|
127
|
+
{
|
|
128
|
+
pExecutionContext.TaskManifests[pNodeHash] = {
|
|
129
|
+
NodeHash: pNodeHash,
|
|
130
|
+
Executions: []
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pExecutionContext.TaskManifests[pNodeHash].Executions.push({
|
|
135
|
+
TriggerEvent: pEventName,
|
|
136
|
+
StartTime: new Date().toISOString(),
|
|
137
|
+
StopTime: null,
|
|
138
|
+
Status: 'Running',
|
|
139
|
+
Log: []
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Record a task execution completion in the manifest.
|
|
145
|
+
*
|
|
146
|
+
* @param {object} pExecutionContext - The execution context.
|
|
147
|
+
* @param {string} pNodeHash - The task node hash.
|
|
148
|
+
* @param {object} pResult - The task result (EventToFire, Outputs, Log).
|
|
149
|
+
*/
|
|
150
|
+
recordTaskComplete(pExecutionContext, pNodeHash, pResult)
|
|
151
|
+
{
|
|
152
|
+
let tmpTaskManifest = pExecutionContext.TaskManifests[pNodeHash];
|
|
153
|
+
if (!tmpTaskManifest || tmpTaskManifest.Executions.length === 0)
|
|
154
|
+
{
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let tmpExecution = tmpTaskManifest.Executions[tmpTaskManifest.Executions.length - 1];
|
|
159
|
+
tmpExecution.StopTime = new Date().toISOString();
|
|
160
|
+
tmpExecution.Status = 'Complete';
|
|
161
|
+
tmpExecution.EventFired = pResult.EventToFire || '';
|
|
162
|
+
|
|
163
|
+
if (Array.isArray(pResult.Log))
|
|
164
|
+
{
|
|
165
|
+
tmpExecution.Log = tmpExecution.Log.concat(pResult.Log);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Record a task execution error in the manifest.
|
|
171
|
+
*
|
|
172
|
+
* @param {object} pExecutionContext - The execution context.
|
|
173
|
+
* @param {string} pNodeHash - The task node hash.
|
|
174
|
+
* @param {Error} pError - The error that occurred.
|
|
175
|
+
*/
|
|
176
|
+
recordTaskError(pExecutionContext, pNodeHash, pError)
|
|
177
|
+
{
|
|
178
|
+
let tmpTaskManifest = pExecutionContext.TaskManifests[pNodeHash];
|
|
179
|
+
if (!tmpTaskManifest || tmpTaskManifest.Executions.length === 0)
|
|
180
|
+
{
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let tmpExecution = tmpTaskManifest.Executions[tmpTaskManifest.Executions.length - 1];
|
|
185
|
+
tmpExecution.StopTime = new Date().toISOString();
|
|
186
|
+
tmpExecution.Status = 'Error';
|
|
187
|
+
tmpExecution.Log.push(`[${new Date().toISOString()}] Error: ${pError.message}`);
|
|
188
|
+
|
|
189
|
+
pExecutionContext.Errors.push({
|
|
190
|
+
NodeHash: pNodeHash,
|
|
191
|
+
Message: pError.message,
|
|
192
|
+
Timestamp: tmpExecution.StopTime
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Finalize the execution context after the operation completes.
|
|
198
|
+
* Writes manifest, state snapshots, and logs to the staging folder.
|
|
199
|
+
*
|
|
200
|
+
* @param {object} pExecutionContext - The execution context.
|
|
201
|
+
*/
|
|
202
|
+
finalizeExecution(pExecutionContext)
|
|
203
|
+
{
|
|
204
|
+
pExecutionContext.StopTime = new Date().toISOString();
|
|
205
|
+
pExecutionContext.ElapsedMs = new Date(pExecutionContext.StopTime).getTime()
|
|
206
|
+
- new Date(pExecutionContext.StartTime).getTime();
|
|
207
|
+
|
|
208
|
+
if (pExecutionContext.Errors.length > 0)
|
|
209
|
+
{
|
|
210
|
+
pExecutionContext.Status = 'Error';
|
|
211
|
+
}
|
|
212
|
+
else if (pExecutionContext.Status !== 'WaitingForInput')
|
|
213
|
+
{
|
|
214
|
+
pExecutionContext.Status = 'Complete';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let tmpStagingPath = pExecutionContext.StagingPath;
|
|
218
|
+
|
|
219
|
+
// Always write the manifest
|
|
220
|
+
this._writeManifest(pExecutionContext, tmpStagingPath);
|
|
221
|
+
|
|
222
|
+
// Write log file
|
|
223
|
+
this._writeLogFile(pExecutionContext, tmpStagingPath);
|
|
224
|
+
|
|
225
|
+
// Debug mode: write state snapshots
|
|
226
|
+
if (pExecutionContext.RunMode === 'debug')
|
|
227
|
+
{
|
|
228
|
+
this._writeStateSnapshots(pExecutionContext, tmpStagingPath);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Production mode: clean up working files (but keep manifest and log)
|
|
232
|
+
if (pExecutionContext.RunMode === 'production')
|
|
233
|
+
{
|
|
234
|
+
this._cleanupWorkingFiles(pExecutionContext, tmpStagingPath);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.log.info(`UltravisorExecutionManifest: operation [${pExecutionContext.OperationHash}] run [${pExecutionContext.Hash}] ${pExecutionContext.Status} in ${pExecutionContext.ElapsedMs}ms`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get an execution context by run hash.
|
|
242
|
+
*
|
|
243
|
+
* @param {string} pRunHash - The run hash.
|
|
244
|
+
* @returns {object|null} The execution context, or null if not found.
|
|
245
|
+
*/
|
|
246
|
+
getRun(pRunHash)
|
|
247
|
+
{
|
|
248
|
+
return this._Runs[pRunHash] || null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* List all execution runs.
|
|
253
|
+
*
|
|
254
|
+
* @returns {Array} Array of execution context summaries.
|
|
255
|
+
*/
|
|
256
|
+
listRuns()
|
|
257
|
+
{
|
|
258
|
+
let tmpRuns = [];
|
|
259
|
+
let tmpKeys = Object.keys(this._Runs);
|
|
260
|
+
|
|
261
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
262
|
+
{
|
|
263
|
+
let tmpRun = this._Runs[tmpKeys[i]];
|
|
264
|
+
tmpRuns.push({
|
|
265
|
+
Hash: tmpRun.Hash,
|
|
266
|
+
OperationHash: tmpRun.OperationHash,
|
|
267
|
+
OperationName: tmpRun.OperationName,
|
|
268
|
+
Status: tmpRun.Status,
|
|
269
|
+
RunMode: tmpRun.RunMode,
|
|
270
|
+
StartTime: tmpRun.StartTime,
|
|
271
|
+
StopTime: tmpRun.StopTime,
|
|
272
|
+
ElapsedMs: tmpRun.ElapsedMs,
|
|
273
|
+
ErrorCount: tmpRun.Errors.length,
|
|
274
|
+
StagingPath: tmpRun.StagingPath
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return tmpRuns;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// --- Internal: file writing ---
|
|
282
|
+
|
|
283
|
+
_writeManifest(pExecutionContext, pStagingPath)
|
|
284
|
+
{
|
|
285
|
+
try
|
|
286
|
+
{
|
|
287
|
+
let tmpManifestPath = libPath.resolve(pStagingPath, `Manifest_${pExecutionContext.OperationHash}.json`);
|
|
288
|
+
|
|
289
|
+
// Build a serializable manifest (exclude large state data in production mode)
|
|
290
|
+
let tmpManifest = {
|
|
291
|
+
Hash: pExecutionContext.Hash,
|
|
292
|
+
OperationHash: pExecutionContext.OperationHash,
|
|
293
|
+
OperationName: pExecutionContext.OperationName,
|
|
294
|
+
Status: pExecutionContext.Status,
|
|
295
|
+
RunMode: pExecutionContext.RunMode,
|
|
296
|
+
StartTime: pExecutionContext.StartTime,
|
|
297
|
+
StopTime: pExecutionContext.StopTime,
|
|
298
|
+
ElapsedMs: pExecutionContext.ElapsedMs,
|
|
299
|
+
TaskManifests: pExecutionContext.TaskManifests,
|
|
300
|
+
Errors: pExecutionContext.Errors,
|
|
301
|
+
Log: pExecutionContext.Log
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
libFS.writeFileSync(tmpManifestPath, JSON.stringify(tmpManifest, null, '\t'), 'utf8');
|
|
305
|
+
pExecutionContext.Log.push(`[${new Date().toISOString()}] Manifest written to ${tmpManifestPath}`);
|
|
306
|
+
}
|
|
307
|
+
catch (pError)
|
|
308
|
+
{
|
|
309
|
+
this.log.error(`UltravisorExecutionManifest: failed to write manifest: ${pError.message}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_writeLogFile(pExecutionContext, pStagingPath)
|
|
314
|
+
{
|
|
315
|
+
try
|
|
316
|
+
{
|
|
317
|
+
let tmpLogPath = libPath.resolve(pStagingPath, 'run.log');
|
|
318
|
+
let tmpLogContent = pExecutionContext.Log.join('\n') + '\n';
|
|
319
|
+
libFS.writeFileSync(tmpLogPath, tmpLogContent, 'utf8');
|
|
320
|
+
}
|
|
321
|
+
catch (pError)
|
|
322
|
+
{
|
|
323
|
+
this.log.error(`UltravisorExecutionManifest: failed to write log file: ${pError.message}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
_writeStateSnapshots(pExecutionContext, pStagingPath)
|
|
328
|
+
{
|
|
329
|
+
try
|
|
330
|
+
{
|
|
331
|
+
let tmpStatePath = libPath.resolve(pStagingPath, 'state');
|
|
332
|
+
if (!libFS.existsSync(tmpStatePath))
|
|
333
|
+
{
|
|
334
|
+
libFS.mkdirSync(tmpStatePath, { recursive: true });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Write operation state
|
|
338
|
+
let tmpOpStatePath = libPath.resolve(tmpStatePath, 'operation.json');
|
|
339
|
+
libFS.writeFileSync(tmpOpStatePath, JSON.stringify(pExecutionContext.OperationState, null, '\t'), 'utf8');
|
|
340
|
+
|
|
341
|
+
// Write per-task output state
|
|
342
|
+
let tmpTaskKeys = Object.keys(pExecutionContext.TaskOutputs);
|
|
343
|
+
for (let i = 0; i < tmpTaskKeys.length; i++)
|
|
344
|
+
{
|
|
345
|
+
let tmpNodeHash = tmpTaskKeys[i];
|
|
346
|
+
let tmpTaskStatePath = libPath.resolve(tmpStatePath, `${tmpNodeHash}.json`);
|
|
347
|
+
libFS.writeFileSync(tmpTaskStatePath,
|
|
348
|
+
JSON.stringify(pExecutionContext.TaskOutputs[tmpNodeHash], null, '\t'), 'utf8');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
pExecutionContext.Log.push(`[${new Date().toISOString()}] State snapshots written to ${tmpStatePath}`);
|
|
352
|
+
}
|
|
353
|
+
catch (pError)
|
|
354
|
+
{
|
|
355
|
+
this.log.error(`UltravisorExecutionManifest: failed to write state snapshots: ${pError.message}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
_cleanupWorkingFiles(pExecutionContext, pStagingPath)
|
|
360
|
+
{
|
|
361
|
+
// In production mode, we keep the manifest and log but remove working files.
|
|
362
|
+
// Working files are anything in the staging folder that isn't the manifest or log.
|
|
363
|
+
try
|
|
364
|
+
{
|
|
365
|
+
let tmpFiles = libFS.readdirSync(pStagingPath);
|
|
366
|
+
|
|
367
|
+
for (let i = 0; i < tmpFiles.length; i++)
|
|
368
|
+
{
|
|
369
|
+
let tmpFile = tmpFiles[i];
|
|
370
|
+
|
|
371
|
+
// Keep manifest and log files
|
|
372
|
+
if (tmpFile.startsWith('Manifest_') || tmpFile === 'run.log')
|
|
373
|
+
{
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
let tmpFilePath = libPath.resolve(pStagingPath, tmpFile);
|
|
378
|
+
let tmpStat = libFS.statSync(tmpFilePath);
|
|
379
|
+
|
|
380
|
+
if (tmpStat.isDirectory())
|
|
381
|
+
{
|
|
382
|
+
libFS.rmSync(tmpFilePath, { recursive: true, force: true });
|
|
383
|
+
}
|
|
384
|
+
else
|
|
385
|
+
{
|
|
386
|
+
libFS.unlinkSync(tmpFilePath);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
pExecutionContext.Log.push(`[${new Date().toISOString()}] Working files cleaned up from ${pStagingPath}`);
|
|
391
|
+
}
|
|
392
|
+
catch (pError)
|
|
393
|
+
{
|
|
394
|
+
this.log.error(`UltravisorExecutionManifest: failed to clean up working files: ${pError.message}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
module.exports = UltravisorExecutionManifest;
|