@camstack/addon-pipeline 1.2.29 → 1.2.31
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/audio-analyzer/index.js +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +448 -192
- package/dist/detection-pipeline/index.mjs +447 -191
- package/dist/{dist-DUHlZcWd.js → dist-BvFLZyhW.js} +105 -3
- package/dist/{dist-CsKaXmP8.mjs → dist-Du7_SNiR.mjs} +105 -3
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +127 -6
- package/dist/pipeline-runner/index.mjs +126 -6
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/{step-definitions-1GIwU6R8.mjs → step-definitions-BkY9_IM6.mjs} +1 -1
- package/dist/{step-definitions-Drb9cWbB.js → step-definitions-gC-Ovglc.js} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-tHahN-lK.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BUXeX-vb.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ByHi7ZAe.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CscpvIRR.mjs +26 -0
- package/dist/stream-broker/{hostInit-CIwilJUZ.mjs → hostInit-DJ3h-b1X.mjs} +3 -3
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-B7VdNJlO.js → MaskShapeCanvas-DI4BY7W2-B0DyP46h.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-YyB4uLdH.js → MotionZonesSettings-NcxxQN8r-CzGSy_EO.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DfqixEad.js → PrivacyMaskSettings-APgPLF7p-CEgMcOOW.js} +1 -1
- package/embed-dist/assets/{index-zVLLVdIS.js → index-CHXOWKHF.js} +14 -14
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D1zJmtUe.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CSWUWZ8c.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../types/dist/event-category-
|
|
1
|
+
//#region ../types/dist/event-category-Bz24uP1U.mjs
|
|
2
2
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
3
3
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4
4
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -269,6 +269,19 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
269
269
|
*/
|
|
270
270
|
EventCategory["DeviceStateChanged"] = "device.state-changed";
|
|
271
271
|
/**
|
|
272
|
+
* Frame occupancy for a camera CHANGED — a tracked object was gained or
|
|
273
|
+
* lost. Carries `{ deviceId, totalObjects, byClass, zones }`.
|
|
274
|
+
*
|
|
275
|
+
* Emitted only on a change, so a steady scene is silent. It exists so a
|
|
276
|
+
* client can stop polling `zoneAnalytics.getCurrentSnapshot`: that was the
|
|
277
|
+
* one live badge with no push signal at all, and it cost a request every
|
|
278
|
+
* four seconds per visible camera.
|
|
279
|
+
*
|
|
280
|
+
* Like every event it is telemetry and may be dropped ([D8]) — a consumer
|
|
281
|
+
* keeps a slow reconcile rather than trusting it alone.
|
|
282
|
+
*/
|
|
283
|
+
EventCategory["ZoneAnalyticsOccupancyChanged"] = "zone-analytics.occupancy-changed";
|
|
284
|
+
/**
|
|
272
285
|
* Cap event fired by every device that registers the `battery`
|
|
273
286
|
* capability. Mirrors the cap definition's `onStatusChanged`. Carries
|
|
274
287
|
* `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,
|
|
@@ -9607,6 +9620,29 @@ var NcOccupancyConditionSchema = object({
|
|
|
9607
9620
|
count: number().int().min(0).default(1),
|
|
9608
9621
|
sustainSeconds: number().int().min(0).max(3600).default(15)
|
|
9609
9622
|
});
|
|
9623
|
+
/**
|
|
9624
|
+
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
9625
|
+
*
|
|
9626
|
+
* The values are not symmetric, and deliberately so — the absent value has to
|
|
9627
|
+
* mean exactly what every rule authored before this condition existed already
|
|
9628
|
+
* does:
|
|
9629
|
+
* - `enter` — entries and every NON-crossing record (movement state,
|
|
9630
|
+
* package, sensor). Exits are rejected. **This is the absent behaviour**:
|
|
9631
|
+
* an operator who never asked for exits must not start receiving them.
|
|
9632
|
+
* - `exit` — ONLY an exit crossing. A record that is not a crossing at all
|
|
9633
|
+
* fails closed, because "the car left the drive" is a question about a
|
|
9634
|
+
* boundary, not about a detection.
|
|
9635
|
+
* - `any` — no direction filter; entries, exits and non-crossings alike.
|
|
9636
|
+
*
|
|
9637
|
+
* A rule asking for a direction should normally also scope `zones`, which the
|
|
9638
|
+
* engine evaluates against the crossed zone as well as the current membership
|
|
9639
|
+
* (an exit's membership no longer contains the zone it just left).
|
|
9640
|
+
*/
|
|
9641
|
+
var NcCrossingSchema = _enum([
|
|
9642
|
+
"enter",
|
|
9643
|
+
"exit",
|
|
9644
|
+
"any"
|
|
9645
|
+
]);
|
|
9610
9646
|
/** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
|
|
9611
9647
|
var NcZoneConditionSchema = object({
|
|
9612
9648
|
ids: array(string().min(1)).min(1),
|
|
@@ -9631,6 +9667,13 @@ var NcConditionsSchema = object({
|
|
|
9631
9667
|
/** Veto zones — any hit fails the rule. */
|
|
9632
9668
|
zonesExclude: array(string().min(1)).optional(),
|
|
9633
9669
|
/**
|
|
9670
|
+
* Zone-crossing direction. IMMEDIATE only — a crossing is a per-event fact
|
|
9671
|
+
* and a closed track carries none, so a `track-end` rule asking for one
|
|
9672
|
+
* fails closed (use `zones`, which tests `zonesVisited`). ABSENT = `enter`,
|
|
9673
|
+
* which is exactly today's behaviour. See {@link NcCrossingSchema}.
|
|
9674
|
+
*/
|
|
9675
|
+
crossing: NcCrossingSchema.optional(),
|
|
9676
|
+
/**
|
|
9634
9677
|
* Exact (case-insensitive) match on the record's collapsed `label`
|
|
9635
9678
|
* (identity name / plate text / subclass).
|
|
9636
9679
|
*/
|
|
@@ -9819,11 +9862,31 @@ var NcMediaPolicySchema = object({
|
|
|
9819
9862
|
*/
|
|
9820
9863
|
profile: CamProfileSchema.optional()
|
|
9821
9864
|
});
|
|
9865
|
+
/**
|
|
9866
|
+
* Cooldown GRANULARITY over the subject's class — how much a fired
|
|
9867
|
+
* notification suppresses.
|
|
9868
|
+
* - `shared` (default, and the absent value) — one window for the whole
|
|
9869
|
+
* rule/scope: a cat silences the next dog for `cooldownSec`.
|
|
9870
|
+
* - `per-class` — an independent window per detected class, so cat→dog fires
|
|
9871
|
+
* at once and cat→cat still waits.
|
|
9872
|
+
*
|
|
9873
|
+
* AUDIO subjects are ALWAYS per-class regardless of this setting: a scream
|
|
9874
|
+
* must not be swallowed by a bark's window (the precedent this generalizes —
|
|
9875
|
+
* see `cooldownKey` in the rule engine).
|
|
9876
|
+
*/
|
|
9877
|
+
var NcThrottleGranularitySchema = _enum(["shared", "per-class"]);
|
|
9822
9878
|
/** Throttle — cooldown survives restarts (rebuilt from the outbox on boot). */
|
|
9823
9879
|
var NcThrottleSchema = object({
|
|
9824
9880
|
cooldownSec: number().int().min(0).max(86400).default(60),
|
|
9825
9881
|
/** `rule` = one shared cooldown; `rule-device` = per-camera cooldown. */
|
|
9826
|
-
scope: _enum(["rule", "rule-device"]).default("rule-device")
|
|
9882
|
+
scope: _enum(["rule", "rule-device"]).default("rule-device"),
|
|
9883
|
+
/**
|
|
9884
|
+
* Class granularity of the cooldown key. Optional rather than defaulted:
|
|
9885
|
+
* a Zod default does NOT run on the addon→addon cap path, so a persisted
|
|
9886
|
+
* rule authored before this field simply carries none — and the engine
|
|
9887
|
+
* reads absent as `shared`, the pre-existing behaviour.
|
|
9888
|
+
*/
|
|
9889
|
+
granularity: NcThrottleGranularitySchema.optional()
|
|
9827
9890
|
});
|
|
9828
9891
|
/** Client-supplied rule fields (server stamps id/createdBy/createdAt/updatedAt). */
|
|
9829
9892
|
var NcRuleInputSchema = object({
|
|
@@ -9931,6 +9994,7 @@ var NcConditionDescriptorSchema = object({
|
|
|
9931
9994
|
"schedule",
|
|
9932
9995
|
"plateMatcher",
|
|
9933
9996
|
"packagePhase",
|
|
9997
|
+
"crossingSelect",
|
|
9934
9998
|
"polygonDraw",
|
|
9935
9999
|
"occupancy"
|
|
9936
10000
|
]),
|
|
@@ -17092,7 +17156,10 @@ method(object({
|
|
|
17092
17156
|
}), method(object({
|
|
17093
17157
|
deviceId: number(),
|
|
17094
17158
|
caps: array(string()).readonly().optional()
|
|
17095
|
-
}), record(string(), unknown().nullable()))
|
|
17159
|
+
}), record(string(), unknown().nullable())), method(object({
|
|
17160
|
+
deviceIds: array(number()).readonly(),
|
|
17161
|
+
caps: array(string()).readonly().optional()
|
|
17162
|
+
}), record(string(), record(string(), unknown().nullable())));
|
|
17096
17163
|
method(object({ deviceId: number() }), record(string(), record(string(), unknown()))), method(object({
|
|
17097
17164
|
deviceId: number(),
|
|
17098
17165
|
capName: string()
|
|
@@ -18500,6 +18567,29 @@ var MotionEventSchema = object({
|
|
|
18500
18567
|
* Absent on legacy rows ⇒ treat as `pipeline`.
|
|
18501
18568
|
*/
|
|
18502
18569
|
var DetectionSourceSchema = _enum(["pipeline", "onboard"]);
|
|
18570
|
+
/**
|
|
18571
|
+
* The confirmed zone crossing that produced an object event. Present ONLY on
|
|
18572
|
+
* an event emitted BY a crossing (`zone.enter` / `zone.exit`); a movement-state
|
|
18573
|
+
* event (`object.entering` / `leaving` / `stationary` / `loitering`) and an
|
|
18574
|
+
* appearance event carry none, so a rule asking for a direction fails closed
|
|
18575
|
+
* on them.
|
|
18576
|
+
*
|
|
18577
|
+
* Exactly ONE crossing per event: the emitter turns each confirmed crossing
|
|
18578
|
+
* into its own event, so a frame in which a track enters A while leaving B
|
|
18579
|
+
* produces two events with two directions — never one ambiguous row.
|
|
18580
|
+
*
|
|
18581
|
+
* `zoneId` is load-bearing for an EXIT: the event's `zones` list is the
|
|
18582
|
+
* membership the box has NOW, and by definition it no longer contains the zone
|
|
18583
|
+
* that was just left. Without the id here, a zone-scoped rule could never match
|
|
18584
|
+
* the exit it asked for.
|
|
18585
|
+
*/
|
|
18586
|
+
var ZoneCrossingSchema = object({
|
|
18587
|
+
direction: _enum(["enter", "exit"]),
|
|
18588
|
+
/** Admin zone id crossed. */
|
|
18589
|
+
zoneId: string(),
|
|
18590
|
+
/** Zone display name at crossing time (falls back to the id). */
|
|
18591
|
+
zoneName: string().optional()
|
|
18592
|
+
});
|
|
18503
18593
|
var ObjectEventSchema = object({
|
|
18504
18594
|
...BaseEventFields,
|
|
18505
18595
|
kind: literal("object"),
|
|
@@ -18526,6 +18616,12 @@ var ObjectEventSchema = object({
|
|
|
18526
18616
|
zones: array(string()).readonly().optional(),
|
|
18527
18617
|
/** Omitted in slim projection. */
|
|
18528
18618
|
state: TrackStateSchema.optional(),
|
|
18619
|
+
/**
|
|
18620
|
+
* The zone crossing this event IS, when it is one. Absent on every other
|
|
18621
|
+
* event kind (movement state, appearance, package) — see
|
|
18622
|
+
* {@link ZoneCrossingSchema}. Omitted in slim projection.
|
|
18623
|
+
*/
|
|
18624
|
+
zoneCrossing: ZoneCrossingSchema.optional(),
|
|
18529
18625
|
/** Detection-frame dimensions in pixels — let consumers normalize the
|
|
18530
18626
|
* pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
|
|
18531
18627
|
frameWidth: number().optional(),
|
|
@@ -24041,6 +24137,12 @@ Object.freeze({
|
|
|
24041
24137
|
addonId: null,
|
|
24042
24138
|
access: "view"
|
|
24043
24139
|
},
|
|
24140
|
+
"deviceManager.getDeviceStatusAggregateBatch": {
|
|
24141
|
+
capName: "device-manager",
|
|
24142
|
+
capScope: "system",
|
|
24143
|
+
addonId: null,
|
|
24144
|
+
access: "view"
|
|
24145
|
+
},
|
|
24044
24146
|
"deviceManager.getLinkedDevices": {
|
|
24045
24147
|
capName: "device-manager",
|
|
24046
24148
|
capScope: "system",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../types/dist/event-category-
|
|
1
|
+
//#region ../types/dist/event-category-Bz24uP1U.mjs
|
|
2
2
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
3
3
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4
4
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -269,6 +269,19 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
269
269
|
*/
|
|
270
270
|
EventCategory["DeviceStateChanged"] = "device.state-changed";
|
|
271
271
|
/**
|
|
272
|
+
* Frame occupancy for a camera CHANGED — a tracked object was gained or
|
|
273
|
+
* lost. Carries `{ deviceId, totalObjects, byClass, zones }`.
|
|
274
|
+
*
|
|
275
|
+
* Emitted only on a change, so a steady scene is silent. It exists so a
|
|
276
|
+
* client can stop polling `zoneAnalytics.getCurrentSnapshot`: that was the
|
|
277
|
+
* one live badge with no push signal at all, and it cost a request every
|
|
278
|
+
* four seconds per visible camera.
|
|
279
|
+
*
|
|
280
|
+
* Like every event it is telemetry and may be dropped ([D8]) — a consumer
|
|
281
|
+
* keeps a slow reconcile rather than trusting it alone.
|
|
282
|
+
*/
|
|
283
|
+
EventCategory["ZoneAnalyticsOccupancyChanged"] = "zone-analytics.occupancy-changed";
|
|
284
|
+
/**
|
|
272
285
|
* Cap event fired by every device that registers the `battery`
|
|
273
286
|
* capability. Mirrors the cap definition's `onStatusChanged`. Carries
|
|
274
287
|
* `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,
|
|
@@ -9607,6 +9620,29 @@ var NcOccupancyConditionSchema = object({
|
|
|
9607
9620
|
count: number().int().min(0).default(1),
|
|
9608
9621
|
sustainSeconds: number().int().min(0).max(3600).default(15)
|
|
9609
9622
|
});
|
|
9623
|
+
/**
|
|
9624
|
+
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
9625
|
+
*
|
|
9626
|
+
* The values are not symmetric, and deliberately so — the absent value has to
|
|
9627
|
+
* mean exactly what every rule authored before this condition existed already
|
|
9628
|
+
* does:
|
|
9629
|
+
* - `enter` — entries and every NON-crossing record (movement state,
|
|
9630
|
+
* package, sensor). Exits are rejected. **This is the absent behaviour**:
|
|
9631
|
+
* an operator who never asked for exits must not start receiving them.
|
|
9632
|
+
* - `exit` — ONLY an exit crossing. A record that is not a crossing at all
|
|
9633
|
+
* fails closed, because "the car left the drive" is a question about a
|
|
9634
|
+
* boundary, not about a detection.
|
|
9635
|
+
* - `any` — no direction filter; entries, exits and non-crossings alike.
|
|
9636
|
+
*
|
|
9637
|
+
* A rule asking for a direction should normally also scope `zones`, which the
|
|
9638
|
+
* engine evaluates against the crossed zone as well as the current membership
|
|
9639
|
+
* (an exit's membership no longer contains the zone it just left).
|
|
9640
|
+
*/
|
|
9641
|
+
var NcCrossingSchema = _enum([
|
|
9642
|
+
"enter",
|
|
9643
|
+
"exit",
|
|
9644
|
+
"any"
|
|
9645
|
+
]);
|
|
9610
9646
|
/** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
|
|
9611
9647
|
var NcZoneConditionSchema = object({
|
|
9612
9648
|
ids: array(string().min(1)).min(1),
|
|
@@ -9631,6 +9667,13 @@ var NcConditionsSchema = object({
|
|
|
9631
9667
|
/** Veto zones — any hit fails the rule. */
|
|
9632
9668
|
zonesExclude: array(string().min(1)).optional(),
|
|
9633
9669
|
/**
|
|
9670
|
+
* Zone-crossing direction. IMMEDIATE only — a crossing is a per-event fact
|
|
9671
|
+
* and a closed track carries none, so a `track-end` rule asking for one
|
|
9672
|
+
* fails closed (use `zones`, which tests `zonesVisited`). ABSENT = `enter`,
|
|
9673
|
+
* which is exactly today's behaviour. See {@link NcCrossingSchema}.
|
|
9674
|
+
*/
|
|
9675
|
+
crossing: NcCrossingSchema.optional(),
|
|
9676
|
+
/**
|
|
9634
9677
|
* Exact (case-insensitive) match on the record's collapsed `label`
|
|
9635
9678
|
* (identity name / plate text / subclass).
|
|
9636
9679
|
*/
|
|
@@ -9819,11 +9862,31 @@ var NcMediaPolicySchema = object({
|
|
|
9819
9862
|
*/
|
|
9820
9863
|
profile: CamProfileSchema.optional()
|
|
9821
9864
|
});
|
|
9865
|
+
/**
|
|
9866
|
+
* Cooldown GRANULARITY over the subject's class — how much a fired
|
|
9867
|
+
* notification suppresses.
|
|
9868
|
+
* - `shared` (default, and the absent value) — one window for the whole
|
|
9869
|
+
* rule/scope: a cat silences the next dog for `cooldownSec`.
|
|
9870
|
+
* - `per-class` — an independent window per detected class, so cat→dog fires
|
|
9871
|
+
* at once and cat→cat still waits.
|
|
9872
|
+
*
|
|
9873
|
+
* AUDIO subjects are ALWAYS per-class regardless of this setting: a scream
|
|
9874
|
+
* must not be swallowed by a bark's window (the precedent this generalizes —
|
|
9875
|
+
* see `cooldownKey` in the rule engine).
|
|
9876
|
+
*/
|
|
9877
|
+
var NcThrottleGranularitySchema = _enum(["shared", "per-class"]);
|
|
9822
9878
|
/** Throttle — cooldown survives restarts (rebuilt from the outbox on boot). */
|
|
9823
9879
|
var NcThrottleSchema = object({
|
|
9824
9880
|
cooldownSec: number().int().min(0).max(86400).default(60),
|
|
9825
9881
|
/** `rule` = one shared cooldown; `rule-device` = per-camera cooldown. */
|
|
9826
|
-
scope: _enum(["rule", "rule-device"]).default("rule-device")
|
|
9882
|
+
scope: _enum(["rule", "rule-device"]).default("rule-device"),
|
|
9883
|
+
/**
|
|
9884
|
+
* Class granularity of the cooldown key. Optional rather than defaulted:
|
|
9885
|
+
* a Zod default does NOT run on the addon→addon cap path, so a persisted
|
|
9886
|
+
* rule authored before this field simply carries none — and the engine
|
|
9887
|
+
* reads absent as `shared`, the pre-existing behaviour.
|
|
9888
|
+
*/
|
|
9889
|
+
granularity: NcThrottleGranularitySchema.optional()
|
|
9827
9890
|
});
|
|
9828
9891
|
/** Client-supplied rule fields (server stamps id/createdBy/createdAt/updatedAt). */
|
|
9829
9892
|
var NcRuleInputSchema = object({
|
|
@@ -9931,6 +9994,7 @@ var NcConditionDescriptorSchema = object({
|
|
|
9931
9994
|
"schedule",
|
|
9932
9995
|
"plateMatcher",
|
|
9933
9996
|
"packagePhase",
|
|
9997
|
+
"crossingSelect",
|
|
9934
9998
|
"polygonDraw",
|
|
9935
9999
|
"occupancy"
|
|
9936
10000
|
]),
|
|
@@ -17092,7 +17156,10 @@ method(object({
|
|
|
17092
17156
|
}), method(object({
|
|
17093
17157
|
deviceId: number(),
|
|
17094
17158
|
caps: array(string()).readonly().optional()
|
|
17095
|
-
}), record(string(), unknown().nullable()))
|
|
17159
|
+
}), record(string(), unknown().nullable())), method(object({
|
|
17160
|
+
deviceIds: array(number()).readonly(),
|
|
17161
|
+
caps: array(string()).readonly().optional()
|
|
17162
|
+
}), record(string(), record(string(), unknown().nullable())));
|
|
17096
17163
|
method(object({ deviceId: number() }), record(string(), record(string(), unknown()))), method(object({
|
|
17097
17164
|
deviceId: number(),
|
|
17098
17165
|
capName: string()
|
|
@@ -18500,6 +18567,29 @@ var MotionEventSchema = object({
|
|
|
18500
18567
|
* Absent on legacy rows ⇒ treat as `pipeline`.
|
|
18501
18568
|
*/
|
|
18502
18569
|
var DetectionSourceSchema = _enum(["pipeline", "onboard"]);
|
|
18570
|
+
/**
|
|
18571
|
+
* The confirmed zone crossing that produced an object event. Present ONLY on
|
|
18572
|
+
* an event emitted BY a crossing (`zone.enter` / `zone.exit`); a movement-state
|
|
18573
|
+
* event (`object.entering` / `leaving` / `stationary` / `loitering`) and an
|
|
18574
|
+
* appearance event carry none, so a rule asking for a direction fails closed
|
|
18575
|
+
* on them.
|
|
18576
|
+
*
|
|
18577
|
+
* Exactly ONE crossing per event: the emitter turns each confirmed crossing
|
|
18578
|
+
* into its own event, so a frame in which a track enters A while leaving B
|
|
18579
|
+
* produces two events with two directions — never one ambiguous row.
|
|
18580
|
+
*
|
|
18581
|
+
* `zoneId` is load-bearing for an EXIT: the event's `zones` list is the
|
|
18582
|
+
* membership the box has NOW, and by definition it no longer contains the zone
|
|
18583
|
+
* that was just left. Without the id here, a zone-scoped rule could never match
|
|
18584
|
+
* the exit it asked for.
|
|
18585
|
+
*/
|
|
18586
|
+
var ZoneCrossingSchema = object({
|
|
18587
|
+
direction: _enum(["enter", "exit"]),
|
|
18588
|
+
/** Admin zone id crossed. */
|
|
18589
|
+
zoneId: string(),
|
|
18590
|
+
/** Zone display name at crossing time (falls back to the id). */
|
|
18591
|
+
zoneName: string().optional()
|
|
18592
|
+
});
|
|
18503
18593
|
var ObjectEventSchema = object({
|
|
18504
18594
|
...BaseEventFields,
|
|
18505
18595
|
kind: literal("object"),
|
|
@@ -18526,6 +18616,12 @@ var ObjectEventSchema = object({
|
|
|
18526
18616
|
zones: array(string()).readonly().optional(),
|
|
18527
18617
|
/** Omitted in slim projection. */
|
|
18528
18618
|
state: TrackStateSchema.optional(),
|
|
18619
|
+
/**
|
|
18620
|
+
* The zone crossing this event IS, when it is one. Absent on every other
|
|
18621
|
+
* event kind (movement state, appearance, package) — see
|
|
18622
|
+
* {@link ZoneCrossingSchema}. Omitted in slim projection.
|
|
18623
|
+
*/
|
|
18624
|
+
zoneCrossing: ZoneCrossingSchema.optional(),
|
|
18529
18625
|
/** Detection-frame dimensions in pixels — let consumers normalize the
|
|
18530
18626
|
* pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
|
|
18531
18627
|
frameWidth: number().optional(),
|
|
@@ -24041,6 +24137,12 @@ Object.freeze({
|
|
|
24041
24137
|
addonId: null,
|
|
24042
24138
|
access: "view"
|
|
24043
24139
|
},
|
|
24140
|
+
"deviceManager.getDeviceStatusAggregateBatch": {
|
|
24141
|
+
capName: "device-manager",
|
|
24142
|
+
capScope: "system",
|
|
24143
|
+
addonId: null,
|
|
24144
|
+
access: "view"
|
|
24145
|
+
},
|
|
24044
24146
|
"deviceManager.getLinkedDevices": {
|
|
24045
24147
|
capName: "device-manager",
|
|
24046
24148
|
capScope: "system",
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-BvFLZyhW.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as motionDetectionCapability, E as evaluateZoneRules, K as hydrateSchema, V as BaseAddon, W as DeviceType } from "../dist-
|
|
1
|
+
import { A as motionDetectionCapability, E as evaluateZoneRules, K as hydrateSchema, V as BaseAddon, W as DeviceType } from "../dist-Du7_SNiR.mjs";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -3,11 +3,13 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-BvFLZyhW.js");
|
|
7
7
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
8
8
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
9
9
|
const require_worker_protocol = require("../worker-protocol-DextwlTX.js");
|
|
10
|
-
const require_step_definitions = require("../step-definitions-
|
|
10
|
+
const require_step_definitions = require("../step-definitions-gC-Ovglc.js");
|
|
11
|
+
let node_os = require("node:os");
|
|
12
|
+
node_os = require_chunk.__toESM(node_os);
|
|
11
13
|
let node_child_process = require("node:child_process");
|
|
12
14
|
let node_url = require("node:url");
|
|
13
15
|
let sharp = require("sharp");
|
|
@@ -383,6 +385,51 @@ var Semaphore = class {
|
|
|
383
385
|
if (next) next();
|
|
384
386
|
}
|
|
385
387
|
};
|
|
388
|
+
var MotionWithoutInferenceDetector = class {
|
|
389
|
+
streak = /* @__PURE__ */ new Map();
|
|
390
|
+
reported = /* @__PURE__ */ new Set();
|
|
391
|
+
minIntervals;
|
|
392
|
+
constructor(minIntervals = 3) {
|
|
393
|
+
this.minIntervals = Math.max(1, minIntervals);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Feed one interval for one camera. Returns what, if anything, to say.
|
|
397
|
+
*
|
|
398
|
+
* A camera with NO motion is not evidence either way — it is simply quiet,
|
|
399
|
+
* and its streak is left untouched rather than reset, so an outage that
|
|
400
|
+
* spans a lull is still reported as one continuous stall.
|
|
401
|
+
*/
|
|
402
|
+
observe(deviceId, summary) {
|
|
403
|
+
if (summary.inferenceFrames > 0) {
|
|
404
|
+
const wasReported = this.reported.delete(deviceId);
|
|
405
|
+
const intervals = this.streak.get(deviceId) ?? 0;
|
|
406
|
+
this.streak.delete(deviceId);
|
|
407
|
+
return wasReported ? {
|
|
408
|
+
kind: "recovered",
|
|
409
|
+
intervals
|
|
410
|
+
} : { kind: "quiet" };
|
|
411
|
+
}
|
|
412
|
+
if (summary.motionFrames <= 0) return { kind: "quiet" };
|
|
413
|
+
const next = (this.streak.get(deviceId) ?? 0) + 1;
|
|
414
|
+
this.streak.set(deviceId, next);
|
|
415
|
+
if (next < this.minIntervals) return { kind: "quiet" };
|
|
416
|
+
if (this.reported.has(deviceId)) return { kind: "quiet" };
|
|
417
|
+
this.reported.add(deviceId);
|
|
418
|
+
return {
|
|
419
|
+
kind: "stalled",
|
|
420
|
+
intervals: next
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/** Drop a camera's state — it was detached, so its streak means nothing. */
|
|
424
|
+
forget(deviceId) {
|
|
425
|
+
this.streak.delete(deviceId);
|
|
426
|
+
this.reported.delete(deviceId);
|
|
427
|
+
}
|
|
428
|
+
/** Cameras currently in a reported stall — the board's "motion, no inference". */
|
|
429
|
+
stalledDevices() {
|
|
430
|
+
return [...this.reported];
|
|
431
|
+
}
|
|
432
|
+
};
|
|
386
433
|
//#endregion
|
|
387
434
|
//#region src/pipeline-runner/timing-sampler.ts
|
|
388
435
|
var REPORT_INTERVAL_MS = 1e4;
|
|
@@ -395,6 +442,8 @@ var REPORT_INTERVAL_MS = 1e4;
|
|
|
395
442
|
var PipelineTimingSampler = class {
|
|
396
443
|
detSamples = /* @__PURE__ */ new Map();
|
|
397
444
|
motSamples = /* @__PURE__ */ new Map();
|
|
445
|
+
/** See {@link MotionWithoutInferenceDetector} — the two-incident signature. */
|
|
446
|
+
motionStall = new MotionWithoutInferenceDetector();
|
|
398
447
|
audioSamples = /* @__PURE__ */ new Map();
|
|
399
448
|
/** Per-device cumulative dropped-frames readers (see {@link DroppedFramesSource}). */
|
|
400
449
|
droppedSources = /* @__PURE__ */ new Map();
|
|
@@ -526,6 +575,28 @@ var PipelineTimingSampler = class {
|
|
|
526
575
|
}
|
|
527
576
|
});
|
|
528
577
|
}
|
|
578
|
+
const devicesSeen = new Set([...this.detSamples.keys(), ...this.motSamples.keys()]);
|
|
579
|
+
for (const deviceId of devicesSeen) {
|
|
580
|
+
const verdict = this.motionStall.observe(deviceId, {
|
|
581
|
+
motionFrames: this.motSamples.get(deviceId)?.length ?? 0,
|
|
582
|
+
inferenceFrames: this.detSamples.get(deviceId)?.length ?? 0
|
|
583
|
+
});
|
|
584
|
+
if (verdict.kind === "stalled") this.log.warn("motion is flowing but NOTHING is being inferred", {
|
|
585
|
+
tags: { deviceId },
|
|
586
|
+
meta: {
|
|
587
|
+
intervals: verdict.intervals,
|
|
588
|
+
forSec: verdict.intervals * REPORT_INTERVAL_MS / 1e3,
|
|
589
|
+
motionFrames: this.motSamples.get(deviceId)?.length ?? 0
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
else if (verdict.kind === "recovered") this.log.info("inference resumed after a motion-without-inference stall", {
|
|
593
|
+
tags: { deviceId },
|
|
594
|
+
meta: {
|
|
595
|
+
intervals: verdict.intervals,
|
|
596
|
+
lastedSec: verdict.intervals * REPORT_INTERVAL_MS / 1e3
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
529
600
|
this.detSamples.clear();
|
|
530
601
|
for (const [deviceId, mot] of this.motSamples) {
|
|
531
602
|
if (mot.length === 0) continue;
|
|
@@ -1271,6 +1342,40 @@ function summarizeDeviceLoad(attachedDeviceKeys) {
|
|
|
1271
1342
|
})).toSorted((a, b) => a.deviceKey.localeCompare(b.deviceKey));
|
|
1272
1343
|
}
|
|
1273
1344
|
//#endregion
|
|
1345
|
+
//#region src/pipeline-runner/hardware-capabilities.ts
|
|
1346
|
+
/**
|
|
1347
|
+
* The `hardware` block of `RunnerLocalLoad` — what this node can accelerate on.
|
|
1348
|
+
*
|
|
1349
|
+
* ## Why this file exists
|
|
1350
|
+
*
|
|
1351
|
+
* `getLocalLoad()` reported a hardcoded `hasGpu: false`. It was not a probe that
|
|
1352
|
+
* failed: when the hardware probe was moved off the runner (Phase 4) the field
|
|
1353
|
+
* was left as a literal, and it has answered "no GPU" ever since — including on
|
|
1354
|
+
* nodes actively running OpenVINO on their Intel iGPU. The admin UI renders a
|
|
1355
|
+
* "GPU" badge straight off it, and the orchestrator's hardware-aware balancing
|
|
1356
|
+
* reads it, so a stale `false` is a wrong answer, not a missing one.
|
|
1357
|
+
*
|
|
1358
|
+
* The probe is cheap and local (`ctx.kernel.inferenceEngine.resolveHardware()`
|
|
1359
|
+
* is deterministic per host, the same resolver the detection addon uses to pick
|
|
1360
|
+
* the node's default engine), and the runner shares a process with the
|
|
1361
|
+
* detection pipeline, so there is no cross-node ambiguity: this is THIS node.
|
|
1362
|
+
*/
|
|
1363
|
+
/**
|
|
1364
|
+
* Summarise a local hardware probe into the runner's reported capabilities.
|
|
1365
|
+
*
|
|
1366
|
+
* `hasGpu` is literal — a GPU, not "an accelerator". An NPU or a Coral is not a
|
|
1367
|
+
* GPU and does not set it; they surface per pool through `RunnerLocalLoad.devices`
|
|
1368
|
+
* (`openvino:npu`, `edgetpu:usb`, …), which is the finer-grained answer the
|
|
1369
|
+
* device balancer weights on. A null probe means "could not tell", which reports
|
|
1370
|
+
* as `false` — the same conservative floor the engine cascade takes.
|
|
1371
|
+
*/
|
|
1372
|
+
function summarizeRunnerHardware(hardware, cpuCores = node_os.cpus().length) {
|
|
1373
|
+
return {
|
|
1374
|
+
hasGpu: hardware?.gpu != null,
|
|
1375
|
+
cpuCores
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
//#endregion
|
|
1274
1379
|
//#region src/pipeline-runner/native-crop-tier.ts
|
|
1275
1380
|
/**
|
|
1276
1381
|
* Pure tier decision for `getNativeCrop`. A native HIT always resolves to the
|
|
@@ -3010,6 +3115,8 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3010
3115
|
* {@link MOTION_FRESHNESS_WINDOW_MS}.
|
|
3011
3116
|
*/
|
|
3012
3117
|
lastMotionFrameAt = /* @__PURE__ */ new Map();
|
|
3118
|
+
/** Cached local accelerator probe — see {@link resolveLocalHardware}. */
|
|
3119
|
+
localHardware = null;
|
|
3013
3120
|
constructor() {
|
|
3014
3121
|
super({ ...DEFAULT_CONFIG });
|
|
3015
3122
|
}
|
|
@@ -3863,12 +3970,26 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3863
3970
|
avgInferenceTimeMs: metrics.avgInferenceTimeMs,
|
|
3864
3971
|
queueDepthTotal: metrics.queueDepth,
|
|
3865
3972
|
devices,
|
|
3866
|
-
hardware:
|
|
3867
|
-
hasGpu: false,
|
|
3868
|
-
inferenceBackend: void 0
|
|
3869
|
-
}
|
|
3973
|
+
hardware: summarizeRunnerHardware(await this.resolveLocalHardware())
|
|
3870
3974
|
};
|
|
3871
3975
|
}
|
|
3976
|
+
/**
|
|
3977
|
+
* THIS node's accelerators, probed once from the local kernel resolver and
|
|
3978
|
+
* cached (the probe is deterministic per host, and `getLocalLoad` is polled).
|
|
3979
|
+
* A throwing/absent resolver caches nothing and reports "could not tell" so a
|
|
3980
|
+
* later poll retries.
|
|
3981
|
+
*/
|
|
3982
|
+
async resolveLocalHardware() {
|
|
3983
|
+
if (this.localHardware) return this.localHardware;
|
|
3984
|
+
try {
|
|
3985
|
+
const hw = await this.ctx.kernel?.inferenceEngine?.resolveHardware();
|
|
3986
|
+
this.localHardware = hw ?? null;
|
|
3987
|
+
return this.localHardware;
|
|
3988
|
+
} catch (err) {
|
|
3989
|
+
this.ctx.logger.warn("local hardware probe failed — reporting no accelerator", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
3990
|
+
return null;
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3872
3993
|
async getLocalMetrics() {
|
|
3873
3994
|
const m = this.runner?.getMetrics() ?? {
|
|
3874
3995
|
activeCameras: 0,
|