@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.js
CHANGED
|
@@ -4985,6 +4985,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
4985
4985
|
*/
|
|
4986
4986
|
EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
|
|
4987
4987
|
/**
|
|
4988
|
+
* Per-node detection-engine runtime-provisioning transition. Emitted by
|
|
4989
|
+
* the detection-pipeline provider on every state change of its lazy
|
|
4990
|
+
* engine-provisioning machine (idle → installing → verifying → ready,
|
|
4991
|
+
* or → failed with a `nextRetryAt`). Payload is the
|
|
4992
|
+
* `EngineProvisioningState` snapshot; `event.source.nodeId` carries the
|
|
4993
|
+
* node. The Pipeline page subscribes to drive a live "installing
|
|
4994
|
+
* OpenVINO… / ready" indicator per node without polling
|
|
4995
|
+
* `pipelineExecutor.getEngineProvisioning`. Telemetry-grade (D8): the UI
|
|
4996
|
+
* also reads the cap snapshot on mount / reconnect. Phase 2.
|
|
4997
|
+
*/
|
|
4998
|
+
EventCategory["PipelineEngineProvisioning"] = "pipeline.engine-provisioning";
|
|
4999
|
+
/**
|
|
4988
5000
|
* Cluster topology snapshot. Carries the same payload returned by
|
|
4989
5001
|
* `nodes.topology` (every reachable node + addons + processes).
|
|
4990
5002
|
* Emitted by the hub on any agent / addon lifecycle change
|
|
@@ -5134,14 +5146,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5134
5146
|
EventCategory["DeviceSleeping"] = "device.sleeping";
|
|
5135
5147
|
EventCategory["RetentionCleanup"] = "retention.cleanup";
|
|
5136
5148
|
/**
|
|
5137
|
-
*
|
|
5138
|
-
*
|
|
5139
|
-
*
|
|
5140
|
-
* to
|
|
5141
|
-
*
|
|
5142
|
-
* Spec: docs/superpowers/specs/2026-05-21-addons-bulk-update-progress-design.md
|
|
5149
|
+
* Legacy bulk-update progress snapshot (payload `BulkUpdateState`). No longer
|
|
5150
|
+
* emitted — F3 removed the coordinator that produced it; "Update all" now runs
|
|
5151
|
+
* as one lifecycle engine job (`AddonsJobProgress`/`AddonsJobLog`). Retained
|
|
5152
|
+
* (with `BulkUpdateState`) only to avoid regenerating the event maps; removed
|
|
5153
|
+
* in F4 once live bulk progress is re-implemented over the engine events.
|
|
5143
5154
|
*/
|
|
5144
5155
|
EventCategory["AddonsBulkUpdateProgress"] = "addons.bulk-update-progress";
|
|
5156
|
+
EventCategory["AddonsJobProgress"] = "addons.job-progress";
|
|
5157
|
+
EventCategory["AddonsJobLog"] = "addons.job-log";
|
|
5145
5158
|
/**
|
|
5146
5159
|
* A container's child visibility toggled (hidden/shown). Emitted by the
|
|
5147
5160
|
* `accessories` cap when a child device is hidden or revealed.
|
|
@@ -9565,6 +9578,24 @@ var DetectorOutputSchema = object({
|
|
|
9565
9578
|
inferenceMs: number(),
|
|
9566
9579
|
modelId: string()
|
|
9567
9580
|
});
|
|
9581
|
+
var EngineProvisioningSchema = object({
|
|
9582
|
+
runtimeId: _enum([
|
|
9583
|
+
"onnx",
|
|
9584
|
+
"openvino",
|
|
9585
|
+
"coreml"
|
|
9586
|
+
]).nullable(),
|
|
9587
|
+
device: string().nullable(),
|
|
9588
|
+
state: _enum([
|
|
9589
|
+
"idle",
|
|
9590
|
+
"installing",
|
|
9591
|
+
"verifying",
|
|
9592
|
+
"ready",
|
|
9593
|
+
"failed"
|
|
9594
|
+
]),
|
|
9595
|
+
progress: number().optional(),
|
|
9596
|
+
error: string().optional(),
|
|
9597
|
+
nextRetryAt: number().optional()
|
|
9598
|
+
});
|
|
9568
9599
|
var PipelineStepInputSchema = lazy(() => object({
|
|
9569
9600
|
addonId: string(),
|
|
9570
9601
|
modelId: string(),
|
|
@@ -9664,6 +9695,15 @@ var pipelineExecutorCapability = {
|
|
|
9664
9695
|
kind: "mutation",
|
|
9665
9696
|
auth: "admin"
|
|
9666
9697
|
}),
|
|
9698
|
+
/**
|
|
9699
|
+
* Per-node detection-engine provisioning snapshot. Returns the live
|
|
9700
|
+
* state of the lazy runtime-provisioning machine on `nodeId`
|
|
9701
|
+
* (idle / installing / verifying / ready / failed). The UI pairs this
|
|
9702
|
+
* one-shot query with the `pipeline.engine-provisioning` live event
|
|
9703
|
+
* (emitted on every transition) to drive a per-node "engine ready?"
|
|
9704
|
+
* indicator without polling. Phase 2.
|
|
9705
|
+
*/
|
|
9706
|
+
getEngineProvisioning: method(object({ nodeId: string() }), EngineProvisioningSchema),
|
|
9667
9707
|
getVideoPipelineSteps: method(_void(), record(string(), object({
|
|
9668
9708
|
modelId: string(),
|
|
9669
9709
|
settings: record(string(), unknown()).readonly()
|
|
@@ -13540,7 +13580,10 @@ var AgentAddonConfigSchema = object({
|
|
|
13540
13580
|
modelId: string(),
|
|
13541
13581
|
settings: record(string(), unknown()).readonly()
|
|
13542
13582
|
});
|
|
13543
|
-
var AgentPipelineSettingsSchema = object({
|
|
13583
|
+
var AgentPipelineSettingsSchema = object({
|
|
13584
|
+
addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
|
|
13585
|
+
maxCameras: number().int().nonnegative().nullable().default(null)
|
|
13586
|
+
});
|
|
13544
13587
|
var CameraPipelineForAgentSchema = object({
|
|
13545
13588
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
13546
13589
|
audio: object({
|
|
@@ -13642,6 +13685,133 @@ var GlobalMetricsSchema = object({
|
|
|
13642
13685
|
* capability providers.
|
|
13643
13686
|
*/
|
|
13644
13687
|
var CapabilityBindingsSchema = record(string(), string());
|
|
13688
|
+
/** Source block — always present; derives from the stream catalog. */
|
|
13689
|
+
var CameraSourceStatusSchema = object({ streams: array(object({
|
|
13690
|
+
camStreamId: string(),
|
|
13691
|
+
codec: string(),
|
|
13692
|
+
width: number(),
|
|
13693
|
+
height: number(),
|
|
13694
|
+
fps: number(),
|
|
13695
|
+
kind: string()
|
|
13696
|
+
})).readonly() });
|
|
13697
|
+
/** Assignment block — always present (orchestrator-local, no remote call). */
|
|
13698
|
+
var CameraAssignmentStatusSchema = object({
|
|
13699
|
+
detectionNodeId: string().nullable(),
|
|
13700
|
+
decoderNodeId: string().nullable(),
|
|
13701
|
+
audioNodeId: string().nullable(),
|
|
13702
|
+
pinned: object({
|
|
13703
|
+
detection: boolean(),
|
|
13704
|
+
decoder: boolean(),
|
|
13705
|
+
audio: boolean()
|
|
13706
|
+
}),
|
|
13707
|
+
reasons: object({
|
|
13708
|
+
detection: string().optional(),
|
|
13709
|
+
decoder: string().optional(),
|
|
13710
|
+
audio: string().optional()
|
|
13711
|
+
})
|
|
13712
|
+
});
|
|
13713
|
+
/** Broker block — null when the broker stage is unreachable or inactive. */
|
|
13714
|
+
var CameraBrokerStatusSchema = object({
|
|
13715
|
+
profiles: array(object({
|
|
13716
|
+
profile: string(),
|
|
13717
|
+
status: string(),
|
|
13718
|
+
codec: string(),
|
|
13719
|
+
width: number(),
|
|
13720
|
+
height: number(),
|
|
13721
|
+
subscribers: number(),
|
|
13722
|
+
inFps: number(),
|
|
13723
|
+
outFps: number()
|
|
13724
|
+
})).readonly(),
|
|
13725
|
+
webrtcSessions: number(),
|
|
13726
|
+
rtspRestream: boolean()
|
|
13727
|
+
});
|
|
13728
|
+
/** Shared-memory ring statistics within the decoder block. */
|
|
13729
|
+
var CameraDecoderShmSchema = object({
|
|
13730
|
+
framesWritten: number(),
|
|
13731
|
+
getFrameHits: number(),
|
|
13732
|
+
getFrameMisses: number(),
|
|
13733
|
+
budgetMb: number()
|
|
13734
|
+
});
|
|
13735
|
+
/** Decoder block — null when the decoder stage is unreachable or inactive. */
|
|
13736
|
+
var CameraDecoderStatusSchema = object({
|
|
13737
|
+
nodeId: string(),
|
|
13738
|
+
formats: array(string()).readonly(),
|
|
13739
|
+
sessionCount: number(),
|
|
13740
|
+
shm: CameraDecoderShmSchema
|
|
13741
|
+
});
|
|
13742
|
+
/** Motion block — null when motion detection is not active for this device. */
|
|
13743
|
+
var CameraMotionStatusSchema = object({
|
|
13744
|
+
enabled: boolean(),
|
|
13745
|
+
fps: number()
|
|
13746
|
+
});
|
|
13747
|
+
/** Detection provisioning sub-block. */
|
|
13748
|
+
var CameraDetectionProvisioningSchema = object({
|
|
13749
|
+
state: _enum([
|
|
13750
|
+
"idle",
|
|
13751
|
+
"installing",
|
|
13752
|
+
"verifying",
|
|
13753
|
+
"ready",
|
|
13754
|
+
"failed"
|
|
13755
|
+
]),
|
|
13756
|
+
error: string().optional()
|
|
13757
|
+
});
|
|
13758
|
+
/** Detection phase — derived from the runner's engine phase. */
|
|
13759
|
+
var CameraDetectionPhaseSchema = _enum([
|
|
13760
|
+
"idle",
|
|
13761
|
+
"watching",
|
|
13762
|
+
"active"
|
|
13763
|
+
]);
|
|
13764
|
+
/** Detection block — null when no detection node is assigned or reachable. */
|
|
13765
|
+
var CameraDetectionStatusSchema = object({
|
|
13766
|
+
nodeId: string(),
|
|
13767
|
+
engine: object({
|
|
13768
|
+
backend: string(),
|
|
13769
|
+
device: string()
|
|
13770
|
+
}),
|
|
13771
|
+
phase: CameraDetectionPhaseSchema,
|
|
13772
|
+
configuredFps: number(),
|
|
13773
|
+
actualFps: number(),
|
|
13774
|
+
queueDepth: number(),
|
|
13775
|
+
avgInferenceMs: number(),
|
|
13776
|
+
provisioning: CameraDetectionProvisioningSchema
|
|
13777
|
+
});
|
|
13778
|
+
/** Audio block — null when no audio node is assigned or reachable. */
|
|
13779
|
+
var CameraAudioStatusSchema = object({
|
|
13780
|
+
nodeId: string(),
|
|
13781
|
+
enabled: boolean()
|
|
13782
|
+
});
|
|
13783
|
+
/** Recording block — null when no recording cap is active for this device. */
|
|
13784
|
+
var CameraRecordingStatusSchema = object({
|
|
13785
|
+
mode: _enum([
|
|
13786
|
+
"off",
|
|
13787
|
+
"continuous",
|
|
13788
|
+
"events"
|
|
13789
|
+
]),
|
|
13790
|
+
active: boolean(),
|
|
13791
|
+
storageBytes: number()
|
|
13792
|
+
});
|
|
13793
|
+
/**
|
|
13794
|
+
* Aggregated per-camera pipeline status — server-composed, single call.
|
|
13795
|
+
*
|
|
13796
|
+
* The `assignment` and `source` blocks are always present.
|
|
13797
|
+
* Every other block is `null` when the stage is inactive or unreachable
|
|
13798
|
+
* during the bounded parallel fan-out in the orchestrator implementation.
|
|
13799
|
+
*
|
|
13800
|
+
* See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
|
|
13801
|
+
*/
|
|
13802
|
+
var CameraStatusSchema = object({
|
|
13803
|
+
deviceId: number(),
|
|
13804
|
+
assignment: CameraAssignmentStatusSchema,
|
|
13805
|
+
source: CameraSourceStatusSchema,
|
|
13806
|
+
broker: CameraBrokerStatusSchema.nullable(),
|
|
13807
|
+
decoder: CameraDecoderStatusSchema.nullable(),
|
|
13808
|
+
motion: CameraMotionStatusSchema.nullable(),
|
|
13809
|
+
detection: CameraDetectionStatusSchema.nullable(),
|
|
13810
|
+
audio: CameraAudioStatusSchema.nullable(),
|
|
13811
|
+
recording: CameraRecordingStatusSchema.nullable(),
|
|
13812
|
+
/** Unix timestamp (ms) when this snapshot was composed server-side. */
|
|
13813
|
+
fetchedAt: number()
|
|
13814
|
+
});
|
|
13645
13815
|
/**
|
|
13646
13816
|
* Pipeline Orchestrator capability — global load balancer + camera dispatcher.
|
|
13647
13817
|
*
|
|
@@ -13818,6 +13988,25 @@ var pipelineOrchestratorCapability = {
|
|
|
13818
13988
|
kind: "mutation",
|
|
13819
13989
|
auth: "admin"
|
|
13820
13990
|
}),
|
|
13991
|
+
/**
|
|
13992
|
+
* Set the per-node camera cap for one agent.
|
|
13993
|
+
*
|
|
13994
|
+
* `maxCameras: 0` and `maxCameras: null` are both treated as unlimited
|
|
13995
|
+
* by the load balancer (0 is stored as-is; the balancer treats ≤0 as
|
|
13996
|
+
* unlimited alongside null). Pass `null` to clear an existing cap.
|
|
13997
|
+
* Note: the admin UI normalises 0→null before calling this method, so
|
|
13998
|
+
* `maxCameras: 0` only reaches the store via direct SDK or CLI calls.
|
|
13999
|
+
* Changes take effect immediately on the next dispatch cycle — cameras
|
|
14000
|
+
* currently assigned over-cap are left in place (no forced eviction),
|
|
14001
|
+
* but new assignments obey the updated cap.
|
|
14002
|
+
*/
|
|
14003
|
+
setAgentMaxCameras: method(object({
|
|
14004
|
+
agentNodeId: string(),
|
|
14005
|
+
maxCameras: number().int().nonnegative().nullable()
|
|
14006
|
+
}), object({ success: literal(true) }), {
|
|
14007
|
+
kind: "mutation",
|
|
14008
|
+
auth: "admin"
|
|
14009
|
+
}),
|
|
13821
14010
|
/** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
|
|
13822
14011
|
getCameraSettings: method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()),
|
|
13823
14012
|
/** Set or clear the 3-state toggle for one (camera, addonId). Pass `enabled: null` to clear and revert to agent default. */
|
|
@@ -13858,6 +14047,29 @@ var pipelineOrchestratorCapability = {
|
|
|
13858
14047
|
deviceId: number(),
|
|
13859
14048
|
agentNodeId: string().optional()
|
|
13860
14049
|
}), CameraPipelineConfigSchema),
|
|
14050
|
+
/**
|
|
14051
|
+
* Server-composed aggregated status for a single camera.
|
|
14052
|
+
*
|
|
14053
|
+
* Fans out in parallel (bounded, per-stage graceful degradation) to
|
|
14054
|
+
* broker / decoder / motion / detection / audio / recording source caps
|
|
14055
|
+
* via `ctx.api`. A stage whose source errors or times out becomes `null`
|
|
14056
|
+
* in the returned payload — one slow agent never breaks the whole call.
|
|
14057
|
+
*
|
|
14058
|
+
* Intended for "on open / on camera select" snapshots. Live deltas
|
|
14059
|
+
* keep arriving from the existing ~1Hz events the UI already subscribes to.
|
|
14060
|
+
*/
|
|
14061
|
+
getCameraStatus: method(object({ deviceId: number() }), CameraStatusSchema),
|
|
14062
|
+
/**
|
|
14063
|
+
* Server-composed aggregated status for multiple cameras in one call.
|
|
14064
|
+
*
|
|
14065
|
+
* `deviceIds` defaults to all cameras currently tracked by the
|
|
14066
|
+
* orchestrator's assignment map when omitted.
|
|
14067
|
+
*
|
|
14068
|
+
* Runs per-device composition in parallel (bounded). Use this to
|
|
14069
|
+
* populate the cluster assignments table and the pipeline flow overview
|
|
14070
|
+
* rail without issuing N parallel browser round-trips.
|
|
14071
|
+
*/
|
|
14072
|
+
getCameraStatuses: method(object({ deviceIds: array(number()).optional() }), array(CameraStatusSchema).readonly()),
|
|
13861
14073
|
/** List every template the operator has saved. */
|
|
13862
14074
|
listTemplates: method(_void(), array(PipelineTemplateSchema).readonly()),
|
|
13863
14075
|
/** Create a new named preset from a given CameraPipelineConfig. */
|
|
@@ -16869,6 +17081,69 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
16869
17081
|
kind: "mutation",
|
|
16870
17082
|
auth: "admin"
|
|
16871
17083
|
});
|
|
17084
|
+
var jobKindSchema = _enum([
|
|
17085
|
+
"install",
|
|
17086
|
+
"update",
|
|
17087
|
+
"uninstall",
|
|
17088
|
+
"restart"
|
|
17089
|
+
]);
|
|
17090
|
+
var taskPhaseSchema = _enum([
|
|
17091
|
+
"queued",
|
|
17092
|
+
"fetching",
|
|
17093
|
+
"staged",
|
|
17094
|
+
"validating",
|
|
17095
|
+
"applying",
|
|
17096
|
+
"restarting",
|
|
17097
|
+
"applied",
|
|
17098
|
+
"done",
|
|
17099
|
+
"failed",
|
|
17100
|
+
"skipped"
|
|
17101
|
+
]);
|
|
17102
|
+
var taskTargetSchema = _enum(["framework", "addon"]);
|
|
17103
|
+
var taskLogEntrySchema = object({
|
|
17104
|
+
tsMs: number(),
|
|
17105
|
+
nodeId: string(),
|
|
17106
|
+
packageName: string(),
|
|
17107
|
+
phase: taskPhaseSchema,
|
|
17108
|
+
message: string()
|
|
17109
|
+
});
|
|
17110
|
+
var lifecycleTaskSchema = object({
|
|
17111
|
+
taskId: string(),
|
|
17112
|
+
nodeId: string(),
|
|
17113
|
+
packageName: string(),
|
|
17114
|
+
fromVersion: string().nullable(),
|
|
17115
|
+
toVersion: string(),
|
|
17116
|
+
target: taskTargetSchema,
|
|
17117
|
+
phase: taskPhaseSchema,
|
|
17118
|
+
stagedPath: string().nullable(),
|
|
17119
|
+
attempts: number(),
|
|
17120
|
+
steps: array(taskLogEntrySchema),
|
|
17121
|
+
error: string().nullable(),
|
|
17122
|
+
startedAtMs: number().nullable(),
|
|
17123
|
+
finishedAtMs: number().nullable()
|
|
17124
|
+
});
|
|
17125
|
+
var lifecycleJobStateSchema = _enum([
|
|
17126
|
+
"running",
|
|
17127
|
+
"completed",
|
|
17128
|
+
"failed",
|
|
17129
|
+
"partially-failed",
|
|
17130
|
+
"cancelled"
|
|
17131
|
+
]);
|
|
17132
|
+
var lifecycleJobScopeSchema = _enum([
|
|
17133
|
+
"single",
|
|
17134
|
+
"bulk",
|
|
17135
|
+
"cluster"
|
|
17136
|
+
]);
|
|
17137
|
+
var lifecycleJobSchema = object({
|
|
17138
|
+
jobId: string(),
|
|
17139
|
+
kind: jobKindSchema,
|
|
17140
|
+
createdAtMs: number(),
|
|
17141
|
+
createdBy: string(),
|
|
17142
|
+
scope: lifecycleJobScopeSchema,
|
|
17143
|
+
tasks: array(lifecycleTaskSchema),
|
|
17144
|
+
state: lifecycleJobStateSchema,
|
|
17145
|
+
schemaVersion: literal(1)
|
|
17146
|
+
});
|
|
16872
17147
|
/**
|
|
16873
17148
|
* addons — system-scoped singleton capability for addon package
|
|
16874
17149
|
* management (install, update, configure, restart) and per-addon log
|
|
@@ -17051,7 +17326,7 @@ var BulkUpdatePhaseSchema = _enum([
|
|
|
17051
17326
|
"restarting",
|
|
17052
17327
|
"finalizing"
|
|
17053
17328
|
]);
|
|
17054
|
-
|
|
17329
|
+
object({
|
|
17055
17330
|
id: string(),
|
|
17056
17331
|
nodeId: string(),
|
|
17057
17332
|
startedAtMs: number(),
|
|
@@ -17154,20 +17429,7 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
17154
17429
|
}), UpdateFrameworkPackageResultSchema, {
|
|
17155
17430
|
kind: "mutation",
|
|
17156
17431
|
auth: "admin"
|
|
17157
|
-
}), method(object({
|
|
17158
|
-
nodeId: string(),
|
|
17159
|
-
items: array(object({
|
|
17160
|
-
name: string(),
|
|
17161
|
-
version: string(),
|
|
17162
|
-
isSystem: boolean()
|
|
17163
|
-
})).readonly()
|
|
17164
|
-
}), object({ id: string() }), {
|
|
17165
|
-
kind: "mutation",
|
|
17166
|
-
auth: "admin"
|
|
17167
|
-
}), method(object({ id: string() }), BulkUpdateStateSchema.nullable(), { auth: "admin" }), method(object({ id: string() }), object({ cancelled: boolean() }), {
|
|
17168
|
-
kind: "mutation",
|
|
17169
|
-
auth: "admin"
|
|
17170
|
-
}), method(object({ nodeId: string().optional() }), array(BulkUpdateStateSchema).readonly(), { auth: "admin" }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
17432
|
+
}), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
|
|
17171
17433
|
kind: "mutation",
|
|
17172
17434
|
auth: "admin"
|
|
17173
17435
|
}), method(object({ packageName: string() }), object({ success: literal(true) }), {
|
|
@@ -17189,6 +17451,24 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
|
|
|
17189
17451
|
kind: "mutation",
|
|
17190
17452
|
auth: "admin"
|
|
17191
17453
|
}), method(CustomActionInputSchema, unknown(), { kind: "mutation" }), method(object({
|
|
17454
|
+
kind: _enum([
|
|
17455
|
+
"install",
|
|
17456
|
+
"update",
|
|
17457
|
+
"uninstall",
|
|
17458
|
+
"restart"
|
|
17459
|
+
]),
|
|
17460
|
+
targets: array(object({
|
|
17461
|
+
name: string().min(1),
|
|
17462
|
+
version: string().min(1)
|
|
17463
|
+
})).min(1),
|
|
17464
|
+
nodeIds: array(string()).optional()
|
|
17465
|
+
}), object({ jobId: string() }), {
|
|
17466
|
+
kind: "mutation",
|
|
17467
|
+
auth: "admin"
|
|
17468
|
+
}), method(object({ jobId: string() }), lifecycleJobSchema.nullable(), { auth: "admin" }), method(object({ activeOnly: boolean().optional() }), array(lifecycleJobSchema), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
17469
|
+
kind: "mutation",
|
|
17470
|
+
auth: "admin"
|
|
17471
|
+
}), method(object({
|
|
17192
17472
|
addonId: string(),
|
|
17193
17473
|
level: LogLevelSchema$1.optional()
|
|
17194
17474
|
}), LogStreamEntrySchema, { kind: "subscription" });
|
|
@@ -17229,7 +17509,7 @@ Object.freeze({
|
|
|
17229
17509
|
addonId: null,
|
|
17230
17510
|
access: "create"
|
|
17231
17511
|
},
|
|
17232
|
-
"addons.
|
|
17512
|
+
"addons.cancelJob": {
|
|
17233
17513
|
capName: "addons",
|
|
17234
17514
|
capScope: "system",
|
|
17235
17515
|
addonId: null,
|
|
@@ -17259,7 +17539,7 @@ Object.freeze({
|
|
|
17259
17539
|
addonId: null,
|
|
17260
17540
|
access: "view"
|
|
17261
17541
|
},
|
|
17262
|
-
"addons.
|
|
17542
|
+
"addons.getJob": {
|
|
17263
17543
|
capName: "addons",
|
|
17264
17544
|
capScope: "system",
|
|
17265
17545
|
addonId: null,
|
|
@@ -17307,19 +17587,19 @@ Object.freeze({
|
|
|
17307
17587
|
addonId: null,
|
|
17308
17588
|
access: "view"
|
|
17309
17589
|
},
|
|
17310
|
-
"addons.
|
|
17590
|
+
"addons.listCapabilityProviders": {
|
|
17311
17591
|
capName: "addons",
|
|
17312
17592
|
capScope: "system",
|
|
17313
17593
|
addonId: null,
|
|
17314
17594
|
access: "view"
|
|
17315
17595
|
},
|
|
17316
|
-
"addons.
|
|
17596
|
+
"addons.listFrameworkPackages": {
|
|
17317
17597
|
capName: "addons",
|
|
17318
17598
|
capScope: "system",
|
|
17319
17599
|
addonId: null,
|
|
17320
17600
|
access: "view"
|
|
17321
17601
|
},
|
|
17322
|
-
"addons.
|
|
17602
|
+
"addons.listJobs": {
|
|
17323
17603
|
capName: "addons",
|
|
17324
17604
|
capScope: "system",
|
|
17325
17605
|
addonId: null,
|
|
@@ -17403,7 +17683,7 @@ Object.freeze({
|
|
|
17403
17683
|
addonId: null,
|
|
17404
17684
|
access: "create"
|
|
17405
17685
|
},
|
|
17406
|
-
"addons.
|
|
17686
|
+
"addons.startJob": {
|
|
17407
17687
|
capName: "addons",
|
|
17408
17688
|
capScope: "system",
|
|
17409
17689
|
addonId: null,
|
|
@@ -19629,6 +19909,12 @@ Object.freeze({
|
|
|
19629
19909
|
addonId: null,
|
|
19630
19910
|
access: "view"
|
|
19631
19911
|
},
|
|
19912
|
+
"pipelineExecutor.getEngineProvisioning": {
|
|
19913
|
+
capName: "pipeline-executor",
|
|
19914
|
+
capScope: "system",
|
|
19915
|
+
addonId: null,
|
|
19916
|
+
access: "view"
|
|
19917
|
+
},
|
|
19632
19918
|
"pipelineExecutor.getGlobalPipelineConfig": {
|
|
19633
19919
|
capName: "pipeline-executor",
|
|
19634
19920
|
capScope: "system",
|
|
@@ -19833,6 +20119,18 @@ Object.freeze({
|
|
|
19833
20119
|
addonId: null,
|
|
19834
20120
|
access: "view"
|
|
19835
20121
|
},
|
|
20122
|
+
"pipelineOrchestrator.getCameraStatus": {
|
|
20123
|
+
capName: "pipeline-orchestrator",
|
|
20124
|
+
capScope: "system",
|
|
20125
|
+
addonId: null,
|
|
20126
|
+
access: "view"
|
|
20127
|
+
},
|
|
20128
|
+
"pipelineOrchestrator.getCameraStatuses": {
|
|
20129
|
+
capName: "pipeline-orchestrator",
|
|
20130
|
+
capScope: "system",
|
|
20131
|
+
addonId: null,
|
|
20132
|
+
access: "view"
|
|
20133
|
+
},
|
|
19836
20134
|
"pipelineOrchestrator.getCameraStepOverrides": {
|
|
19837
20135
|
capName: "pipeline-orchestrator",
|
|
19838
20136
|
capScope: "system",
|
|
@@ -19917,6 +20215,12 @@ Object.freeze({
|
|
|
19917
20215
|
addonId: null,
|
|
19918
20216
|
access: "create"
|
|
19919
20217
|
},
|
|
20218
|
+
"pipelineOrchestrator.setAgentMaxCameras": {
|
|
20219
|
+
capName: "pipeline-orchestrator",
|
|
20220
|
+
capScope: "system",
|
|
20221
|
+
addonId: null,
|
|
20222
|
+
access: "create"
|
|
20223
|
+
},
|
|
19920
20224
|
"pipelineOrchestrator.setCameraPipelineForAgent": {
|
|
19921
20225
|
capName: "pipeline-orchestrator",
|
|
19922
20226
|
capScope: "system",
|
|
@@ -21390,6 +21694,32 @@ Object.freeze({
|
|
|
21390
21694
|
"network-access": "ingress",
|
|
21391
21695
|
"smtp-provider": "email"
|
|
21392
21696
|
});
|
|
21697
|
+
var frameworkSwapPackageSchema = object({
|
|
21698
|
+
name: string(),
|
|
21699
|
+
stagedPath: string(),
|
|
21700
|
+
backupPath: string(),
|
|
21701
|
+
toVersion: string(),
|
|
21702
|
+
fromVersion: string().nullable()
|
|
21703
|
+
});
|
|
21704
|
+
object({
|
|
21705
|
+
jobId: string(),
|
|
21706
|
+
taskId: string(),
|
|
21707
|
+
packages: array(frameworkSwapPackageSchema),
|
|
21708
|
+
requestedAtMs: number(),
|
|
21709
|
+
schemaVersion: literal(1)
|
|
21710
|
+
});
|
|
21711
|
+
object({
|
|
21712
|
+
jobId: string(),
|
|
21713
|
+
taskId: string(),
|
|
21714
|
+
backups: array(object({
|
|
21715
|
+
name: string(),
|
|
21716
|
+
backupPath: string(),
|
|
21717
|
+
livePath: string()
|
|
21718
|
+
})),
|
|
21719
|
+
appliedAtMs: number(),
|
|
21720
|
+
bootAttempts: number(),
|
|
21721
|
+
schemaVersion: literal(1)
|
|
21722
|
+
});
|
|
21393
21723
|
/**
|
|
21394
21724
|
* Promise-based timer helpers — used everywhere the codebase needs to
|
|
21395
21725
|
* wait, back off, or schedule a retry. Before these helpers landed, each
|
|
@@ -21921,7 +22251,10 @@ var StoredAgentAddonConfigSchema = object({
|
|
|
21921
22251
|
modelId: string(),
|
|
21922
22252
|
settings: record(string(), unknown()).readonly()
|
|
21923
22253
|
});
|
|
21924
|
-
var StoredAgentPipelineSettingsSchema = object({
|
|
22254
|
+
var StoredAgentPipelineSettingsSchema = object({
|
|
22255
|
+
addonDefaults: record(string(), StoredAgentAddonConfigSchema).readonly(),
|
|
22256
|
+
maxCameras: number().int().nonnegative().nullable().default(null)
|
|
22257
|
+
});
|
|
21925
22258
|
var AgentSettingsMapSchema = record(string(), StoredAgentPipelineSettingsSchema);
|
|
21926
22259
|
var StoredCameraStepOverridePatchSchema = object({
|
|
21927
22260
|
enabled: boolean().optional(),
|
|
@@ -21966,10 +22299,26 @@ function computeCapacityScore(load) {
|
|
|
21966
22299
|
return load.attachedCameras * Math.max(load.avgInferenceFps, 0) + Math.max(load.queueDepthTotal, 0);
|
|
21967
22300
|
}
|
|
21968
22301
|
/**
|
|
22302
|
+
* Returns true when the node has remaining capacity.
|
|
22303
|
+
* A node is eligible iff `cap` is unlimited (null/absent/<=0) OR
|
|
22304
|
+
* its `attachedCameras` count is strictly less than the cap.
|
|
22305
|
+
* Pins count toward the cap via `attachedCameras`.
|
|
22306
|
+
*/
|
|
22307
|
+
function isEligible(node, caps) {
|
|
22308
|
+
const cap = caps?.[node.nodeId];
|
|
22309
|
+
if (cap === null || cap === void 0 || cap <= 0) return true;
|
|
22310
|
+
return node.attachedCameras < cap;
|
|
22311
|
+
}
|
|
22312
|
+
/**
|
|
21969
22313
|
* Run the two-level camera balancer.
|
|
21970
22314
|
*
|
|
21971
|
-
* L1 (manual affinity): if `preferredAgent` names an online node
|
|
21972
|
-
*
|
|
22315
|
+
* L1 (manual affinity): if `preferredAgent` names an online node AND that
|
|
22316
|
+
* node is under its `maxCameras` cap, return it. If the node is online but at
|
|
22317
|
+
* or over cap, return `{kind:'pending'}` — a pinned camera is never silently
|
|
22318
|
+
* over-assigned.
|
|
22319
|
+
*
|
|
22320
|
+
* L2 (capacity): filter to eligible nodes and pick the lowest capacity score.
|
|
22321
|
+
* If all nodes are at/over cap, return `{kind:'pending'}`.
|
|
21973
22322
|
*
|
|
21974
22323
|
* Returns `null` when no runners are online. The orchestrator decides how to
|
|
21975
22324
|
* react — typically by logging and deferring the assignment until a runner
|
|
@@ -21978,19 +22327,32 @@ function computeCapacityScore(load) {
|
|
|
21978
22327
|
function balance(input) {
|
|
21979
22328
|
const online = input.nodes.filter((n) => n.nodeId.length > 0);
|
|
21980
22329
|
if (online.length === 0) return null;
|
|
22330
|
+
const eligible = online.filter((n) => isEligible(n, input.nodeCaps));
|
|
21981
22331
|
if (input.preferredAgent) {
|
|
21982
|
-
const
|
|
21983
|
-
if (
|
|
21984
|
-
|
|
21985
|
-
|
|
21986
|
-
|
|
21987
|
-
|
|
22332
|
+
const pinnedOnline = online.find((n) => n.nodeId === input.preferredAgent);
|
|
22333
|
+
if (pinnedOnline) {
|
|
22334
|
+
if (eligible.some((n) => n.nodeId === pinnedOnline.nodeId)) return {
|
|
22335
|
+
kind: "assigned",
|
|
22336
|
+
agentNodeId: pinnedOnline.nodeId,
|
|
22337
|
+
reason: "manual",
|
|
22338
|
+
score: computeCapacityScore(pinnedOnline)
|
|
22339
|
+
};
|
|
22340
|
+
return {
|
|
22341
|
+
kind: "pending",
|
|
22342
|
+
reason: "over-cap"
|
|
22343
|
+
};
|
|
22344
|
+
}
|
|
21988
22345
|
}
|
|
21989
|
-
|
|
22346
|
+
if (eligible.length === 0) return {
|
|
22347
|
+
kind: "pending",
|
|
22348
|
+
reason: "over-cap"
|
|
22349
|
+
};
|
|
22350
|
+
const best = eligible.map((node) => ({
|
|
21990
22351
|
node,
|
|
21991
22352
|
score: computeCapacityScore(node)
|
|
21992
22353
|
})).toSorted((a, b) => a.score - b.score)[0];
|
|
21993
22354
|
return {
|
|
22355
|
+
kind: "assigned",
|
|
21994
22356
|
agentNodeId: best.node.nodeId,
|
|
21995
22357
|
reason: "capacity",
|
|
21996
22358
|
score: best.score
|
|
@@ -22409,6 +22771,139 @@ var PipelineWatchdog = class {
|
|
|
22409
22771
|
}
|
|
22410
22772
|
};
|
|
22411
22773
|
//#endregion
|
|
22774
|
+
//#region src/camera-status/compose-camera-status.ts
|
|
22775
|
+
function mapAssignment(input) {
|
|
22776
|
+
return {
|
|
22777
|
+
detectionNodeId: input.detectionNodeId,
|
|
22778
|
+
decoderNodeId: input.decoderNodeId,
|
|
22779
|
+
audioNodeId: input.audioNodeId,
|
|
22780
|
+
pinned: {
|
|
22781
|
+
detection: input.pinned.detection,
|
|
22782
|
+
decoder: input.pinned.decoder,
|
|
22783
|
+
audio: input.pinned.audio
|
|
22784
|
+
},
|
|
22785
|
+
reasons: {
|
|
22786
|
+
detection: input.reasons.detection,
|
|
22787
|
+
decoder: input.reasons.decoder,
|
|
22788
|
+
audio: input.reasons.audio
|
|
22789
|
+
}
|
|
22790
|
+
};
|
|
22791
|
+
}
|
|
22792
|
+
function mapSource(sourceResult) {
|
|
22793
|
+
if (sourceResult === null) return { streams: [] };
|
|
22794
|
+
return { streams: sourceResult.streams.map((s) => ({
|
|
22795
|
+
camStreamId: s.camStreamId,
|
|
22796
|
+
codec: s.codec,
|
|
22797
|
+
width: s.width,
|
|
22798
|
+
height: s.height,
|
|
22799
|
+
fps: s.fps,
|
|
22800
|
+
kind: s.kind
|
|
22801
|
+
})) };
|
|
22802
|
+
}
|
|
22803
|
+
function mapBroker(brokerResult) {
|
|
22804
|
+
if (brokerResult === null) return null;
|
|
22805
|
+
return {
|
|
22806
|
+
profiles: brokerResult.profiles.map((p) => ({
|
|
22807
|
+
profile: p.profile,
|
|
22808
|
+
status: p.status,
|
|
22809
|
+
codec: p.codec,
|
|
22810
|
+
width: p.width,
|
|
22811
|
+
height: p.height,
|
|
22812
|
+
subscribers: p.subscribers,
|
|
22813
|
+
inFps: p.inFps,
|
|
22814
|
+
outFps: p.outFps
|
|
22815
|
+
})),
|
|
22816
|
+
webrtcSessions: brokerResult.webrtcSessions,
|
|
22817
|
+
rtspRestream: brokerResult.rtspRestream
|
|
22818
|
+
};
|
|
22819
|
+
}
|
|
22820
|
+
function mapDecoderShm(shm) {
|
|
22821
|
+
return {
|
|
22822
|
+
framesWritten: shm.framesWritten,
|
|
22823
|
+
getFrameHits: shm.getFrameHits,
|
|
22824
|
+
getFrameMisses: shm.getFrameMisses,
|
|
22825
|
+
budgetMb: shm.budgetMb
|
|
22826
|
+
};
|
|
22827
|
+
}
|
|
22828
|
+
function mapDecoder(decoderResult) {
|
|
22829
|
+
if (decoderResult === null) return null;
|
|
22830
|
+
return {
|
|
22831
|
+
nodeId: decoderResult.nodeId,
|
|
22832
|
+
formats: [...decoderResult.formats],
|
|
22833
|
+
sessionCount: decoderResult.sessionCount,
|
|
22834
|
+
shm: mapDecoderShm(decoderResult.shm)
|
|
22835
|
+
};
|
|
22836
|
+
}
|
|
22837
|
+
function mapMotion(motionResult) {
|
|
22838
|
+
if (motionResult === null) return null;
|
|
22839
|
+
return {
|
|
22840
|
+
enabled: motionResult.enabled,
|
|
22841
|
+
fps: motionResult.fps
|
|
22842
|
+
};
|
|
22843
|
+
}
|
|
22844
|
+
function mapProvisioning(p) {
|
|
22845
|
+
if (p.error !== void 0) return {
|
|
22846
|
+
state: p.state,
|
|
22847
|
+
error: p.error
|
|
22848
|
+
};
|
|
22849
|
+
return { state: p.state };
|
|
22850
|
+
}
|
|
22851
|
+
function mapDetection(detectionResult) {
|
|
22852
|
+
if (detectionResult === null) return null;
|
|
22853
|
+
const phase = detectionResult.phase;
|
|
22854
|
+
return {
|
|
22855
|
+
nodeId: detectionResult.nodeId,
|
|
22856
|
+
engine: {
|
|
22857
|
+
backend: detectionResult.engine.backend,
|
|
22858
|
+
device: detectionResult.engine.device
|
|
22859
|
+
},
|
|
22860
|
+
phase,
|
|
22861
|
+
configuredFps: detectionResult.configuredFps,
|
|
22862
|
+
actualFps: detectionResult.actualFps,
|
|
22863
|
+
queueDepth: detectionResult.queueDepth,
|
|
22864
|
+
avgInferenceMs: detectionResult.avgInferenceMs,
|
|
22865
|
+
provisioning: mapProvisioning(detectionResult.provisioning)
|
|
22866
|
+
};
|
|
22867
|
+
}
|
|
22868
|
+
function mapAudio(audioResult) {
|
|
22869
|
+
if (audioResult === null) return null;
|
|
22870
|
+
return {
|
|
22871
|
+
nodeId: audioResult.nodeId,
|
|
22872
|
+
enabled: audioResult.enabled
|
|
22873
|
+
};
|
|
22874
|
+
}
|
|
22875
|
+
function mapRecording(recordingResult) {
|
|
22876
|
+
if (recordingResult === null) return null;
|
|
22877
|
+
return {
|
|
22878
|
+
mode: recordingResult.mode,
|
|
22879
|
+
active: recordingResult.active,
|
|
22880
|
+
storageBytes: recordingResult.storageBytes
|
|
22881
|
+
};
|
|
22882
|
+
}
|
|
22883
|
+
/**
|
|
22884
|
+
* Pure function that composes a `CameraStatus` from per-stage fetch results.
|
|
22885
|
+
*
|
|
22886
|
+
* - `assignment` is always built from orchestrator-local data (never null).
|
|
22887
|
+
* - `source` always present: defaults to `{ streams: [] }` when sourceResult is null.
|
|
22888
|
+
* - Every other block is null when its stage result is null (graceful degradation).
|
|
22889
|
+
* - `fetchedAt` is stamped exactly as provided — never calls `Date.now()`.
|
|
22890
|
+
* - No mutation of the input.
|
|
22891
|
+
*/
|
|
22892
|
+
function composeCameraStatus(input) {
|
|
22893
|
+
return {
|
|
22894
|
+
deviceId: input.deviceId,
|
|
22895
|
+
assignment: mapAssignment(input),
|
|
22896
|
+
source: mapSource(input.sourceResult),
|
|
22897
|
+
broker: mapBroker(input.brokerResult),
|
|
22898
|
+
decoder: mapDecoder(input.decoderResult),
|
|
22899
|
+
motion: mapMotion(input.motionResult),
|
|
22900
|
+
detection: mapDetection(input.detectionResult),
|
|
22901
|
+
audio: mapAudio(input.audioResult),
|
|
22902
|
+
recording: mapRecording(input.recordingResult),
|
|
22903
|
+
fetchedAt: input.fetchedAt
|
|
22904
|
+
};
|
|
22905
|
+
}
|
|
22906
|
+
//#endregion
|
|
22412
22907
|
//#region src/index.ts
|
|
22413
22908
|
var PHASE_MODE_VALUES = new Set([
|
|
22414
22909
|
"disabled",
|
|
@@ -23166,8 +23661,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23166
23661
|
});
|
|
23167
23662
|
return {
|
|
23168
23663
|
success: true,
|
|
23169
|
-
|
|
23170
|
-
reason: "capacity"
|
|
23664
|
+
kind: "pending"
|
|
23171
23665
|
};
|
|
23172
23666
|
}
|
|
23173
23667
|
}
|
|
@@ -23177,14 +23671,22 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23177
23671
|
const preferredAgent = typeof pipelinePin === "string" && pipelinePin !== "auto" ? pipelinePin : legacyPreferred;
|
|
23178
23672
|
const decision = balance({
|
|
23179
23673
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
23180
|
-
preferredAgent
|
|
23674
|
+
preferredAgent,
|
|
23675
|
+
nodeCaps: this.buildNodeCaps()
|
|
23181
23676
|
});
|
|
23182
|
-
|
|
23183
|
-
if (
|
|
23677
|
+
if (!decision) throw new Error(`dispatchCamera: no runner available for ${runnerConfig.deviceId}`);
|
|
23678
|
+
if (decision.kind === "pending") {
|
|
23679
|
+
this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId: runnerConfig.deviceId } });
|
|
23680
|
+
return {
|
|
23681
|
+
success: true,
|
|
23682
|
+
kind: "pending"
|
|
23683
|
+
};
|
|
23684
|
+
}
|
|
23685
|
+
const targetNodeId = decision.agentNodeId;
|
|
23184
23686
|
await this.attachOn(targetNodeId, runnerConfig);
|
|
23185
23687
|
if (targetNodeId === this.localNodeId) this.pipelineWatchdog?.register(this.buildWatchdogCamera(runnerConfig, String(runnerConfig.deviceId)));
|
|
23186
|
-
const reason = decision
|
|
23187
|
-
const pinned = decision
|
|
23688
|
+
const reason = decision.reason;
|
|
23689
|
+
const pinned = decision.reason === "manual";
|
|
23188
23690
|
this.recordAssignment(runnerConfig.deviceId, targetNodeId, reason, pinned);
|
|
23189
23691
|
const decoderNodeId = await this.resolveDecoderNode(runnerConfig.deviceId, targetNodeId);
|
|
23190
23692
|
this.ctx.logger.info("dispatchCamera", {
|
|
@@ -23194,12 +23696,13 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23194
23696
|
},
|
|
23195
23697
|
meta: {
|
|
23196
23698
|
reason,
|
|
23197
|
-
score: decision
|
|
23699
|
+
score: decision.score,
|
|
23198
23700
|
decoderNodeId
|
|
23199
23701
|
}
|
|
23200
23702
|
});
|
|
23201
23703
|
return {
|
|
23202
23704
|
success: true,
|
|
23705
|
+
kind: "assigned",
|
|
23203
23706
|
agentNodeId: targetNodeId,
|
|
23204
23707
|
reason
|
|
23205
23708
|
};
|
|
@@ -23273,10 +23776,13 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23273
23776
|
if (cached) {
|
|
23274
23777
|
const decision = balance({
|
|
23275
23778
|
nodes: await this.collectAgentLoad({ onlyEnabled: true }),
|
|
23276
|
-
preferredAgent: null
|
|
23779
|
+
preferredAgent: null,
|
|
23780
|
+
nodeCaps: this.buildNodeCaps()
|
|
23277
23781
|
});
|
|
23278
|
-
|
|
23279
|
-
if (
|
|
23782
|
+
if (!decision) this.ctx.logger.warn("unassignPipeline: no runner available, leaving unassigned", { tags: { deviceId: input.deviceId } });
|
|
23783
|
+
else if (decision.kind === "pending") this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId: input.deviceId } });
|
|
23784
|
+
else {
|
|
23785
|
+
const targetNodeId = decision.agentNodeId;
|
|
23280
23786
|
if (current && current.agentNodeId !== targetNodeId) await this.detachOn(current.agentNodeId, input.deviceId).catch((err) => {
|
|
23281
23787
|
const msg = errMsg(err);
|
|
23282
23788
|
this.ctx.logger.debug("unassignPipeline detach-old failed", {
|
|
@@ -23285,7 +23791,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23285
23791
|
});
|
|
23286
23792
|
});
|
|
23287
23793
|
if (!current || current.agentNodeId !== targetNodeId) await this.attachOn(targetNodeId, cached);
|
|
23288
|
-
const reason = decision
|
|
23794
|
+
const reason = decision.reason === "manual" ? "capacity" : decision.reason;
|
|
23289
23795
|
this.recordAssignment(input.deviceId, targetNodeId, reason, false);
|
|
23290
23796
|
this.ctx.logger.info("unassignPipeline: re-dispatched via auto", {
|
|
23291
23797
|
tags: {
|
|
@@ -23296,7 +23802,6 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23296
23802
|
});
|
|
23297
23803
|
return { success: true };
|
|
23298
23804
|
}
|
|
23299
|
-
this.ctx.logger.warn("unassignPipeline: no runner available, leaving unassigned", { tags: { deviceId: input.deviceId } });
|
|
23300
23805
|
}
|
|
23301
23806
|
if (current) {
|
|
23302
23807
|
await this.detachOn(current.agentNodeId, input.deviceId).catch((err) => {
|
|
@@ -23314,15 +23819,21 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23314
23819
|
async rebalance() {
|
|
23315
23820
|
if (!this.ctx) throw new Error("PipelineOrchestrator: rebalance called before initialize");
|
|
23316
23821
|
const loads = await this.collectAgentLoad({ onlyEnabled: true });
|
|
23822
|
+
const nodeCaps = this.buildNodeCaps();
|
|
23317
23823
|
let migrated = 0;
|
|
23318
23824
|
for (const [deviceId, config] of this.cameraConfigs) {
|
|
23319
23825
|
const current = this.assignments.get(deviceId);
|
|
23320
23826
|
if (current?.pinned) continue;
|
|
23321
23827
|
const decision = balance({
|
|
23322
23828
|
nodes: loads,
|
|
23323
|
-
preferredAgent: await this.readPreferredAgent(deviceId)
|
|
23829
|
+
preferredAgent: await this.readPreferredAgent(deviceId),
|
|
23830
|
+
nodeCaps
|
|
23324
23831
|
});
|
|
23325
23832
|
if (!decision) continue;
|
|
23833
|
+
if (decision.kind === "pending") {
|
|
23834
|
+
this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId } });
|
|
23835
|
+
continue;
|
|
23836
|
+
}
|
|
23326
23837
|
if (current && current.agentNodeId === decision.agentNodeId) continue;
|
|
23327
23838
|
if (current) await this.detachOn(current.agentNodeId, deviceId).catch((err) => {
|
|
23328
23839
|
const msg = errMsg(err);
|
|
@@ -23460,15 +23971,6 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23460
23971
|
nodeId
|
|
23461
23972
|
});
|
|
23462
23973
|
}
|
|
23463
|
-
async localRunnerNodeId() {
|
|
23464
|
-
const api = this.ctx.api;
|
|
23465
|
-
if (!api) return null;
|
|
23466
|
-
try {
|
|
23467
|
-
return (await api.pipelineRunner.getLocalLoad.query({ nodeId: this.localNodeId }))?.nodeId ?? this.localNodeId;
|
|
23468
|
-
} catch {
|
|
23469
|
-
return this.localNodeId;
|
|
23470
|
-
}
|
|
23471
|
-
}
|
|
23472
23974
|
/**
|
|
23473
23975
|
* Enumerate every runner-capable node currently known (populated via
|
|
23474
23976
|
* AgentOnline/AgentOffline events). Used to populate the `enabledNodes`
|
|
@@ -23623,6 +24125,18 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23623
24125
|
return null;
|
|
23624
24126
|
}
|
|
23625
24127
|
}
|
|
24128
|
+
/**
|
|
24129
|
+
* Build a per-node camera cap map from the persisted agent settings.
|
|
24130
|
+
* Returns `null` for nodes where `maxCameras` is null (unlimited).
|
|
24131
|
+
* Used by every `balance()` call so the balancer can honour operator-set
|
|
24132
|
+
* per-node maximums without polling the store on each decision.
|
|
24133
|
+
*/
|
|
24134
|
+
buildNodeCaps() {
|
|
24135
|
+
const blob = this.agentSettingsState.get();
|
|
24136
|
+
const caps = {};
|
|
24137
|
+
for (const [nodeId, settings] of Object.entries(blob)) caps[nodeId] = settings.maxCameras ?? null;
|
|
24138
|
+
return caps;
|
|
24139
|
+
}
|
|
23626
24140
|
async readAudioNodePin(deviceId) {
|
|
23627
24141
|
if (!this.ctx?.settings) return null;
|
|
23628
24142
|
try {
|
|
@@ -23765,13 +24279,19 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
23765
24279
|
for (const { deviceId, config } of affected) {
|
|
23766
24280
|
const decision = balance({
|
|
23767
24281
|
nodes: loads,
|
|
23768
|
-
preferredAgent: null
|
|
24282
|
+
preferredAgent: null,
|
|
24283
|
+
nodeCaps: this.buildNodeCaps()
|
|
23769
24284
|
});
|
|
23770
24285
|
if (!decision) {
|
|
23771
24286
|
this.ctx.logger.error("Failover: no online runner", { tags: { deviceId } });
|
|
23772
24287
|
this.assignments.delete(deviceId);
|
|
23773
24288
|
continue;
|
|
23774
24289
|
}
|
|
24290
|
+
if (decision.kind === "pending") {
|
|
24291
|
+
this.ctx.logger.warn("camera left pending — all nodes at maxCameras", { tags: { deviceId } });
|
|
24292
|
+
this.assignments.delete(deviceId);
|
|
24293
|
+
continue;
|
|
24294
|
+
}
|
|
23775
24295
|
try {
|
|
23776
24296
|
await this.attachOn(decision.agentNodeId, config);
|
|
23777
24297
|
this.recordAssignment(deviceId, decision.agentNodeId, "failover", false);
|
|
@@ -24251,7 +24771,10 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24251
24771
|
}
|
|
24252
24772
|
async setAgentAddonDefaults(input) {
|
|
24253
24773
|
let existing = (await this.readAgentSettingsMap())[input.agentNodeId];
|
|
24254
|
-
if (!existing)
|
|
24774
|
+
if (!existing) {
|
|
24775
|
+
await this.seedAgentSettingsFromCatalog(input.agentNodeId);
|
|
24776
|
+
existing = (await this.readAgentSettingsMap())[input.agentNodeId];
|
|
24777
|
+
}
|
|
24255
24778
|
if (!existing) await this.writeAgentSettings(input.agentNodeId, { addonDefaults: { ...input.defaults } });
|
|
24256
24779
|
else await this.writeAgentSettings(input.agentNodeId, {
|
|
24257
24780
|
...existing,
|
|
@@ -24289,6 +24812,22 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24289
24812
|
removed: true
|
|
24290
24813
|
};
|
|
24291
24814
|
}
|
|
24815
|
+
async setAgentMaxCameras(input) {
|
|
24816
|
+
const existing = (await this.readAgentSettingsMap())[input.agentNodeId];
|
|
24817
|
+
const next = existing ? {
|
|
24818
|
+
...existing,
|
|
24819
|
+
maxCameras: input.maxCameras
|
|
24820
|
+
} : {
|
|
24821
|
+
addonDefaults: {},
|
|
24822
|
+
maxCameras: input.maxCameras
|
|
24823
|
+
};
|
|
24824
|
+
await this.writeAgentSettings(input.agentNodeId, next);
|
|
24825
|
+
this.ctx.logger.info("agentSettings.maxCameras updated", {
|
|
24826
|
+
tags: { nodeId: input.agentNodeId },
|
|
24827
|
+
meta: { maxCameras: input.maxCameras }
|
|
24828
|
+
});
|
|
24829
|
+
return { success: true };
|
|
24830
|
+
}
|
|
24292
24831
|
async getCameraSettings(input) {
|
|
24293
24832
|
return (await this.readCameraSettingsMap())[String(input.deviceId)] ?? null;
|
|
24294
24833
|
}
|
|
@@ -24418,6 +24957,204 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24418
24957
|
this.ctx.logger.info("template deleted", { meta: { templateId: input.id } });
|
|
24419
24958
|
return { success: true };
|
|
24420
24959
|
}
|
|
24960
|
+
/**
|
|
24961
|
+
* Races a promise against a timeout. Returns `null` on timeout OR rejection.
|
|
24962
|
+
* Never throws — individual stage failures become `null` in the aggregate.
|
|
24963
|
+
*
|
|
24964
|
+
* @param p The stage fetch promise.
|
|
24965
|
+
* @param ms Timeout in milliseconds.
|
|
24966
|
+
*/
|
|
24967
|
+
boundedStage(p, ms) {
|
|
24968
|
+
let timer;
|
|
24969
|
+
const timeout = new Promise((resolve) => {
|
|
24970
|
+
timer = setTimeout(() => resolve(null), ms);
|
|
24971
|
+
});
|
|
24972
|
+
return Promise.race([p, timeout]).catch(() => null).finally(() => {
|
|
24973
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
24974
|
+
});
|
|
24975
|
+
}
|
|
24976
|
+
/**
|
|
24977
|
+
* Server-composed aggregated status for a single camera.
|
|
24978
|
+
*
|
|
24979
|
+
* Fans out in parallel (bounded, per-stage graceful degradation) to
|
|
24980
|
+
* broker / decoder / motion / detection / audio / recording source caps
|
|
24981
|
+
* via `ctx.api`. A stage whose source errors or times out becomes `null`
|
|
24982
|
+
* in the returned payload — one slow agent never breaks the whole call.
|
|
24983
|
+
*/
|
|
24984
|
+
async getCameraStatus(input) {
|
|
24985
|
+
const { deviceId } = input;
|
|
24986
|
+
const api = this.ctx.api;
|
|
24987
|
+
const STAGE_TIMEOUT_MS = 3e3;
|
|
24988
|
+
const pipelineAssignment = this.assignments.get(deviceId) ?? null;
|
|
24989
|
+
const detectionNodeId = pipelineAssignment?.agentNodeId ?? null;
|
|
24990
|
+
const decoderPinRaw = (api ? await this.ctx.settings?.readDeviceStore(deviceId).catch(() => ({})) ?? {} : {})["decoderNodeId"];
|
|
24991
|
+
const decoderPinned = typeof decoderPinRaw === "string" && decoderPinRaw !== "auto";
|
|
24992
|
+
const decoderNodeId = detectionNodeId ? await this.resolveDecoderNode(deviceId, detectionNodeId).catch(() => null) : null;
|
|
24993
|
+
const audioAssignment = this.audioAssignments.get(deviceId) ?? null;
|
|
24994
|
+
const audioNodeId = audioAssignment?.nodeId ?? null;
|
|
24995
|
+
const audioPinned = audioAssignment?.pinned ?? false;
|
|
24996
|
+
const pinned = {
|
|
24997
|
+
detection: pipelineAssignment?.pinned ?? false,
|
|
24998
|
+
decoder: decoderPinned,
|
|
24999
|
+
audio: audioPinned
|
|
25000
|
+
};
|
|
25001
|
+
const reasons = {
|
|
25002
|
+
detection: pipelineAssignment?.reason,
|
|
25003
|
+
decoder: decoderPinned ? "manual" : "co-located",
|
|
25004
|
+
audio: audioPinned ? "manual" : void 0
|
|
25005
|
+
};
|
|
25006
|
+
const allSlotsFetch = api ? api.streamBroker.listAllProfileSlots.query() : null;
|
|
25007
|
+
const sourceFetch = api && allSlotsFetch ? this.boundedStage(allSlotsFetch.then((slots) => {
|
|
25008
|
+
return { streams: slots.filter((s) => s.deviceId === deviceId).map((s) => ({
|
|
25009
|
+
camStreamId: s.sourceCamStreamId ?? s.brokerId,
|
|
25010
|
+
codec: s.codec ?? "",
|
|
25011
|
+
width: s.resolution?.width ?? 0,
|
|
25012
|
+
height: s.resolution?.height ?? 0,
|
|
25013
|
+
fps: 0,
|
|
25014
|
+
kind: s.profile
|
|
25015
|
+
})) };
|
|
25016
|
+
}), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25017
|
+
const WEBRTC_KINDS = new Set([
|
|
25018
|
+
"webrtc-browser",
|
|
25019
|
+
"webrtc-mobile",
|
|
25020
|
+
"webrtc-whep"
|
|
25021
|
+
]);
|
|
25022
|
+
const brokerFetch = api && allSlotsFetch ? this.boundedStage(allSlotsFetch.then(async (slots) => {
|
|
25023
|
+
const deviceSlots = slots.filter((s) => s.deviceId === deviceId);
|
|
25024
|
+
if (deviceSlots.length === 0) return {
|
|
25025
|
+
profiles: [],
|
|
25026
|
+
webrtcSessions: 0,
|
|
25027
|
+
rtspRestream: false
|
|
25028
|
+
};
|
|
25029
|
+
const [statsAndClients, rtspEntry] = await Promise.all([Promise.all(deviceSlots.map(async (slot) => {
|
|
25030
|
+
return {
|
|
25031
|
+
slot,
|
|
25032
|
+
stats: await api.streamBroker.getBrokerStats.query({ brokerId: slot.brokerId }).catch(() => null),
|
|
25033
|
+
clients: await api.streamBroker.listClients.query({ brokerId: slot.brokerId }).catch(() => null)
|
|
25034
|
+
};
|
|
25035
|
+
})), api.streamBroker.getAllRtspEntries.query({}).catch(() => null)]);
|
|
25036
|
+
return {
|
|
25037
|
+
profiles: statsAndClients.map(({ slot, stats, clients }) => ({
|
|
25038
|
+
profile: slot.profile,
|
|
25039
|
+
status: slot.status,
|
|
25040
|
+
codec: stats?.codec ?? slot.codec ?? "",
|
|
25041
|
+
width: slot.resolution?.width ?? 0,
|
|
25042
|
+
height: slot.resolution?.height ?? 0,
|
|
25043
|
+
subscribers: clients?.encodedSubscribers ?? 0,
|
|
25044
|
+
inFps: stats?.inputFps ?? 0,
|
|
25045
|
+
outFps: stats?.decodeFps ?? 0
|
|
25046
|
+
})),
|
|
25047
|
+
webrtcSessions: statsAndClients.reduce((total, { clients }) => {
|
|
25048
|
+
if (!clients) return total;
|
|
25049
|
+
return total + clients.encoded.filter((c) => WEBRTC_KINDS.has(c.attribution.kind)).length;
|
|
25050
|
+
}, 0),
|
|
25051
|
+
rtspRestream: rtspEntry?.some((e) => {
|
|
25052
|
+
return e.brokerId.split("/")[0] === String(deviceId) && e.enabled;
|
|
25053
|
+
}) ?? false
|
|
25054
|
+
};
|
|
25055
|
+
}), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25056
|
+
const decoderFetch = decoderNodeId ? Promise.resolve({
|
|
25057
|
+
nodeId: decoderNodeId,
|
|
25058
|
+
formats: [],
|
|
25059
|
+
sessionCount: 0,
|
|
25060
|
+
shm: {
|
|
25061
|
+
framesWritten: 0,
|
|
25062
|
+
getFrameHits: 0,
|
|
25063
|
+
getFrameMisses: 0,
|
|
25064
|
+
budgetMb: 0
|
|
25065
|
+
}
|
|
25066
|
+
}) : Promise.resolve(null);
|
|
25067
|
+
const motionResult = (() => {
|
|
25068
|
+
const config = this.cameraConfigs.get(deviceId);
|
|
25069
|
+
if (!config) return null;
|
|
25070
|
+
return {
|
|
25071
|
+
enabled: config.motionSources.includes("analyzer") || config.motionSources.includes("onboard"),
|
|
25072
|
+
fps: config.motionFps
|
|
25073
|
+
};
|
|
25074
|
+
})();
|
|
25075
|
+
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]) => {
|
|
25076
|
+
const metrics = await api.pipelineRunner.getCameraMetrics.query({
|
|
25077
|
+
deviceId,
|
|
25078
|
+
nodeId: detectionNodeId
|
|
25079
|
+
}).catch(() => null);
|
|
25080
|
+
const phase = (() => {
|
|
25081
|
+
const p = metrics?.phase;
|
|
25082
|
+
if (p === "active") return "active";
|
|
25083
|
+
if (p === "idle") return "idle";
|
|
25084
|
+
return "watching";
|
|
25085
|
+
})();
|
|
25086
|
+
return {
|
|
25087
|
+
nodeId: detectionNodeId,
|
|
25088
|
+
engine: {
|
|
25089
|
+
backend: engine?.backend ?? "",
|
|
25090
|
+
device: engine?.device ?? ""
|
|
25091
|
+
},
|
|
25092
|
+
phase,
|
|
25093
|
+
configuredFps: metrics?.configuredFps ?? 0,
|
|
25094
|
+
actualFps: metrics?.actualFps ?? 0,
|
|
25095
|
+
queueDepth: metrics?.queueDepth ?? 0,
|
|
25096
|
+
avgInferenceMs: metrics?.avgInferenceTimeMs ?? 0,
|
|
25097
|
+
provisioning: {
|
|
25098
|
+
state: provisioning?.state ?? "idle",
|
|
25099
|
+
...provisioning?.error !== void 0 ? { error: provisioning.error } : {}
|
|
25100
|
+
}
|
|
25101
|
+
};
|
|
25102
|
+
}), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25103
|
+
const audioResult = audioNodeId ? {
|
|
25104
|
+
nodeId: audioNodeId,
|
|
25105
|
+
enabled: true
|
|
25106
|
+
} : null;
|
|
25107
|
+
function isRecordingStatus(v) {
|
|
25108
|
+
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";
|
|
25109
|
+
}
|
|
25110
|
+
const recordingFetch = api ? this.boundedStage(api.recording.getStatus.query({ deviceId }).then((rawStatus) => {
|
|
25111
|
+
if (!isRecordingStatus(rawStatus)) return null;
|
|
25112
|
+
return {
|
|
25113
|
+
mode: rawStatus.activeMode,
|
|
25114
|
+
active: rawStatus.enabled && rawStatus.activeMode !== "off",
|
|
25115
|
+
storageBytes: rawStatus.storageBytes
|
|
25116
|
+
};
|
|
25117
|
+
}).catch(() => null), STAGE_TIMEOUT_MS) : Promise.resolve(null);
|
|
25118
|
+
const [sourceResult, brokerResult, decoderResult, detectionResult, recordingResult] = await Promise.all([
|
|
25119
|
+
sourceFetch,
|
|
25120
|
+
brokerFetch,
|
|
25121
|
+
decoderFetch,
|
|
25122
|
+
detectionFetch,
|
|
25123
|
+
recordingFetch
|
|
25124
|
+
]);
|
|
25125
|
+
return composeCameraStatus({
|
|
25126
|
+
deviceId,
|
|
25127
|
+
fetchedAt: Date.now(),
|
|
25128
|
+
detectionNodeId,
|
|
25129
|
+
decoderNodeId,
|
|
25130
|
+
audioNodeId,
|
|
25131
|
+
pinned,
|
|
25132
|
+
reasons,
|
|
25133
|
+
sourceResult,
|
|
25134
|
+
brokerResult,
|
|
25135
|
+
decoderResult,
|
|
25136
|
+
motionResult,
|
|
25137
|
+
detectionResult,
|
|
25138
|
+
audioResult,
|
|
25139
|
+
recordingResult
|
|
25140
|
+
});
|
|
25141
|
+
}
|
|
25142
|
+
/**
|
|
25143
|
+
* Server-composed aggregated status for multiple cameras in one call.
|
|
25144
|
+
*
|
|
25145
|
+
* `deviceIds` defaults to all cameras currently tracked by the
|
|
25146
|
+
* orchestrator's assignment map when omitted.
|
|
25147
|
+
*
|
|
25148
|
+
* v1: `Promise.all` over per-device composition (no concurrency cap).
|
|
25149
|
+
* Note: for large fleets (hundreds of cameras) this may fan out many
|
|
25150
|
+
* parallel calls. A concurrency limiter (p-limit / semaphore) should be
|
|
25151
|
+
* added if latency measurements show it's necessary — deliberately
|
|
25152
|
+
* deferred per the YAGNI constraint in the spec.
|
|
25153
|
+
*/
|
|
25154
|
+
async getCameraStatuses(input) {
|
|
25155
|
+
const ids = input.deviceIds !== void 0 && input.deviceIds.length > 0 ? input.deviceIds : [...this.assignments.keys()];
|
|
25156
|
+
return Promise.all(ids.map((deviceId) => this.getCameraStatus({ deviceId })));
|
|
25157
|
+
}
|
|
24421
25158
|
/** Read the templates map from the addon store via the durable handle. */
|
|
24422
25159
|
async readTemplatesMap() {
|
|
24423
25160
|
const raw = await this.templatesState.get();
|
|
@@ -24448,7 +25185,11 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24448
25185
|
return;
|
|
24449
25186
|
}
|
|
24450
25187
|
const all = await this.readAgentSettingsMap();
|
|
24451
|
-
all[nodeId]
|
|
25188
|
+
const existing = all[nodeId];
|
|
25189
|
+
all[nodeId] = {
|
|
25190
|
+
addonDefaults: settings.addonDefaults,
|
|
25191
|
+
maxCameras: settings.maxCameras !== void 0 ? settings.maxCameras ?? null : existing?.maxCameras ?? null
|
|
25192
|
+
};
|
|
24452
25193
|
await this.agentSettingsState.set(all);
|
|
24453
25194
|
}
|
|
24454
25195
|
/** Read the `cameraSettings` map (keyed on `deviceId` as string) via the durable handle. */
|
|
@@ -24658,12 +25399,15 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24658
25399
|
}
|
|
24659
25400
|
let agent = (await this.readAgentSettingsMap())[nodeId];
|
|
24660
25401
|
if (!agent || Object.keys(agent.addonDefaults ?? {}).length === 0) {
|
|
24661
|
-
|
|
24662
|
-
if (!seeded) {
|
|
25402
|
+
if (!await this.seedAgentSettingsFromCatalog(nodeId)) {
|
|
24663
25403
|
await sleep$1(2e3);
|
|
24664
25404
|
continue;
|
|
24665
25405
|
}
|
|
24666
|
-
agent =
|
|
25406
|
+
agent = (await this.readAgentSettingsMap())[nodeId];
|
|
25407
|
+
}
|
|
25408
|
+
if (!agent) {
|
|
25409
|
+
await sleep$1(2e3);
|
|
25410
|
+
continue;
|
|
24667
25411
|
}
|
|
24668
25412
|
const engine = await this.readDetectionPipelineEngine(nodeId) ?? catalog.selectedEngine;
|
|
24669
25413
|
return {
|
|
@@ -25570,7 +26314,8 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25570
26314
|
};
|
|
25571
26315
|
let dispatchedNodeId = null;
|
|
25572
26316
|
try {
|
|
25573
|
-
|
|
26317
|
+
const result = await this.dispatchCamera(runnerConfig);
|
|
26318
|
+
if (result.kind === "assigned") dispatchedNodeId = result.agentNodeId;
|
|
25574
26319
|
} catch (err) {
|
|
25575
26320
|
const msg = errMsg(err);
|
|
25576
26321
|
log.error("dispatchCamera failed", { meta: { error: msg } });
|