ultravisor 1.0.25 → 1.0.26
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/docs/_sidebar.md +1 -0
- package/docs/features/persistence-via-databeacon.md +1211 -0
- package/package.json +6 -6
- package/source/cli/Ultravisor-CLIProgram.cjs +62 -0
- package/source/config/Ultravisor-Default-Command-Configuration.cjs +9 -1
- package/source/persistence/UltravisorPersistenceSchema.json +240 -0
- package/source/services/Ultravisor-AuthBeaconBridge.cjs +271 -0
- package/source/services/Ultravisor-Beacon-Coordinator.cjs +242 -149
- package/source/services/Ultravisor-Beacon-Scheduler.cjs +65 -29
- package/source/services/Ultravisor-ExecutionManifest.cjs +99 -4
- package/source/services/Ultravisor-FleetManager.cjs +19 -1
- package/source/services/Ultravisor-ManifestStoreBridge.cjs +1134 -0
- package/source/services/Ultravisor-QueuePersistenceBridge.cjs +1336 -0
- package/source/web_server/Ultravisor-API-Server.cjs +951 -90
- package/webinterface/package.json +1 -0
- package/webinterface/source/Pict-Application-Ultravisor.js +57 -2
- package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +8 -0
- package/webinterface/source/views/PictView-Ultravisor-Login.js +74 -0
- package/webinterface/source/views/PictView-Ultravisor-TopBar.js +25 -0
- package/webinterface/source/views/PictView-Ultravisor-UserManagement.js +159 -0
|
@@ -519,8 +519,12 @@ class UltravisorExecutionManifest extends libPictService
|
|
|
519
519
|
|
|
520
520
|
let tmpStagingPath = pExecutionContext.StagingPath;
|
|
521
521
|
|
|
522
|
-
// Always write the manifest
|
|
523
|
-
|
|
522
|
+
// Always write the manifest. Routes through the ManifestStore
|
|
523
|
+
// bridge so a connected ManifestStore beacon owns the durable
|
|
524
|
+
// copy; the bridge's local fallback calls _writeManifest exactly
|
|
525
|
+
// like the old code path, preserving the on-disk JSON for any
|
|
526
|
+
// deployment that doesn't run the beacon.
|
|
527
|
+
this._persistManifestViaBridge(pExecutionContext, tmpStagingPath);
|
|
524
528
|
|
|
525
529
|
// Write log file
|
|
526
530
|
this._writeLogFile(pExecutionContext, tmpStagingPath);
|
|
@@ -822,10 +826,12 @@ class UltravisorExecutionManifest extends libPictService
|
|
|
822
826
|
|
|
823
827
|
tmpContext.Log.push(`[${new Date().toISOString()}] Run abandoned by user.`);
|
|
824
828
|
|
|
825
|
-
// Persist the updated status
|
|
829
|
+
// Persist the updated status via the bridge (beacon when
|
|
830
|
+
// connected, on-disk JSON otherwise — same shape as the
|
|
831
|
+
// finalize path).
|
|
826
832
|
if (tmpContext.StagingPath)
|
|
827
833
|
{
|
|
828
|
-
this.
|
|
834
|
+
this._persistManifestViaBridge(tmpContext, tmpContext.StagingPath);
|
|
829
835
|
}
|
|
830
836
|
|
|
831
837
|
this.log.info(`UltravisorExecutionManifest: run [${pRunHash}] abandoned.`);
|
|
@@ -872,6 +878,95 @@ class UltravisorExecutionManifest extends libPictService
|
|
|
872
878
|
return tmpCount;
|
|
873
879
|
}
|
|
874
880
|
|
|
881
|
+
// --- Internal: persistence ---
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Bridge between the in-memory execution context and durable
|
|
885
|
+
* storage. Looks up the ManifestStoreBridge service; if present,
|
|
886
|
+
* dispatches the manifest there. Falls through to _writeManifest
|
|
887
|
+
* (on-disk JSON) when no bridge or no beacon. Fire-and-await on
|
|
888
|
+
* the Promise — manifests are written at terminal points
|
|
889
|
+
* (finalize/abandon) so a slow persist doesn't block live work.
|
|
890
|
+
*
|
|
891
|
+
* Why both StagingPath and the bridge call? The local fallback
|
|
892
|
+
* still needs to know where to write the JSON. The beacon path
|
|
893
|
+
* persists the whole manifest blob (StagingPath included as a
|
|
894
|
+
* string), so a future cold-start replay from the beacon can
|
|
895
|
+
* still find the original staging dir if it survives.
|
|
896
|
+
*/
|
|
897
|
+
_persistManifestViaBridge(pExecutionContext, pStagingPath)
|
|
898
|
+
{
|
|
899
|
+
// Build the same serializable manifest _writeManifest builds —
|
|
900
|
+
// the bridge gets the canonical shape, not a partial in-memory
|
|
901
|
+
// view. Keeping the projection here means the beacon and on-
|
|
902
|
+
// disk paths agree byte-for-byte.
|
|
903
|
+
let tmpManifest = this._serializableManifest(pExecutionContext);
|
|
904
|
+
// The local fallback in the bridge reads this field to know
|
|
905
|
+
// where to write. It's set on the in-memory context too —
|
|
906
|
+
// callers don't need to pass it separately.
|
|
907
|
+
tmpManifest.StagingPath = pStagingPath;
|
|
908
|
+
|
|
909
|
+
let tmpMap = this.fable && this.fable.servicesMap
|
|
910
|
+
&& this.fable.servicesMap.UltravisorManifestStoreBridge;
|
|
911
|
+
let tmpBridge = tmpMap ? Object.values(tmpMap)[0] : null;
|
|
912
|
+
if (!tmpBridge)
|
|
913
|
+
{
|
|
914
|
+
// No bridge installed at all — preserve the legacy direct-
|
|
915
|
+
// write behavior so older deployments keep working.
|
|
916
|
+
this._writeManifest(pExecutionContext, pStagingPath);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
tmpBridge.upsertManifest(tmpManifest)
|
|
920
|
+
.then((pResult) =>
|
|
921
|
+
{
|
|
922
|
+
if (pResult && pResult.Success === false && pResult.Reason)
|
|
923
|
+
{
|
|
924
|
+
this.log.warn('UltravisorExecutionManifest: bridge upsert failed: ' + pResult.Reason);
|
|
925
|
+
}
|
|
926
|
+
})
|
|
927
|
+
.catch((pErr) =>
|
|
928
|
+
{
|
|
929
|
+
this.log.warn('UltravisorExecutionManifest: bridge upsert threw: '
|
|
930
|
+
+ ((pErr && pErr.message) || pErr));
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Build the serializable manifest object the bridge ships and
|
|
936
|
+
* _writeManifest writes. Extracted so both paths use exactly the
|
|
937
|
+
* same projection — no risk of one path including a field the
|
|
938
|
+
* other strips.
|
|
939
|
+
*/
|
|
940
|
+
_serializableManifest(pExecutionContext)
|
|
941
|
+
{
|
|
942
|
+
let tmpManifest = {
|
|
943
|
+
Hash: pExecutionContext.Hash,
|
|
944
|
+
OperationHash: pExecutionContext.OperationHash,
|
|
945
|
+
OperationName: pExecutionContext.OperationName,
|
|
946
|
+
Status: pExecutionContext.Status,
|
|
947
|
+
RunMode: pExecutionContext.RunMode,
|
|
948
|
+
StartTime: pExecutionContext.StartTime,
|
|
949
|
+
StopTime: pExecutionContext.StopTime,
|
|
950
|
+
ElapsedMs: pExecutionContext.ElapsedMs,
|
|
951
|
+
Output: pExecutionContext.Output || {},
|
|
952
|
+
TaskManifests: pExecutionContext.TaskManifests,
|
|
953
|
+
TimingSummary: pExecutionContext.TimingSummary,
|
|
954
|
+
EventLog: pExecutionContext.EventLog,
|
|
955
|
+
Errors: pExecutionContext.Errors,
|
|
956
|
+
Log: pExecutionContext.Log,
|
|
957
|
+
GlobalState: pExecutionContext.GlobalState,
|
|
958
|
+
OperationState: pExecutionContext.OperationState,
|
|
959
|
+
TaskOutputs: pExecutionContext.TaskOutputs
|
|
960
|
+
};
|
|
961
|
+
if (pExecutionContext.Status === 'WaitingForInput'
|
|
962
|
+
&& pExecutionContext.WaitingTasks
|
|
963
|
+
&& Object.keys(pExecutionContext.WaitingTasks).length > 0)
|
|
964
|
+
{
|
|
965
|
+
tmpManifest.WaitingTasks = pExecutionContext.WaitingTasks;
|
|
966
|
+
}
|
|
967
|
+
return tmpManifest;
|
|
968
|
+
}
|
|
969
|
+
|
|
875
970
|
// --- Internal: file writing ---
|
|
876
971
|
|
|
877
972
|
_writeManifest(pExecutionContext, pStagingPath)
|
|
@@ -666,12 +666,30 @@ class UltravisorFleetManager extends libPictService
|
|
|
666
666
|
let tmpAvailableModels = [];
|
|
667
667
|
for (let tmpM of this._availableModels.values())
|
|
668
668
|
{
|
|
669
|
+
// Include a slim Actions[] projection so consumers (the lab
|
|
670
|
+
// FleetView, the admin UI, etc.) can group catalog entries
|
|
671
|
+
// into Tool / Capability concepts without a second fetch.
|
|
672
|
+
// Skips pict-only widget metadata to keep the response small.
|
|
673
|
+
let tmpManifestActions = (tmpM.Manifest && tmpM.Manifest.Actions) || [];
|
|
674
|
+
let tmpProjectedActions = [];
|
|
675
|
+
for (let tmpA of tmpManifestActions)
|
|
676
|
+
{
|
|
677
|
+
if (!tmpA || !tmpA.Capability || !tmpA.RemoteAction) continue;
|
|
678
|
+
tmpProjectedActions.push({
|
|
679
|
+
Capability: tmpA.Capability,
|
|
680
|
+
RemoteAction: tmpA.RemoteAction,
|
|
681
|
+
Title: tmpA.Title || '',
|
|
682
|
+
Description: tmpA.Description || '',
|
|
683
|
+
ActionType: tmpA.ActionType || ''
|
|
684
|
+
});
|
|
685
|
+
}
|
|
669
686
|
tmpAvailableModels.push({
|
|
670
687
|
ModelKey: tmpM.ModelKey,
|
|
671
688
|
ModelName: tmpM.ModelName,
|
|
672
689
|
DisplayName: tmpM.DisplayName,
|
|
673
690
|
CatalogName: tmpM.CatalogName,
|
|
674
|
-
ModelSourceDir: tmpM.ModelSourceDir
|
|
691
|
+
ModelSourceDir: tmpM.ModelSourceDir,
|
|
692
|
+
Actions: tmpProjectedActions
|
|
675
693
|
});
|
|
676
694
|
}
|
|
677
695
|
|