@camstack/addon-pipeline-orchestrator 1.0.3 → 1.0.5
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/dist/_stub.js +2 -2
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-CVaOp6w3.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-B-Qt-xEj.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DXnipYJF.mjs +26 -0
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-KklTiJaw.mjs +26 -0
- package/dist/{hostInit-CaKMGtSL.mjs → hostInit-DUWCx6dX.mjs} +3 -3
- package/dist/index.js +813 -68
- package/dist/index.mjs +813 -68
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DTMQMZQR.mjs +0 -26
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DVwAHhw9.mjs +0 -26
package/dist/index.mjs
CHANGED
|
@@ -4981,6 +4981,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
4981
4981
|
*/
|
|
4982
4982
|
EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
|
|
4983
4983
|
/**
|
|
4984
|
+
* Per-node detection-engine runtime-provisioning transition. Emitted by
|
|
4985
|
+
* the detection-pipeline provider on every state change of its lazy
|
|
4986
|
+
* engine-provisioning machine (idle → installing → verifying → ready,
|
|
4987
|
+
* or → failed with a `nextRetryAt`). Payload is the
|
|
4988
|
+
* `EngineProvisioningState` snapshot; `event.source.nodeId` carries the
|
|
4989
|
+
* node. The Pipeline page subscribes to drive a live "installing
|
|
4990
|
+
* OpenVINO… / ready" indicator per node without polling
|
|
4991
|
+
* `pipelineExecutor.getEngineProvisioning`. Telemetry-grade (D8): the UI
|
|
4992
|
+
* also reads the cap snapshot on mount / reconnect. Phase 2.
|
|
4993
|
+
*/
|
|
4994
|
+
EventCategory["PipelineEngineProvisioning"] = "pipeline.engine-provisioning";
|
|
4995
|
+
/**
|
|
4984
4996
|
* Cluster topology snapshot. Carries the same payload returned by
|
|
4985
4997
|
* `nodes.topology` (every reachable node + addons + processes).
|
|
4986
4998
|
* Emitted by the hub on any agent / addon lifecycle change
|
|
@@ -5130,14 +5142,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5130
5142
|
EventCategory["DeviceSleeping"] = "device.sleeping";
|
|
5131
5143
|
EventCategory["RetentionCleanup"] = "retention.cleanup";
|
|
5132
5144
|
/**
|
|
5133
|
-
*
|
|
5134
|
-
*
|
|
5135
|
-
*
|
|
5136
|
-
* to
|
|
5137
|
-
*
|
|
5138
|
-
* Spec: docs/superpowers/specs/2026-05-21-addons-bulk-update-progress-design.md
|
|
5145
|
+
* Legacy bulk-update progress snapshot (payload `BulkUpdateState`). No longer
|
|
5146
|
+
* emitted — F3 removed the coordinator that produced it; "Update all" now runs
|
|
5147
|
+
* as one lifecycle engine job (`AddonsJobProgress`/`AddonsJobLog`). Retained
|
|
5148
|
+
* (with `BulkUpdateState`) only to avoid regenerating the event maps; removed
|
|
5149
|
+
* in F4 once live bulk progress is re-implemented over the engine events.
|
|
5139
5150
|
*/
|
|
5140
5151
|
EventCategory["AddonsBulkUpdateProgress"] = "addons.bulk-update-progress";
|
|
5152
|
+
EventCategory["AddonsJobProgress"] = "addons.job-progress";
|
|
5153
|
+
EventCategory["AddonsJobLog"] = "addons.job-log";
|
|
5141
5154
|
/**
|
|
5142
5155
|
* A container's child visibility toggled (hidden/shown). Emitted by the
|
|
5143
5156
|
* `accessories` cap when a child device is hidden or revealed.
|
|
@@ -9561,6 +9574,24 @@ var DetectorOutputSchema = object({
|
|
|
9561
9574
|
inferenceMs: number(),
|
|
9562
9575
|
modelId: string()
|
|
9563
9576
|
});
|
|
9577
|
+
var EngineProvisioningSchema = object({
|
|
9578
|
+
runtimeId: _enum([
|
|
9579
|
+
"onnx",
|
|
9580
|
+
"openvino",
|
|
9581
|
+
"coreml"
|
|
9582
|
+
]).nullable(),
|
|
9583
|
+
device: string().nullable(),
|
|
9584
|
+
state: _enum([
|
|
9585
|
+
"idle",
|
|
9586
|
+
"installing",
|
|
9587
|
+
"verifying",
|
|
9588
|
+
"ready",
|
|
9589
|
+
"failed"
|
|
9590
|
+
]),
|
|
9591
|
+
progress: number().optional(),
|
|
9592
|
+
error: string().optional(),
|
|
9593
|
+
nextRetryAt: number().optional()
|
|
9594
|
+
});
|
|
9564
9595
|
var PipelineStepInputSchema = lazy(() => object({
|
|
9565
9596
|
addonId: string(),
|
|
9566
9597
|
modelId: string(),
|
|
@@ -9660,6 +9691,15 @@ var pipelineExecutorCapability = {
|
|
|
9660
9691
|
kind: "mutation",
|
|
9661
9692
|
auth: "admin"
|
|
9662
9693
|
}),
|
|
9694
|
+
/**
|
|
9695
|
+
* Per-node detection-engine provisioning snapshot. Returns the live
|
|
9696
|
+
* state of the lazy runtime-provisioning machine on `nodeId`
|
|
9697
|
+
* (idle / installing / verifying / ready / failed). The UI pairs this
|
|
9698
|
+
* one-shot query with the `pipeline.engine-provisioning` live event
|
|
9699
|
+
* (emitted on every transition) to drive a per-node "engine ready?"
|
|
9700
|
+
* indicator without polling. Phase 2.
|
|
9701
|
+
*/
|
|
9702
|
+
getEngineProvisioning: method(object({ nodeId: string() }), EngineProvisioningSchema),
|
|
9663
9703
|
getVideoPipelineSteps: method(_void(), record(string(), object({
|
|
9664
9704
|
modelId: string(),
|
|
9665
9705
|
settings: record(string(), unknown()).readonly()
|
|
@@ -13536,7 +13576,10 @@ var AgentAddonConfigSchema = object({
|
|
|
13536
13576
|
modelId: string(),
|
|
13537
13577
|
settings: record(string(), unknown()).readonly()
|
|
13538
13578
|
});
|
|
13539
|
-
var AgentPipelineSettingsSchema = object({
|
|
13579
|
+
var AgentPipelineSettingsSchema = object({
|
|
13580
|
+
addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
|
|
13581
|
+
maxCameras: number().int().nonnegative().nullable().default(null)
|
|
13582
|
+
});
|
|
13540
13583
|
var CameraPipelineForAgentSchema = object({
|
|
13541
13584
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
13542
13585
|
audio: object({
|
|
@@ -13638,6 +13681,133 @@ var GlobalMetricsSchema = object({
|
|
|
13638
13681
|
* capability providers.
|
|
13639
13682
|
*/
|
|
13640
13683
|
var CapabilityBindingsSchema = record(string(), string());
|
|
13684
|
+
/** Source block — always present; derives from the stream catalog. */
|
|
13685
|
+
var CameraSourceStatusSchema = object({ streams: array(object({
|
|
13686
|
+
camStreamId: string(),
|
|
13687
|
+
codec: string(),
|
|
13688
|
+
width: number(),
|
|
13689
|
+
height: number(),
|
|
13690
|
+
fps: number(),
|
|
13691
|
+
kind: string()
|
|
13692
|
+
})).readonly() });
|
|
13693
|
+
/** Assignment block — always present (orchestrator-local, no remote call). */
|
|
13694
|
+
var CameraAssignmentStatusSchema = object({
|
|
13695
|
+
detectionNodeId: string().nullable(),
|
|
13696
|
+
decoderNodeId: string().nullable(),
|
|
13697
|
+
audioNodeId: string().nullable(),
|
|
13698
|
+
pinned: object({
|
|
13699
|
+
detection: boolean(),
|
|
13700
|
+
decoder: boolean(),
|
|
13701
|
+
audio: boolean()
|
|
13702
|
+
}),
|
|
13703
|
+
reasons: object({
|
|
13704
|
+
detection: string().optional(),
|
|
13705
|
+
decoder: string().optional(),
|
|
13706
|
+
audio: string().optional()
|
|
13707
|
+
})
|
|
13708
|
+
});
|
|
13709
|
+
/** Broker block — null when the broker stage is unreachable or inactive. */
|
|
13710
|
+
var CameraBrokerStatusSchema = object({
|
|
13711
|
+
profiles: array(object({
|
|
13712
|
+
profile: string(),
|
|
13713
|
+
status: string(),
|
|
13714
|
+
codec: string(),
|
|
13715
|
+
width: number(),
|
|
13716
|
+
height: number(),
|
|
13717
|
+
subscribers: number(),
|
|
13718
|
+
inFps: number(),
|
|
13719
|
+
outFps: number()
|
|
13720
|
+
})).readonly(),
|
|
13721
|
+
webrtcSessions: number(),
|
|
13722
|
+
rtspRestream: boolean()
|
|
13723
|
+
});
|
|
13724
|
+
/** Shared-memory ring statistics within the decoder block. */
|
|
13725
|
+
var CameraDecoderShmSchema = object({
|
|
13726
|
+
framesWritten: number(),
|
|
13727
|
+
getFrameHits: number(),
|
|
13728
|
+
getFrameMisses: number(),
|
|
13729
|
+
budgetMb: number()
|
|
13730
|
+
});
|
|
13731
|
+
/** Decoder block — null when the decoder stage is unreachable or inactive. */
|
|
13732
|
+
var CameraDecoderStatusSchema = object({
|
|
13733
|
+
nodeId: string(),
|
|
13734
|
+
formats: array(string()).readonly(),
|
|
13735
|
+
sessionCount: number(),
|
|
13736
|
+
shm: CameraDecoderShmSchema
|
|
13737
|
+
});
|
|
13738
|
+
/** Motion block — null when motion detection is not active for this device. */
|
|
13739
|
+
var CameraMotionStatusSchema = object({
|
|
13740
|
+
enabled: boolean(),
|
|
13741
|
+
fps: number()
|
|
13742
|
+
});
|
|
13743
|
+
/** Detection provisioning sub-block. */
|
|
13744
|
+
var CameraDetectionProvisioningSchema = object({
|
|
13745
|
+
state: _enum([
|
|
13746
|
+
"idle",
|
|
13747
|
+
"installing",
|
|
13748
|
+
"verifying",
|
|
13749
|
+
"ready",
|
|
13750
|
+
"failed"
|
|
13751
|
+
]),
|
|
13752
|
+
error: string().optional()
|
|
13753
|
+
});
|
|
13754
|
+
/** Detection phase — derived from the runner's engine phase. */
|
|
13755
|
+
var CameraDetectionPhaseSchema = _enum([
|
|
13756
|
+
"idle",
|
|
13757
|
+
"watching",
|
|
13758
|
+
"active"
|
|
13759
|
+
]);
|
|
13760
|
+
/** Detection block — null when no detection node is assigned or reachable. */
|
|
13761
|
+
var CameraDetectionStatusSchema = object({
|
|
13762
|
+
nodeId: string(),
|
|
13763
|
+
engine: object({
|
|
13764
|
+
backend: string(),
|
|
13765
|
+
device: string()
|
|
13766
|
+
}),
|
|
13767
|
+
phase: CameraDetectionPhaseSchema,
|
|
13768
|
+
configuredFps: number(),
|
|
13769
|
+
actualFps: number(),
|
|
13770
|
+
queueDepth: number(),
|
|
13771
|
+
avgInferenceMs: number(),
|
|
13772
|
+
provisioning: CameraDetectionProvisioningSchema
|
|
13773
|
+
});
|
|
13774
|
+
/** Audio block — null when no audio node is assigned or reachable. */
|
|
13775
|
+
var CameraAudioStatusSchema = object({
|
|
13776
|
+
nodeId: string(),
|
|
13777
|
+
enabled: boolean()
|
|
13778
|
+
});
|
|
13779
|
+
/** Recording block — null when no recording cap is active for this device. */
|
|
13780
|
+
var CameraRecordingStatusSchema = object({
|
|
13781
|
+
mode: _enum([
|
|
13782
|
+
"off",
|
|
13783
|
+
"continuous",
|
|
13784
|
+
"events"
|
|
13785
|
+
]),
|
|
13786
|
+
active: boolean(),
|
|
13787
|
+
storageBytes: number()
|
|
13788
|
+
});
|
|
13789
|
+
/**
|
|
13790
|
+
* Aggregated per-camera pipeline status — server-composed, single call.
|
|
13791
|
+
*
|
|
13792
|
+
* The `assignment` and `source` blocks are always present.
|
|
13793
|
+
* Every other block is `null` when the stage is inactive or unreachable
|
|
13794
|
+
* during the bounded parallel fan-out in the orchestrator implementation.
|
|
13795
|
+
*
|
|
13796
|
+
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
13797
|
+
*/
|
|
13798
|
+
var CameraStatusSchema = object({
|
|
13799
|
+
deviceId: number(),
|
|
13800
|
+
assignment: CameraAssignmentStatusSchema,
|
|
13801
|
+
source: CameraSourceStatusSchema,
|
|
13802
|
+
broker: CameraBrokerStatusSchema.nullable(),
|
|
13803
|
+
decoder: CameraDecoderStatusSchema.nullable(),
|
|
13804
|
+
motion: CameraMotionStatusSchema.nullable(),
|
|
13805
|
+
detection: CameraDetectionStatusSchema.nullable(),
|
|
13806
|
+
audio: CameraAudioStatusSchema.nullable(),
|
|
13807
|
+
recording: CameraRecordingStatusSchema.nullable(),
|
|
13808
|
+
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
13809
|
+
fetchedAt: number()
|
|
13810
|
+
});
|
|
13641
13811
|
/**
|
|
13642
13812
|
* Pipeline Orchestrator capability — global load balancer + camera dispatcher.
|
|
13643
13813
|
*
|
|
@@ -13814,6 +13984,25 @@ var pipelineOrchestratorCapability = {
|
|
|
13814
13984
|
kind: "mutation",
|
|
13815
13985
|
auth: "admin"
|
|
13816
13986
|
}),
|
|
13987
|
+
/**
|
|
13988
|
+
* Set the per-node camera cap for one agent.
|
|
13989
|
+
*
|
|
13990
|
+
* `maxCameras: 0` and `maxCameras: null` are both treated as unlimited
|
|
13991
|
+
* by the load balancer (0 is stored as-is; the balancer treats ≤0 as
|
|
13992
|
+
* unlimited alongside null). Pass `null` to clear an existing cap.
|
|
13993
|
+
* Note: the admin UI normalises 0→null before calling this method, so
|
|
13994
|
+
* `maxCameras: 0` only reaches the store via direct SDK or CLI calls.
|
|
13995
|
+
* Changes take effect immediately on the next dispatch cycle — cameras
|
|
13996
|
+
* currently assigned over-cap are left in place (no forced eviction),
|
|
13997
|
+
* but new assignments obey the updated cap.
|
|
13998
|
+
*/
|
|
13999
|
+
setAgentMaxCameras: method(object({
|
|
14000
|
+
agentNodeId: string(),
|
|
14001
|
+
maxCameras: number().int().nonnegative().nullable()
|
|
14002
|
+
}), object({ success: literal(true) }), {
|
|
14003
|
+
kind: "mutation",
|
|
14004
|
+
auth: "admin"
|
|
14005
|
+
}),
|
|
13817
14006
|
/** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
|
|
13818
14007
|
getCameraSettings: method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()),
|
|
13819
14008
|
/** Set or clear the 3-state toggle for one (camera, addonId). Pass `enabled: null` to clear and revert to agent default. */
|
|
@@ -13854,6 +14043,29 @@ var pipelineOrchestratorCapability = {
|
|
|
13854
14043
|
deviceId: number(),
|
|
13855
14044
|
agentNodeId: string().optional()
|
|
13856
14045
|
}), CameraPipelineConfigSchema),
|
|
14046
|
+
/**
|
|
14047
|
+
* Server-composed aggregated status for a single camera.
|
|
14048
|
+
*
|
|
14049
|
+
* Fans out in parallel (bounded, per-stage graceful degradation) to
|
|
14050
|
+
* broker / decoder / motion / detection / audio / recording source caps
|
|
14051
|
+
* via `ctx.api`. A stage whose source errors or times out becomes `null`
|
|
14052
|
+
* in the returned payload — one slow agent never breaks the whole call.
|
|
14053
|
+
*
|
|
14054
|
+
* Intended for "on open / on camera select" snapshots. Live deltas
|
|
14055
|
+
* keep arriving from the existing ~1Hz events the UI already subscribes to.
|
|
14056
|
+
*/
|
|
14057
|
+
getCameraStatus: method(object({ deviceId: number() }), CameraStatusSchema),
|
|
14058
|
+
/**
|
|
14059
|
+
* Server-composed aggregated status for multiple cameras in one call.
|
|
14060
|
+
*
|
|
14061
|
+
* `deviceIds` defaults to all cameras currently tracked by the
|
|
14062
|
+
* orchestrator's assignment map when omitted.
|
|
14063
|
+
*
|
|
14064
|
+
* Runs per-device composition in parallel (bounded). Use this to
|
|
14065
|
+
* populate the cluster assignments table and the pipeline flow overview
|
|
14066
|
+
* rail without issuing N parallel browser round-trips.
|
|
14067
|
+
*/
|
|
14068
|
+
getCameraStatuses: method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()),
|
|
13857
14069
|
/** List every template the operator has saved. */
|
|
13858
14070
|
listTemplates: method(_void(), array(PipelineTemplateSchema).readonly()),
|
|
13859
14071
|
/** Create a new named preset from a given CameraPipelineConfig. */
|
|
@@ -16865,6 +17077,69 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
16865
17077
|
kind: "mutation",
|
|
16866
17078
|
auth: "admin"
|
|
16867
17079
|
});
|
|
17080
|
+
var jobKindSchema = _enum([
|
|
17081
|
+
"install",
|
|
17082
|
+
"update",
|
|
17083
|
+
"uninstall",
|
|
17084
|
+
"restart"
|
|
17085
|
+
]);
|
|
17086
|
+
var taskPhaseSchema = _enum([
|
|
17087
|
+
"queued",
|
|
17088
|
+
"fetching",
|
|
17089
|
+
"staged",
|
|
17090
|
+
"validating",
|
|
17091
|
+
"applying",
|
|
17092
|
+
"restarting",
|
|
17093
|
+
"applied",
|
|
17094
|
+
"done",
|
|
17095
|
+
"failed",
|
|
17096
|
+
"skipped"
|
|
17097
|
+
]);
|
|
17098
|
+
var taskTargetSchema = _enum(["framework", "addon"]);
|
|
17099
|
+
var taskLogEntrySchema = object({
|
|
17100
|
+
tsMs: number(),
|
|
17101
|
+
nodeId: string(),
|
|
17102
|
+
packageName: string(),
|
|
17103
|
+
phase: taskPhaseSchema,
|
|
17104
|
+
message: string()
|
|
17105
|
+
});
|
|
17106
|
+
var lifecycleTaskSchema = object({
|
|
17107
|
+
taskId: string(),
|
|
17108
|
+
nodeId: string(),
|
|
17109
|
+
packageName: string(),
|
|
17110
|
+
fromVersion: string().nullable(),
|
|
17111
|
+
toVersion: string(),
|
|
17112
|
+
target: taskTargetSchema,
|
|
17113
|
+
phase: taskPhaseSchema,
|
|
17114
|
+
stagedPath: string().nullable(),
|
|
17115
|
+
attempts: number(),
|
|
17116
|
+
steps: array(taskLogEntrySchema),
|
|
17117
|
+
error: string().nullable(),
|
|
17118
|
+
startedAtMs: number().nullable(),
|
|
17119
|
+
finishedAtMs: number().nullable()
|
|
17120
|
+
});
|
|
17121
|
+
var lifecycleJobStateSchema = _enum([
|
|
17122
|
+
"running",
|
|
17123
|
+
"completed",
|
|
17124
|
+
"failed",
|
|
17125
|
+
"partially-failed",
|
|
17126
|
+
"cancelled"
|
|
17127
|
+
]);
|
|
17128
|
+
var lifecycleJobScopeSchema = _enum([
|
|
17129
|
+
"single",
|
|
17130
|
+
"bulk",
|
|
17131
|
+
"cluster"
|
|
17132
|
+
]);
|
|
17133
|
+
var lifecycleJobSchema = object({
|
|
17134
|
+
jobId: string(),
|
|
17135
|
+
kind: jobKindSchema,
|
|
17136
|
+
createdAtMs: number(),
|
|
17137
|
+
createdBy: string(),
|
|
17138
|
+
scope: lifecycleJobScopeSchema,
|
|
17139
|
+
tasks: array(lifecycleTaskSchema),
|
|
17140
|
+
state: lifecycleJobStateSchema,
|
|
17141
|
+
schemaVersion: literal(1)
|
|
17142
|
+
});
|
|
16868
17143
|
/**
|
|
16869
17144
|
* addons — system-scoped singleton capability for addon package
|
|
16870
17145
|
* management (install, update, configure, restart) and per-addon log
|
|
@@ -17047,7 +17322,7 @@ var BulkUpdatePhaseSchema = _enum([
|
|
|
17047
17322
|
"restarting",
|
|
17048
17323
|
"finalizing"
|
|
17049
17324
|
]);
|
|
17050
|
-
|
|
17325
|
+
object({
|
|
17051
17326
|
id: string(),
|
|
17052
17327
|
nodeId: string(),
|
|
17053
17328
|
startedAtMs: number(),
|
|
@@ -17150,20 +17425,7 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
17150
17425
|
}), UpdateFrameworkPackageResultSchema, {
|
|
17151
17426
|
kind: "mutation",
|
|
17152
17427
|
auth: "admin"
|
|
17153
|
-
}), method(object({
|
|
17154
|
-
nodeId: string(),
|
|
17155
|
-
items: array(object({
|
|
17156
|
-
name: string(),
|
|
17157
|
-
version: string(),
|
|
17158
|
-
isSystem: boolean()
|
|
17159
|
-
})).readonly()
|
|
17160
|
-
}), object({ id: string() }), {
|
|
17161
|
-
kind: "mutation",
|
|
17162
|
-
auth: "admin"
|
|
17163
|
-
}), method(object({ id: string() }), BulkUpdateStateSchema.nullable(), { auth: "admin" }), method(object({ id: string() }), object({ cancelled: boolean() }), {
|
|
17164
|
-
kind: "mutation",
|
|
17165
|
-
auth: "admin"
|
|
17166
|
-
}), method(object({ nodeId: string().optional() }), array(BulkUpdateStateSchema).readonly(), { auth: "admin" }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
17428
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
17167
17429
|
kind: "mutation",
|
|
17168
17430
|
auth: "admin"
|
|
17169
17431
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -17185,6 +17447,24 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
17185
17447
|
kind: "mutation",
|
|
17186
17448
|
auth: "admin"
|
|
17187
17449
|
}), method(CustomActionInputSchema, unknown(), { kind: "mutation" }), method(object({
|
|
17450
|
+
kind: _enum([
|
|
17451
|
+
"install",
|
|
17452
|
+
"update",
|
|
17453
|
+
"uninstall",
|
|
17454
|
+
"restart"
|
|
17455
|
+
]),
|
|
17456
|
+
targets: array(object({
|
|
17457
|
+
name: string().min(1),
|
|
17458
|
+
version: string().min(1)
|
|
17459
|
+
})).min(1),
|
|
17460
|
+
nodeIds: array(string()).optional()
|
|
17461
|
+
}), object({ jobId: string() }), {
|
|
17462
|
+
kind: "mutation",
|
|
17463
|
+
auth: "admin"
|
|
17464
|
+
}), method(object({ jobId: string() }), lifecycleJobSchema.nullable(), { auth: "admin" }), method(object({ activeOnly: boolean().optional() }), array(lifecycleJobSchema), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
17465
|
+
kind: "mutation",
|
|
17466
|
+
auth: "admin"
|
|
17467
|
+
}), method(object({
|
|
17188
17468
|
addonId: string(),
|
|
17189
17469
|
level: LogLevelSchema$1.optional()
|
|
17190
17470
|
}), LogStreamEntrySchema, { kind: "subscription" });
|
|
@@ -17225,7 +17505,7 @@ Object.freeze({
|
|
|
17225
17505
|
addonId: null,
|
|
17226
17506
|
access: "create"
|
|
17227
17507
|
},
|
|
17228
|
-
"addons.
|
|
17508
|
+
"addons.cancelJob": {
|
|
17229
17509
|
capName: "addons",
|
|
17230
17510
|
capScope: "system",
|
|
17231
17511
|
addonId: null,
|
|
@@ -17255,7 +17535,7 @@ Object.freeze({
|
|
|
17255
17535
|
addonId: null,
|
|
17256
17536
|
access: "view"
|
|
17257
17537
|
},
|
|
17258
|
-
"addons.
|
|
17538
|
+
"addons.getJob": {
|
|
17259
17539
|
capName: "addons",
|
|
17260
17540
|
capScope: "system",
|
|
17261
17541
|
addonId: null,
|
|
@@ -17303,19 +17583,19 @@ Object.freeze({
|
|
|
17303
17583
|
addonId: null,
|
|
17304
17584
|
access: "view"
|
|
17305
17585
|
},
|
|
17306
|
-
"addons.
|
|
17586
|
+
"addons.listCapabilityProviders": {
|
|
17307
17587
|
capName: "addons",
|
|
17308
17588
|
capScope: "system",
|
|
17309
17589
|
addonId: null,
|
|
17310
17590
|
access: "view"
|
|
17311
17591
|
},
|
|
17312
|
-
"addons.
|
|
17592
|
+
"addons.listFrameworkPackages": {
|
|
17313
17593
|
capName: "addons",
|
|
17314
17594
|
capScope: "system",
|
|
17315
17595
|
addonId: null,
|
|
17316
17596
|
access: "view"
|
|
17317
17597
|
},
|
|
17318
|
-
"addons.
|
|
17598
|
+
"addons.listJobs": {
|
|
17319
17599
|
capName: "addons",
|
|
17320
17600
|
capScope: "system",
|
|
17321
17601
|
addonId: null,
|
|
@@ -17399,7 +17679,7 @@ Object.freeze({
|
|
|
17399
17679
|
addonId: null,
|
|
17400
17680
|
access: "create"
|
|
17401
17681
|
},
|
|
17402
|
-
"addons.
|
|
17682
|
+
"addons.startJob": {
|
|
17403
17683
|
capName: "addons",
|
|
17404
17684
|
capScope: "system",
|
|
17405
17685
|
addonId: null,
|
|
@@ -19625,6 +19905,12 @@ Object.freeze({
|
|
|
19625
19905
|
addonId: null,
|
|
19626
19906
|
access: "view"
|
|
19627
19907
|
},
|
|
19908
|
+
"pipelineExecutor.getEngineProvisioning": {
|
|
19909
|
+
capName: "pipeline-executor",
|
|
19910
|
+
capScope: "system",
|
|
19911
|
+
addonId: null,
|
|
19912
|
+
access: "view"
|
|
19913
|
+
},
|
|
19628
19914
|
"pipelineExecutor.getGlobalPipelineConfig": {
|
|
19629
19915
|
capName: "pipeline-executor",
|
|
19630
19916
|
capScope: "system",
|
|
@@ -19829,6 +20115,18 @@ Object.freeze({
|
|
|
19829
20115
|
addonId: null,
|
|
19830
20116
|
access: "view"
|
|
19831
20117
|
},
|
|
20118
|
+
"pipelineOrchestrator.getCameraStatus": {
|
|
20119
|
+
capName: "pipeline-orchestrator",
|
|
20120
|
+
capScope: "system",
|
|
20121
|
+
addonId: null,
|
|
20122
|
+
access: "view"
|
|
20123
|
+
},
|
|
20124
|
+
"pipelineOrchestrator.getCameraStatuses": {
|
|
20125
|
+
capName: "pipeline-orchestrator",
|
|
20126
|
+
capScope: "system",
|
|
20127
|
+
addonId: null,
|
|
20128
|
+
access: "view"
|
|
20129
|
+
},
|
|
19832
20130
|
"pipelineOrchestrator.getCameraStepOverrides": {
|
|
19833
20131
|
capName: "pipeline-orchestrator",
|
|
19834
20132
|
capScope: "system",
|
|
@@ -19913,6 +20211,12 @@ Object.freeze({
|
|
|
19913
20211
|
addonId: null,
|
|
19914
20212
|
access: "create"
|
|
19915
20213
|
},
|
|
20214
|
+
"pipelineOrchestrator.setAgentMaxCameras": {
|
|
20215
|
+
capName: "pipeline-orchestrator",
|
|
20216
|
+
capScope: "system",
|
|
20217
|
+
addonId: null,
|
|
20218
|
+
access: "create"
|
|
20219
|
+
},
|
|
19916
20220
|
"pipelineOrchestrator.setCameraPipelineForAgent": {
|
|
19917
20221
|
capName: "pipeline-orchestrator",
|
|
19918
20222
|
capScope: "system",
|
|
@@ -21386,6 +21690,32 @@ Object.freeze({
|
|
|
21386
21690
|
"network-access": "ingress",
|
|
21387
21691
|
"smtp-provider": "email"
|
|
21388
21692
|
});
|
|
21693
|
+
var frameworkSwapPackageSchema = object({
|
|
21694
|
+
name: string(),
|
|
21695
|
+
stagedPath: string(),
|
|
21696
|
+
backupPath: string(),
|
|
21697
|
+
toVersion: string(),
|
|
21698
|
+
fromVersion: string().nullable()
|
|
21699
|
+
});
|
|
21700
|
+
object({
|
|
21701
|
+
jobId: string(),
|
|
21702
|
+
taskId: string(),
|
|
21703
|
+
packages: array(frameworkSwapPackageSchema),
|
|
21704
|
+
requestedAtMs: number(),
|
|
21705
|
+
schemaVersion: literal(1)
|
|
21706
|
+
});
|
|
21707
|
+
object({
|
|
21708
|
+
jobId: string(),
|
|
21709
|
+
taskId: string(),
|
|
21710
|
+
backups: array(object({
|
|
21711
|
+
name: string(),
|
|
21712
|
+
backupPath: string(),
|
|
21713
|
+
livePath: string()
|
|
21714
|
+
})),
|
|
21715
|
+
appliedAtMs: number(),
|
|
21716
|
+
bootAttempts: number(),
|
|
21717
|
+
schemaVersion: literal(1)
|
|
21718
|
+
});
|
|
21389
21719
|
/**
|
|
21390
21720
|
* Promise-based timer helpers — used everywhere the codebase needs to
|
|
21391
21721
|
* wait, back off, or schedule a retry. Before these helpers landed, each
|
|
@@ -21917,7 +22247,10 @@ var StoredAgentAddonConfigSchema = object({
|
|
|
21917
22247
|
modelId: string(),
|
|
21918
22248
|
settings: record(string(), unknown()).readonly()
|
|
21919
22249
|
});
|
|
21920
|
-
var StoredAgentPipelineSettingsSchema = object({
|
|
22250
|
+
var StoredAgentPipelineSettingsSchema = object({
|
|
22251
|
+
addonDefaults: record(string(), StoredAgentAddonConfigSchema).readonly(),
|
|
22252
|
+
maxCameras: number().int().nonnegative().nullable().default(null)
|
|
22253
|
+
});
|
|
21921
22254
|
var AgentSettingsMapSchema = record(string(), StoredAgentPipelineSettingsSchema);
|
|
21922
22255
|
var StoredCameraStepOverridePatchSchema = object({
|
|
21923
22256
|
enabled: boolean().optional(),
|
|
@@ -21962,10 +22295,26 @@ function computeCapacityScore(load) {
|
|
|
21962
22295
|
return load.attachedCameras * Math.max(load.avgInferenceFps, 0) + Math.max(load.queueDepthTotal, 0);
|
|
21963
22296
|
}
|
|
21964
22297
|
/**
|
|
22298
|
+
* Returns true when the node has remaining capacity.
|
|
22299
|
+
* A node is eligible iff `cap` is unlimited (null/absent/<=0) OR
|
|
22300
|
+
* its `attachedCameras` count is strictly less than the cap.
|
|
22301
|
+
* Pins count toward the cap via `attachedCameras`.
|
|
22302
|
+
*/
|
|
22303
|
+
function isEligible(node, caps) {
|
|
22304
|
+
const cap = caps?.[node.nodeId];
|
|
22305
|
+
if (cap === null || cap === void 0 || cap <= 0) return true;
|
|
22306
|
+
return node.attachedCameras < cap;
|
|
22307
|
+
}
|
|
22308
|
+
/**
|
|
21965
22309
|
* Run the two-level camera balancer.
|
|
21966
22310
|
*
|
|
21967
|
-
* L1 (manual affinity): if `preferredAgent` names an online node
|
|
21968
|
-
*
|
|
22311
|
+
* L1 (manual affinity): if `preferredAgent` names an online node AND that
|
|
22312
|
+
* node is under its `maxCameras` cap, return it. If the node is online but at
|
|
22313
|
+
* or over cap, return `{kind:'pending'}` — a pinned camera is never silently
|
|
22314
|
+
* over-assigned.
|
|
22315
|
+
*
|
|
22316
|
+
* L2 (capacity): filter to eligible nodes and pick the lowest capacity score.
|
|
22317
|
+
* If all nodes are at/over cap, return `{kind:'pending'}`.
|
|
21969
22318
|
*
|
|
21970
22319
|
* Returns `null` when no runners are online. The orchestrator decides how to
|
|
21971
22320
|
* react — typically by logging and deferring the assignment until a runner
|
|
@@ -21974,19 +22323,32 @@ function computeCapacityScore(load) {
|
|
|
21974
22323
|
function balance(input) {
|
|
21975
22324
|
const online = input.nodes.filter((n) => n.nodeId.length > 0);
|
|
21976
22325
|
if (online.length === 0) return null;
|
|
22326
|
+
const eligible = online.filter((n) => isEligible(n, input.nodeCaps));
|
|
21977
22327
|
if (input.preferredAgent) {
|
|
21978
|
-
const
|
|
21979
|
-
if (
|
|
21980
|
-
|
|
21981
|
-
|
|
21982
|
-
|
|
21983
|
-
|
|
22328
|
+
const pinnedOnline = online.find((n) => n.nodeId === input.preferredAgent);
|
|
22329
|
+
if (pinnedOnline) {
|
|
22330
|
+
if (eligible.some((n) => n.nodeId === pinnedOnline.nodeId)) return {
|
|
22331
|
+
kind: "assigned",
|
|
22332
|
+
agentNodeId: pinnedOnline.nodeId,
|
|
22333
|
+
reason: "manual",
|
|
22334
|
+
score: computeCapacityScore(pinnedOnline)
|
|
22335
|
+
};
|
|
22336
|
+
return {
|
|
22337
|
+
kind: "pending",
|
|
22338
|
+
reason: "over-cap"
|
|
22339
|
+
};
|
|
22340
|
+
}
|
|
21984
22341
|
}
|
|
21985
|
-
|
|
22342
|
+
if (eligible.length === 0) return {
|
|
22343
|
+
kind: "pending",
|
|
22344
|
+
reason: "over-cap"
|
|
22345
|
+
};
|
|
22346
|
+
const best = eligible.map((node) => ({
|
|
21986
22347
|
node,
|
|
21987
22348
|
score: computeCapacityScore(node)
|
|
21988
22349
|
})).toSorted((a, b) => a.score - b.score)[0];
|
|
21989
22350
|
return {
|
|
22351
|
+
kind: "assigned",
|
|
21990
22352
|
agentNodeId: best.node.nodeId,
|
|
21991
22353
|
reason: "capacity",
|
|
21992
22354
|
score: best.score
|
|
@@ -22405,6 +22767,139 @@ var PipelineWatchdog = class {
|
|
|
22405
22767
|
}
|
|
22406
22768
|
};
|
|
22407
22769
|
//#endregion
|
|
22770
|
+
//#region src/camera-status/compose-camera-status.ts
|
|
22771
|
+
function mapAssignment(input) {
|
|
22772
|
+
return {
|
|
22773
|
+
detectionNodeId: input.detectionNodeId,
|
|
22774
|
+
decoderNodeId: input.decoderNodeId,
|
|
22775
|
+
audioNodeId: input.audioNodeId,
|
|
22776
|
+
pinned: {
|
|
22777
|
+
detection: input.pinned.detection,
|
|
22778
|
+
decoder: input.pinned.decoder,
|
|
22779
|
+
audio: input.pinned.audio
|
|
22780
|
+
},
|
|
22781
|
+
reasons: {
|
|
22782
|
+
detection: input.reasons.detection,
|
|
22783
|
+
decoder: input.reasons.decoder,
|
|
22784
|
+
audio: input.reasons.audio
|
|
22785
|
+
}
|
|
22786
|
+
};
|
|
22787
|
+
}
|
|
22788
|
+
function mapSource(sourceResult) {
|
|
22789
|
+
if (sourceResult === null) return { streams: [] };
|
|
22790
|
+
return { streams: sourceResult.streams.map((s) => ({
|
|
22791
|
+
camStreamId: s.camStreamId,
|
|
22792
|
+
codec: s.codec,
|
|
22793
|
+
width: s.width,
|
|
22794
|
+
height: s.height,
|
|
22795
|
+
fps: s.fps,
|
|
22796
|
+
kind: s.kind
|
|
22797
|
+
})) };
|
|
22798
|
+
}
|
|
22799
|
+
function mapBroker(brokerResult) {
|
|
22800
|
+
if (brokerResult === null) return null;
|
|
22801
|
+
return {
|
|
22802
|
+
profiles: brokerResult.profiles.map((p) => ({
|
|
22803
|
+
profile: p.profile,
|
|
22804
|
+
status: p.status,
|
|
22805
|
+
codec: p.codec,
|
|
22806
|
+
width: p.width,
|
|
22807
|
+
height: p.height,
|
|
22808
|
+
subscribers: p.subscribers,
|
|
22809
|
+
inFps: p.inFps,
|
|
22810
|
+
outFps: p.outFps
|
|
22811
|
+
})),
|
|
22812
|
+
webrtcSessions: brokerResult.webrtcSessions,
|
|
22813
|
+
rtspRestream: brokerResult.rtspRestream
|
|
22814
|
+
};
|
|
22815
|
+
}
|
|
22816
|
+
function mapDecoderShm(shm) {
|
|
22817
|
+
return {
|
|
22818
|
+
framesWritten: shm.framesWritten,
|
|
22819
|
+
getFrameHits: shm.getFrameHits,
|
|
22820
|
+
getFrameMisses: shm.getFrameMisses,
|
|
22821
|
+
budgetMb: shm.budgetMb
|
|
22822
|
+
};
|
|
22823
|
+
}
|
|
22824
|
+
function mapDecoder(decoderResult) {
|
|
22825
|
+
if (decoderResult === null) return null;
|
|
22826
|
+
return {
|
|
22827
|
+
nodeId: decoderResult.nodeId,
|
|
22828
|
+
formats: [...decoderResult.formats],
|
|
22829
|
+
sessionCount: decoderResult.sessionCount,
|
|
22830
|
+
shm: mapDecoderShm(decoderResult.shm)
|
|
22831
|
+
};
|
|
22832
|
+
}
|
|
22833
|
+
function mapMotion(motionResult) {
|
|
22834
|
+
if (motionResult === null) return null;
|
|
22835
|
+
return {
|
|
22836
|
+
enabled: motionResult.enabled,
|
|
22837
|
+
fps: motionResult.fps
|
|
22838
|
+
};
|
|
22839
|
+
}
|
|
22840
|
+
function mapProvisioning(p) {
|
|
22841
|
+
if (p.error !== void 0) return {
|
|
22842
|
+
state: p.state,
|
|
22843
|
+
error: p.error
|
|
22844
|
+
};
|
|
22845
|
+
return { state: p.state };
|
|
22846
|
+
}
|
|
22847
|
+
function mapDetection(detectionResult) {
|
|
22848
|
+
if (detectionResult === null) return null;
|
|
22849
|
+
const phase = detectionResult.phase;
|
|
22850
|
+
return {
|
|
22851
|
+
nodeId: detectionResult.nodeId,
|
|
22852
|
+
engine: {
|
|
22853
|
+
backend: detectionResult.engine.backend,
|
|
22854
|
+
device: detectionResult.engine.device
|
|
22855
|
+
},
|
|
22856
|
+
phase,
|
|
22857
|
+
configuredFps: detectionResult.configuredFps,
|
|
22858
|
+
actualFps: detectionResult.actualFps,
|
|
22859
|
+
queueDepth: detectionResult.queueDepth,
|
|
22860
|
+
avgInferenceMs: detectionResult.avgInferenceMs,
|
|
22861
|
+
provisioning: mapProvisioning(detectionResult.provisioning)
|
|
22862
|
+
};
|
|
22863
|
+
}
|
|
22864
|
+
function mapAudio(audioResult) {
|
|
22865
|
+
if (audioResult === null) return null;
|
|
22866
|
+
return {
|
|
22867
|
+
nodeId: audioResult.nodeId,
|
|
22868
|
+
enabled: audioResult.enabled
|
|
22869
|
+
};
|
|
22870
|
+
}
|
|
22871
|
+
function mapRecording(recordingResult) {
|
|
22872
|
+
if (recordingResult === null) return null;
|
|
22873
|
+
return {
|
|
22874
|
+
mode: recordingResult.mode,
|
|
22875
|
+
active: recordingResult.active,
|
|
22876
|
+
storageBytes: recordingResult.storageBytes
|
|
22877
|
+
};
|
|
22878
|
+
}
|
|
22879
|
+
/**
|
|
22880
|
+
* Pure function that composes a `CameraStatus` from per-stage fetch results.
|
|
22881
|
+
*
|
|
22882
|
+
* - `assignment` is always built from orchestrator-local data (never null).
|
|
22883
|
+
* - `source` always present: defaults to `{ streams: [] }` when sourceResult is null.
|
|
22884
|
+
* - Every other block is null when its stage result is null (graceful degradation).
|
|
22885
|
+
* - `fetchedAt` is stamped exactly as provided — never calls `Date.now()`.
|
|
22886
|
+
* - No mutation of the input.
|
|
22887
|
+
*/
|
|
22888
|
+
function composeCameraStatus(input) {
|
|
22889
|
+
return {
|
|
22890
|
+
deviceId: input.deviceId,
|
|
22891
|
+
assignment: mapAssignment(input),
|
|
22892
|
+
source: mapSource(input.sourceResult),
|
|
22893
|
+
broker: mapBroker(input.brokerResult),
|
|
22894
|
+
decoder: mapDecoder(input.decoderResult),
|
|
22895
|
+
motion: mapMotion(input.motionResult),
|
|
22896
|
+
detection: mapDetection(input.detectionResult),
|
|
22897
|
+
audio: mapAudio(input.audioResult),
|
|
22898
|
+
recording: mapRecording(input.recordingResult),
|
|
22899
|
+
fetchedAt: input.fetchedAt
|
|
22900
|
+
};
|
|
22901
|
+
}
|
|
22902
|
+
//#endregion
|
|
22408
22903
|
//#region src/index.ts
|
|
22409
22904
|
var PHASE_MODE_VALUES = new Set([
|
|
22410
22905
|
"disabled",
|
|
@@ -23162,8 +23657,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23162
23657
|
});
|
|
23163
23658
|
return {
|
|
23164
23659
|
success: true,
|
|
23165
|
-
|
|
23166
|
-
reason: "capacity"
|
|
23660
|
+
kind: "pending"
|
|
23167
23661
|
};
|
|
23168
23662
|
}
|
|
23169
23663
|
}
|
|
@@ -23173,14 +23667,22 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23173
23667
|
const preferredAgent = typeof pipelinePin === "string" && pipelinePin !== "auto" ? pipelinePin : legacyPreferred;
|
|
23174
23668
|
const decision = balance({
|
|
23175
23669
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
23176
|
-
preferredAgent
|
|
23670
|
+
preferredAgent,
|
|
23671
|
+
nodeCaps: this.buildNodeCaps()
|
|
23177
23672
|
});
|
|
23178
|
-
|
|
23179
|
-
if (
|
|
23673
|
+
if (!decision) throw new Error(`dispatchCamera: no runner available for ${runnerConfig.deviceId}`);
|
|
23674
|
+
if (decision.kind === "pending") {
|
|
23675
|
+
this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId: runnerConfig.deviceId } });
|
|
23676
|
+
return {
|
|
23677
|
+
success: true,
|
|
23678
|
+
kind: "pending"
|
|
23679
|
+
};
|
|
23680
|
+
}
|
|
23681
|
+
const targetNodeId = decision.agentNodeId;
|
|
23180
23682
|
await this.attachOn(targetNodeId, runnerConfig);
|
|
23181
23683
|
if (targetNodeId === this.localNodeId) this.pipelineWatchdog?.register(this.buildWatchdogCamera(runnerConfig, String(runnerConfig.deviceId)));
|
|
23182
|
-
const reason = decision
|
|
23183
|
-
const pinned = decision
|
|
23684
|
+
const reason = decision.reason;
|
|
23685
|
+
const pinned = decision.reason === "manual";
|
|
23184
23686
|
this.recordAssignment(runnerConfig.deviceId, targetNodeId, reason, pinned);
|
|
23185
23687
|
const decoderNodeId = await this.resolveDecoderNode(runnerConfig.deviceId, targetNodeId);
|
|
23186
23688
|
this.ctx.logger.info("dispatchCamera", {
|
|
@@ -23190,12 +23692,13 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23190
23692
|
},
|
|
23191
23693
|
meta: {
|
|
23192
23694
|
reason,
|
|
23193
|
-
score: decision
|
|
23695
|
+
score: decision.score,
|
|
23194
23696
|
decoderNodeId
|
|
23195
23697
|
}
|
|
23196
23698
|
});
|
|
23197
23699
|
return {
|
|
23198
23700
|
success: true,
|
|
23701
|
+
kind: "assigned",
|
|
23199
23702
|
agentNodeId: targetNodeId,
|
|
23200
23703
|
reason
|
|
23201
23704
|
};
|
|
@@ -23269,10 +23772,13 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23269
23772
|
if (cached) {
|
|
23270
23773
|
const decision = balance({
|
|
23271
23774
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
23272
|
-
preferredAgent: null
|
|
23775
|
+
preferredAgent: null,
|
|
23776
|
+
nodeCaps: this.buildNodeCaps()
|
|
23273
23777
|
});
|
|
23274
|
-
|
|
23275
|
-
if (
|
|
23778
|
+
if (!decision) this.ctx.logger.warn("unassignPipeline: no runner available, leaving unassigned", { tags: { deviceId: input.deviceId } });
|
|
23779
|
+
else if (decision.kind === "pending") this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId: input.deviceId } });
|
|
23780
|
+
else {
|
|
23781
|
+
const targetNodeId = decision.agentNodeId;
|
|
23276
23782
|
if (current && current.agentNodeId !== targetNodeId) await this.detachOn(current.agentNodeId, input.deviceId).catch((err) => {
|
|
23277
23783
|
const msg = errMsg(err);
|
|
23278
23784
|
this.ctx.logger.debug("unassignPipeline detach-old failed", {
|
|
@@ -23281,7 +23787,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23281
23787
|
});
|
|
23282
23788
|
});
|
|
23283
23789
|
if (!current || current.agentNodeId !== targetNodeId) await this.attachOn(targetNodeId, cached);
|
|
23284
|
-
const reason = decision
|
|
23790
|
+
const reason = decision.reason === "manual" ? "capacity" : decision.reason;
|
|
23285
23791
|
this.recordAssignment(input.deviceId, targetNodeId, reason, false);
|
|
23286
23792
|
this.ctx.logger.info("unassignPipeline: re-dispatched via auto", {
|
|
23287
23793
|
tags: {
|
|
@@ -23292,7 +23798,6 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23292
23798
|
});
|
|
23293
23799
|
return { success: true };
|
|
23294
23800
|
}
|
|
23295
|
-
this.ctx.logger.warn("unassignPipeline: no runner available, leaving unassigned", { tags: { deviceId: input.deviceId } });
|
|
23296
23801
|
}
|
|
23297
23802
|
if (current) {
|
|
23298
23803
|
await this.detachOn(current.agentNodeId, input.deviceId).catch((err) => {
|
|
@@ -23310,15 +23815,21 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23310
23815
|
async rebalance() {
|
|
23311
23816
|
if (!this.ctx) throw new Error("PipelineOrchestrator: rebalance called before initialize");
|
|
23312
23817
|
const loads = await this.collectAgentLoad({ onlyEnabled: true });
|
|
23818
|
+
const nodeCaps = this.buildNodeCaps();
|
|
23313
23819
|
let migrated = 0;
|
|
23314
23820
|
for (const [deviceId, config] of this.cameraConfigs) {
|
|
23315
23821
|
const current = this.assignments.get(deviceId);
|
|
23316
23822
|
if (current?.pinned) continue;
|
|
23317
23823
|
const decision = balance({
|
|
23318
23824
|
nodes: loads,
|
|
23319
|
-
preferredAgent: await this.readPreferredAgent(deviceId)
|
|
23825
|
+
preferredAgent: await this.readPreferredAgent(deviceId),
|
|
23826
|
+
nodeCaps
|
|
23320
23827
|
});
|
|
23321
23828
|
if (!decision) continue;
|
|
23829
|
+
if (decision.kind === "pending") {
|
|
23830
|
+
this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId } });
|
|
23831
|
+
continue;
|
|
23832
|
+
}
|
|
23322
23833
|
if (current && current.agentNodeId === decision.agentNodeId) continue;
|
|
23323
23834
|
if (current) await this.detachOn(current.agentNodeId, deviceId).catch((err) => {
|
|
23324
23835
|
const msg = errMsg(err);
|
|
@@ -23456,15 +23967,6 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23456
23967
|
nodeId
|
|
23457
23968
|
});
|
|
23458
23969
|
}
|
|
23459
|
-
async localRunnerNodeId() {
|
|
23460
|
-
const api = this.ctx.api;
|
|
23461
|
-
if (!api) return null;
|
|
23462
|
-
try {
|
|
23463
|
-
return (await api.pipelineRunner.getLocalLoad.query({ nodeId: this.localNodeId }))?.nodeId ?? this.localNodeId;
|
|
23464
|
-
} catch {
|
|
23465
|
-
return this.localNodeId;
|
|
23466
|
-
}
|
|
23467
|
-
}
|
|
23468
23970
|
/**
|
|
23469
23971
|
* Enumerate every runner-capable node currently known (populated via
|
|
23470
23972
|
* AgentOnline/AgentOffline events). Used to populate the `enabledNodes`
|
|
@@ -23619,6 +24121,18 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23619
24121
|
return null;
|
|
23620
24122
|
}
|
|
23621
24123
|
}
|
|
24124
|
+
/**
|
|
24125
|
+
* Build a per-node camera cap map from the persisted agent settings.
|
|
24126
|
+
* Returns `null` for nodes where `maxCameras` is null (unlimited).
|
|
24127
|
+
* Used by every `balance()` call so the balancer can honour operator-set
|
|
24128
|
+
* per-node maximums without polling the store on each decision.
|
|
24129
|
+
*/
|
|
24130
|
+
buildNodeCaps() {
|
|
24131
|
+
const blob = this.agentSettingsState.get();
|
|
24132
|
+
const caps = {};
|
|
24133
|
+
for (const [nodeId, settings] of Object.entries(blob)) caps[nodeId] = settings.maxCameras ?? null;
|
|
24134
|
+
return caps;
|
|
24135
|
+
}
|
|
23622
24136
|
async readAudioNodePin(deviceId) {
|
|
23623
24137
|
if (!this.ctx?.settings) return null;
|
|
23624
24138
|
try {
|
|
@@ -23761,13 +24275,19 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23761
24275
|
for (const { deviceId, config } of affected) {
|
|
23762
24276
|
const decision = balance({
|
|
23763
24277
|
nodes: loads,
|
|
23764
|
-
preferredAgent: null
|
|
24278
|
+
preferredAgent: null,
|
|
24279
|
+
nodeCaps: this.buildNodeCaps()
|
|
23765
24280
|
});
|
|
23766
24281
|
if (!decision) {
|
|
23767
24282
|
this.ctx.logger.error("Failover: no online runner", { tags: { deviceId } });
|
|
23768
24283
|
this.assignments.delete(deviceId);
|
|
23769
24284
|
continue;
|
|
23770
24285
|
}
|
|
24286
|
+
if (decision.kind === "pending") {
|
|
24287
|
+
this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId } });
|
|
24288
|
+
this.assignments.delete(deviceId);
|
|
24289
|
+
continue;
|
|
24290
|
+
}
|
|
23771
24291
|
try {
|
|
23772
24292
|
await this.attachOn(decision.agentNodeId, config);
|
|
23773
24293
|
this.recordAssignment(deviceId, decision.agentNodeId, "failover", false);
|
|
@@ -24247,7 +24767,10 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24247
24767
|
}
|
|
24248
24768
|
async setAgentAddonDefaults(input) {
|
|
24249
24769
|
let existing = (await this.readAgentSettingsMap())[input.agentNodeId];
|
|
24250
|
-
if (!existing)
|
|
24770
|
+
if (!existing) {
|
|
24771
|
+
await this.seedAgentSettingsFromCatalog(input.agentNodeId);
|
|
24772
|
+
existing = (await this.readAgentSettingsMap())[input.agentNodeId];
|
|
24773
|
+
}
|
|
24251
24774
|
if (!existing) await this.writeAgentSettings(input.agentNodeId, { addonDefaults: { ...input.defaults } });
|
|
24252
24775
|
else await this.writeAgentSettings(input.agentNodeId, {
|
|
24253
24776
|
...existing,
|
|
@@ -24285,6 +24808,22 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24285
24808
|
removed: true
|
|
24286
24809
|
};
|
|
24287
24810
|
}
|
|
24811
|
+
async setAgentMaxCameras(input) {
|
|
24812
|
+
const existing = (await this.readAgentSettingsMap())[input.agentNodeId];
|
|
24813
|
+
const next = existing ? {
|
|
24814
|
+
...existing,
|
|
24815
|
+
maxCameras: input.maxCameras
|
|
24816
|
+
} : {
|
|
24817
|
+
addonDefaults: {},
|
|
24818
|
+
maxCameras: input.maxCameras
|
|
24819
|
+
};
|
|
24820
|
+
await this.writeAgentSettings(input.agentNodeId, next);
|
|
24821
|
+
this.ctx.logger.info("agentSettings.maxCameras updated", {
|
|
24822
|
+
tags: { nodeId: input.agentNodeId },
|
|
24823
|
+
meta: { maxCameras: input.maxCameras }
|
|
24824
|
+
});
|
|
24825
|
+
return { success: true };
|
|
24826
|
+
}
|
|
24288
24827
|
async getCameraSettings(input) {
|
|
24289
24828
|
return (await this.readCameraSettingsMap())[String(input.deviceId)] ?? null;
|
|
24290
24829
|
}
|
|
@@ -24414,6 +24953,204 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24414
24953
|
this.ctx.logger.info("template deleted", { meta: { templateId: input.id } });
|
|
24415
24954
|
return { success: true };
|
|
24416
24955
|
}
|
|
24956
|
+
/**
|
|
24957
|
+
* Races a promise against a timeout. Returns `null` on timeout OR rejection.
|
|
24958
|
+
* Never throws — individual stage failures become `null` in the aggregate.
|
|
24959
|
+
*
|
|
24960
|
+
* @param p The stage fetch promise.
|
|
24961
|
+
* @param ms Timeout in milliseconds.
|
|
24962
|
+
*/
|
|
24963
|
+
boundedStage(p, ms) {
|
|
24964
|
+
let timer;
|
|
24965
|
+
const timeout = new Promise((resolve) => {
|
|
24966
|
+
timer = setTimeout(() => resolve(null), ms);
|
|
24967
|
+
});
|
|
24968
|
+
return Promise.race([p, timeout]).catch(() => null).finally(() => {
|
|
24969
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
24970
|
+
});
|
|
24971
|
+
}
|
|
24972
|
+
/**
|
|
24973
|
+
* Server-composed aggregated status for a single camera.
|
|
24974
|
+
*
|
|
24975
|
+
* Fans out in parallel (bounded, per-stage graceful degradation) to
|
|
24976
|
+
* broker / decoder / motion / detection / audio / recording source caps
|
|
24977
|
+
* via `ctx.api`. A stage whose source errors or times out becomes `null`
|
|
24978
|
+
* in the returned payload — one slow agent never breaks the whole call.
|
|
24979
|
+
*/
|
|
24980
|
+
async getCameraStatus(input) {
|
|
24981
|
+
const { deviceId } = input;
|
|
24982
|
+
const api = this.ctx.api;
|
|
24983
|
+
const STAGE_TIMEOUT_MS = 3e3;
|
|
24984
|
+
const pipelineAssignment = this.assignments.get(deviceId) ?? null;
|
|
24985
|
+
const detectionNodeId = pipelineAssignment?.agentNodeId ?? null;
|
|
24986
|
+
const decoderPinRaw = (api ? await this.ctx.settings?.readDeviceStore(deviceId).catch(() => ({})) ?? {} : {})["decoderNodeId"];
|
|
24987
|
+
const decoderPinned = typeof decoderPinRaw === "string" && decoderPinRaw !== "auto";
|
|
24988
|
+
const decoderNodeId = detectionNodeId ? await this.resolveDecoderNode(deviceId, detectionNodeId).catch(() => null) : null;
|
|
24989
|
+
const audioAssignment = this.audioAssignments.get(deviceId) ?? null;
|
|
24990
|
+
const audioNodeId = audioAssignment?.nodeId ?? null;
|
|
24991
|
+
const audioPinned = audioAssignment?.pinned ?? false;
|
|
24992
|
+
const pinned = {
|
|
24993
|
+
detection: pipelineAssignment?.pinned ?? false,
|
|
24994
|
+
decoder: decoderPinned,
|
|
24995
|
+
audio: audioPinned
|
|
24996
|
+
};
|
|
24997
|
+
const reasons = {
|
|
24998
|
+
detection: pipelineAssignment?.reason,
|
|
24999
|
+
decoder: decoderPinned ? "manual" : "co-located",
|
|
25000
|
+
audio: audioPinned ? "manual" : void 0
|
|
25001
|
+
};
|
|
25002
|
+
const allSlotsFetch = api ? api.streamBroker.listAllProfileSlots.query() : null;
|
|
25003
|
+
const sourceFetch = api && allSlotsFetch ? this.boundedStage(allSlotsFetch.then((slots) => {
|
|
25004
|
+
return { streams: slots.filter((s) => s.deviceId === deviceId).map((s) => ({
|
|
25005
|
+
camStreamId: s.sourceCamStreamId ?? s.brokerId,
|
|
25006
|
+
codec: s.codec ?? "",
|
|
25007
|
+
width: s.resolution?.width ?? 0,
|
|
25008
|
+
height: s.resolution?.height ?? 0,
|
|
25009
|
+
fps: 0,
|
|
25010
|
+
kind: s.profile
|
|
25011
|
+
})) };
|
|
25012
|
+
}), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25013
|
+
const WEBRTC_KINDS = new Set([
|
|
25014
|
+
"webrtc-browser",
|
|
25015
|
+
"webrtc-mobile",
|
|
25016
|
+
"webrtc-whep"
|
|
25017
|
+
]);
|
|
25018
|
+
const brokerFetch = api && allSlotsFetch ? this.boundedStage(allSlotsFetch.then(async (slots) => {
|
|
25019
|
+
const deviceSlots = slots.filter((s) => s.deviceId === deviceId);
|
|
25020
|
+
if (deviceSlots.length === 0) return {
|
|
25021
|
+
profiles: [],
|
|
25022
|
+
webrtcSessions: 0,
|
|
25023
|
+
rtspRestream: false
|
|
25024
|
+
};
|
|
25025
|
+
const [statsAndClients, rtspEntry] = await Promise.all([Promise.all(deviceSlots.map(async (slot) => {
|
|
25026
|
+
return {
|
|
25027
|
+
slot,
|
|
25028
|
+
stats: await api.streamBroker.getBrokerStats.query({ brokerId: slot.brokerId }).catch(() => null),
|
|
25029
|
+
clients: await api.streamBroker.listClients.query({ brokerId: slot.brokerId }).catch(() => null)
|
|
25030
|
+
};
|
|
25031
|
+
})), api.streamBroker.getAllRtspEntries.query({}).catch(() => null)]);
|
|
25032
|
+
return {
|
|
25033
|
+
profiles: statsAndClients.map(({ slot, stats, clients }) => ({
|
|
25034
|
+
profile: slot.profile,
|
|
25035
|
+
status: slot.status,
|
|
25036
|
+
codec: stats?.codec ?? slot.codec ?? "",
|
|
25037
|
+
width: slot.resolution?.width ?? 0,
|
|
25038
|
+
height: slot.resolution?.height ?? 0,
|
|
25039
|
+
subscribers: clients?.encodedSubscribers ?? 0,
|
|
25040
|
+
inFps: stats?.inputFps ?? 0,
|
|
25041
|
+
outFps: stats?.decodeFps ?? 0
|
|
25042
|
+
})),
|
|
25043
|
+
webrtcSessions: statsAndClients.reduce((total, { clients }) => {
|
|
25044
|
+
if (!clients) return total;
|
|
25045
|
+
return total + clients.encoded.filter((c) => WEBRTC_KINDS.has(c.attribution.kind)).length;
|
|
25046
|
+
}, 0),
|
|
25047
|
+
rtspRestream: rtspEntry?.some((e) => {
|
|
25048
|
+
return e.brokerId.split("/")[0] === String(deviceId) && e.enabled;
|
|
25049
|
+
}) ?? false
|
|
25050
|
+
};
|
|
25051
|
+
}), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25052
|
+
const decoderFetch = decoderNodeId ? Promise.resolve({
|
|
25053
|
+
nodeId: decoderNodeId,
|
|
25054
|
+
formats: [],
|
|
25055
|
+
sessionCount: 0,
|
|
25056
|
+
shm: {
|
|
25057
|
+
framesWritten: 0,
|
|
25058
|
+
getFrameHits: 0,
|
|
25059
|
+
getFrameMisses: 0,
|
|
25060
|
+
budgetMb: 0
|
|
25061
|
+
}
|
|
25062
|
+
}) : Promise.resolve(null);
|
|
25063
|
+
const motionResult = (() => {
|
|
25064
|
+
const config = this.cameraConfigs.get(deviceId);
|
|
25065
|
+
if (!config) return null;
|
|
25066
|
+
return {
|
|
25067
|
+
enabled: config.motionSources.includes("analyzer") || config.motionSources.includes("onboard"),
|
|
25068
|
+
fps: config.motionFps
|
|
25069
|
+
};
|
|
25070
|
+
})();
|
|
25071
|
+
const detectionFetch = api && detectionNodeId ? this.boundedStage(Promise.all([api.pipelineExecutor.getEngineProvisioning.query({ nodeId: detectionNodeId }).catch(() => null), api.pipelineExecutor.getSelectedEngine.query({ nodeId: detectionNodeId }).catch(() => null)]).then(async ([provisioning, engine]) => {
|
|
25072
|
+
const metrics = await api.pipelineRunner.getCameraMetrics.query({
|
|
25073
|
+
deviceId,
|
|
25074
|
+
nodeId: detectionNodeId
|
|
25075
|
+
}).catch(() => null);
|
|
25076
|
+
const phase = (() => {
|
|
25077
|
+
const p = metrics?.phase;
|
|
25078
|
+
if (p === "active") return "active";
|
|
25079
|
+
if (p === "idle") return "idle";
|
|
25080
|
+
return "watching";
|
|
25081
|
+
})();
|
|
25082
|
+
return {
|
|
25083
|
+
nodeId: detectionNodeId,
|
|
25084
|
+
engine: {
|
|
25085
|
+
backend: engine?.backend ?? "",
|
|
25086
|
+
device: engine?.device ?? ""
|
|
25087
|
+
},
|
|
25088
|
+
phase,
|
|
25089
|
+
configuredFps: metrics?.configuredFps ?? 0,
|
|
25090
|
+
actualFps: metrics?.actualFps ?? 0,
|
|
25091
|
+
queueDepth: metrics?.queueDepth ?? 0,
|
|
25092
|
+
avgInferenceMs: metrics?.avgInferenceTimeMs ?? 0,
|
|
25093
|
+
provisioning: {
|
|
25094
|
+
state: provisioning?.state ?? "idle",
|
|
25095
|
+
...provisioning?.error !== void 0 ? { error: provisioning.error } : {}
|
|
25096
|
+
}
|
|
25097
|
+
};
|
|
25098
|
+
}), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25099
|
+
const audioResult = audioNodeId ? {
|
|
25100
|
+
nodeId: audioNodeId,
|
|
25101
|
+
enabled: true
|
|
25102
|
+
} : null;
|
|
25103
|
+
function isRecordingStatus(v) {
|
|
25104
|
+
return v !== null && typeof v === "object" && "activeMode" in v && (v.activeMode === "off" || v.activeMode === "continuous" || v.activeMode === "events") && "enabled" in v && typeof v.enabled === "boolean" && "storageBytes" in v && typeof v.storageBytes === "number";
|
|
25105
|
+
}
|
|
25106
|
+
const recordingFetch = api ? this.boundedStage(api.recording.getStatus.query({ deviceId }).then((rawStatus) => {
|
|
25107
|
+
if (!isRecordingStatus(rawStatus)) return null;
|
|
25108
|
+
return {
|
|
25109
|
+
mode: rawStatus.activeMode,
|
|
25110
|
+
active: rawStatus.enabled && rawStatus.activeMode !== "off",
|
|
25111
|
+
storageBytes: rawStatus.storageBytes
|
|
25112
|
+
};
|
|
25113
|
+
}).catch(() => null), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25114
|
+
const [sourceResult, brokerResult, decoderResult, detectionResult, recordingResult] = await Promise.all([
|
|
25115
|
+
sourceFetch,
|
|
25116
|
+
brokerFetch,
|
|
25117
|
+
decoderFetch,
|
|
25118
|
+
detectionFetch,
|
|
25119
|
+
recordingFetch
|
|
25120
|
+
]);
|
|
25121
|
+
return composeCameraStatus({
|
|
25122
|
+
deviceId,
|
|
25123
|
+
fetchedAt: Date.now(),
|
|
25124
|
+
detectionNodeId,
|
|
25125
|
+
decoderNodeId,
|
|
25126
|
+
audioNodeId,
|
|
25127
|
+
pinned,
|
|
25128
|
+
reasons,
|
|
25129
|
+
sourceResult,
|
|
25130
|
+
brokerResult,
|
|
25131
|
+
decoderResult,
|
|
25132
|
+
motionResult,
|
|
25133
|
+
detectionResult,
|
|
25134
|
+
audioResult,
|
|
25135
|
+
recordingResult
|
|
25136
|
+
});
|
|
25137
|
+
}
|
|
25138
|
+
/**
|
|
25139
|
+
* Server-composed aggregated status for multiple cameras in one call.
|
|
25140
|
+
*
|
|
25141
|
+
* `deviceIds` defaults to all cameras currently tracked by the
|
|
25142
|
+
* orchestrator's assignment map when omitted.
|
|
25143
|
+
*
|
|
25144
|
+
* v1: `Promise.all` over per-device composition (no concurrency cap).
|
|
25145
|
+
* Note: for large fleets (hundreds of cameras) this may fan out many
|
|
25146
|
+
* parallel calls. A concurrency limiter (p-limit / semaphore) should be
|
|
25147
|
+
* added if latency measurements show it's necessary — deliberately
|
|
25148
|
+
* deferred per the YAGNI constraint in the spec.
|
|
25149
|
+
*/
|
|
25150
|
+
async getCameraStatuses(input) {
|
|
25151
|
+
const ids = input.deviceIds !== void 0 && input.deviceIds.length > 0 ? input.deviceIds : [...this.assignments.keys()];
|
|
25152
|
+
return Promise.all(ids.map((deviceId) => this.getCameraStatus({ deviceId })));
|
|
25153
|
+
}
|
|
24417
25154
|
/** Read the templates map from the addon store via the durable handle. */
|
|
24418
25155
|
async readTemplatesMap() {
|
|
24419
25156
|
const raw = await this.templatesState.get();
|
|
@@ -24444,7 +25181,11 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24444
25181
|
return;
|
|
24445
25182
|
}
|
|
24446
25183
|
const all = await this.readAgentSettingsMap();
|
|
24447
|
-
all[nodeId]
|
|
25184
|
+
const existing = all[nodeId];
|
|
25185
|
+
all[nodeId] = {
|
|
25186
|
+
addonDefaults: settings.addonDefaults,
|
|
25187
|
+
maxCameras: settings.maxCameras !== void 0 ? settings.maxCameras ?? null : existing?.maxCameras ?? null
|
|
25188
|
+
};
|
|
24448
25189
|
await this.agentSettingsState.set(all);
|
|
24449
25190
|
}
|
|
24450
25191
|
/** Read the `cameraSettings` map (keyed on `deviceId` as string) via the durable handle. */
|
|
@@ -24654,12 +25395,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24654
25395
|
}
|
|
24655
25396
|
let agent = (await this.readAgentSettingsMap())[nodeId];
|
|
24656
25397
|
if (!agent || Object.keys(agent.addonDefaults ?? {}).length === 0) {
|
|
24657
|
-
|
|
24658
|
-
if (!seeded) {
|
|
25398
|
+
if (!await this.seedAgentSettingsFromCatalog(nodeId)) {
|
|
24659
25399
|
await sleep$1(2e3);
|
|
24660
25400
|
continue;
|
|
24661
25401
|
}
|
|
24662
|
-
agent =
|
|
25402
|
+
agent = (await this.readAgentSettingsMap())[nodeId];
|
|
25403
|
+
}
|
|
25404
|
+
if (!agent) {
|
|
25405
|
+
await sleep$1(2e3);
|
|
25406
|
+
continue;
|
|
24663
25407
|
}
|
|
24664
25408
|
const engine = await this.readDetectionPipelineEngine(nodeId) ?? catalog.selectedEngine;
|
|
24665
25409
|
return {
|
|
@@ -25566,7 +26310,8 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25566
26310
|
};
|
|
25567
26311
|
let dispatchedNodeId = null;
|
|
25568
26312
|
try {
|
|
25569
|
-
|
|
26313
|
+
const result = await this.dispatchCamera(runnerConfig);
|
|
26314
|
+
if (result.kind === "assigned") dispatchedNodeId = result.agentNodeId;
|
|
25570
26315
|
} catch (err) {
|
|
25571
26316
|
const msg = errMsg(err);
|
|
25572
26317
|
log.error("dispatchCamera failed", { meta: { error: msg } });
|