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.
@@ -519,8 +519,12 @@ class UltravisorExecutionManifest extends libPictService
519
519
 
520
520
  let tmpStagingPath = pExecutionContext.StagingPath;
521
521
 
522
- // Always write the manifest
523
- this._writeManifest(pExecutionContext, tmpStagingPath);
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 to disk
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._writeManifest(tmpContext, tmpContext.StagingPath);
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