@camstack/addon-mqtt-broker 1.2.89 → 1.2.92

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.
@@ -44,7 +44,7 @@ let crypto$10 = __toESM(crypto$1, 1);
44
44
  crypto$1 = __toESM(crypto$1);
45
45
  let node_fs = require("node:fs");
46
46
  let node_path = require("node:path");
47
- //#region ../types/dist/event-category-CnLqLOKs.mjs
47
+ //#region ../types/dist/event-category-DAXzJeTX.mjs
48
48
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
49
49
  EventCategory["SystemBoot"] = "system.boot";
50
50
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -636,7 +636,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
636
636
  * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
637
637
  */
638
638
  EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
639
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
640
639
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
641
640
  /**
642
641
  * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
@@ -12601,7 +12600,7 @@ DeviceType.Camera;
12601
12600
  var SourceInfoSchema = object({
12602
12601
  /** Live dispatch key — mutable when the source system allows rename. */
12603
12602
  id: string(),
12604
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'frigate'. */
12603
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
12605
12604
  system: string(),
12606
12605
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
12607
12606
  * Used to detect rename when `id` changes. */
@@ -13088,7 +13087,7 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13088
13087
  * - `device-management.router.ts` (deleted in Phase 2)
13089
13088
  * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13090
13089
  *
13091
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13090
+ * All device provider addons (rtsp, onvif, …) are hub-local: they may
13092
13091
  * fork into separate processes but never run on remote cluster agents. Therefore:
13093
13092
  * - No nodeId routing needed — this is a pure hub singleton.
13094
13093
  * - The hub's DeviceRegistry is the single source of truth for all live devices.
@@ -17117,7 +17116,7 @@ var NcRuleInputSchema = object({
17117
17116
  * The measured seven-person arrival on device 590 spans 110 s with every
17118
17117
  * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
17119
17118
  * idle cutoff holds it as one and ends it when the arrival actually ends.
17120
- * 30 is Frigate's shipped value for the same decision.
17119
+ * 30 s is a widely shipped value for the same decision.
17121
17120
  *
17122
17121
  * ### What it replaces
17123
17122
  *
@@ -19159,6 +19158,13 @@ var ZoneCrossingSchema = object({
19159
19158
  /** Zone display name at crossing time (falls back to the id). */
19160
19159
  zoneName: string().optional()
19161
19160
  });
19161
+ /** One zone a box was stamped with, and how much of the BOX fell inside it. */
19162
+ var ZoneOverlapSchema = object({
19163
+ zoneId: string(),
19164
+ zoneName: string().optional(),
19165
+ /** Percentage of the BOX area inside the zone, 0–100. */
19166
+ overlapPct: number()
19167
+ });
19162
19168
  var ObjectEventSchema = object({
19163
19169
  ...BaseEventFields,
19164
19170
  kind: literal("object"),
@@ -19183,6 +19189,35 @@ var ObjectEventSchema = object({
19183
19189
  bbox: BoundingBoxSchema.optional(),
19184
19190
  /** Heavy JSON — omitted in slim projection. */
19185
19191
  zones: array(string()).readonly().optional(),
19192
+ /**
19193
+ * How far inside each stamped zone the box actually was, as a percentage of
19194
+ * the BOX area.
19195
+ *
19196
+ * Membership alone cannot express "at least a third of it inside", which is
19197
+ * the bar an operator argues with — and the number already existed: the
19198
+ * engine computed it, the pipeline logged it, and then discarded it. It
19199
+ * matters most on the events that carry no track, where a rule has nothing
19200
+ * else to judge by.
19201
+ */
19202
+ zoneOverlaps: array(ZoneOverlapSchema).readonly().optional(),
19203
+ /**
19204
+ * Present when this event describes a detection the TRACKER refused, so it
19205
+ * has no track and never will.
19206
+ *
19207
+ * A zone-scoped immediate rule asks "is there an animal in the flowerbed
19208
+ * NOW?"; tracking answers a different question ("is this the same subject as
19209
+ * before?") whose confirmation threshold presumes continuity. On 2026-09-09 a
19210
+ * dog at 0.7729, 100% inside a watched zone, was refused three times in six
19211
+ * seconds with three different candidate ids — seven analysed frames in the
19212
+ * window, the subject in a handful of them, metres apart — and produced no
19213
+ * event at all.
19214
+ *
19215
+ * It is NAMED rather than merely implied by a missing `trackId`: an event
19216
+ * with no track and no reason is indistinguishable from a track whose media
19217
+ * went missing, and a consumer that asks this one for a best shot or a
19218
+ * history is asking for something that never existed.
19219
+ */
19220
+ spawnRefused: string().optional(),
19186
19221
  /** Omitted in slim projection. */
19187
19222
  state: TrackStateSchema.optional(),
19188
19223
  /**
@@ -20256,6 +20291,60 @@ var NativeCropResultSchema = object({
20256
20291
  */
20257
20292
  tier: _enum(["native", "ram-fullframe"]).optional()
20258
20293
  });
20294
+ /**
20295
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
20296
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
20297
+ *
20298
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
20299
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
20300
+ * from, and what the detail plane's full-frame rung cuts from.
20301
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
20302
+ * window, cut by the runner at uncapped native resolution.
20303
+ *
20304
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
20305
+ */
20306
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
20307
+ /**
20308
+ * Why a park did NOT happen. Every one is logged on the runner with
20309
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
20310
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
20311
+ * never a reason to retry in a loop.
20312
+ *
20313
+ * - `handle-gone` — the native surface no longer resolves this handle (never
20314
+ * registered, past the lease bound, or the worker released it). `detail`
20315
+ * carries the runner's own miss reason.
20316
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
20317
+ */
20318
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
20319
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
20320
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
20321
+ parked: literal(true),
20322
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
20323
+ nodeId: string(),
20324
+ width: number().int().positive(),
20325
+ height: number().int().positive(),
20326
+ /** JPEG bytes the parcel costs on the node. */
20327
+ bytes: number().int().nonnegative(),
20328
+ /** Whether this park replaced a parcel already held for the key. */
20329
+ replaced: boolean()
20330
+ }), object({
20331
+ parked: literal(false),
20332
+ nodeId: string(),
20333
+ reason: ParkRefusalSchema,
20334
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
20335
+ handleAgeMs: number().nullable(),
20336
+ detail: string().optional()
20337
+ })]);
20338
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
20339
+ var ParkedTrackFrameSchema = object({
20340
+ jpeg: string(),
20341
+ width: number().int().positive(),
20342
+ height: number().int().positive(),
20343
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
20344
+ timestamp: number(),
20345
+ /** When the node stored it (the node's clock). */
20346
+ parkedAt: number()
20347
+ });
20259
20348
  /** Parent detection context passed to `runDetailSubtree` — the crop's
20260
20349
  * originating detection, in FRAME-space coordinates. Reuses
20261
20350
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -20645,7 +20734,7 @@ var RunnerCameraConfigSchema = object({
20645
20734
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
20646
20735
  * analyzer providers) running on decoded frames in this runner.
20647
20736
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
20648
- * analytics, Frigate motion). The provider emits ProviderMotion
20737
+ * analytics). The provider emits ProviderMotion
20649
20738
  * events; the orchestrator forwards to `reportMotion`.
20650
20739
  */
20651
20740
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -20887,7 +20976,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
20887
20976
  className: string(),
20888
20977
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
20889
20978
  sourceDeviceId: number()
20890
- }), RunStatelessStepResultSchema, { kind: "mutation" });
20979
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
20980
+ deviceId: number(),
20981
+ /** Opaque to the runner — the hub's track id. */
20982
+ trackId: string(),
20983
+ kind: ParkedFrameKindSchema,
20984
+ handle: FrameHandleSchema,
20985
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
20986
+ bbox: NativeCropBboxSchema,
20987
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
20988
+ maxWidth: number().int().positive().optional(),
20989
+ /** The frame instant (caller's clock) — orders parks for one key. */
20990
+ timestamp: number()
20991
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
20992
+ deviceId: number(),
20993
+ trackId: string(),
20994
+ kind: ParkedFrameKindSchema
20995
+ }), ParkedTrackFrameSchema.nullable()), method(object({
20996
+ deviceId: number(),
20997
+ trackId: string()
20998
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
20891
20999
  var CameraPipelineConfigSchema = object({
20892
21000
  engine: PipelineEngineChoiceSchema.optional(),
20893
21001
  steps: array(PipelineStepInputSchema).readonly(),
@@ -23816,7 +23924,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
23816
23924
  *
23817
23925
  * Implementations:
23818
23926
  * - Each camera-providing addon registers this cap natively per
23819
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
23927
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
23820
23928
  * - Returns a snapshot from the persisted device config blob — no
23821
23929
  * extra round-trip to the camera, no probing.
23822
23930
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -25202,7 +25310,7 @@ var AvailableIntegrationTypeSchema = object({
25202
25310
  * Which integration-marker cap the addon declared, so the wizard can
25203
25311
  * branch on CAP — never on addon name. `device-adoption` integrations
25204
25312
  * (Home Assistant, …) route through the broker step (Approach A);
25205
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
25313
+ * `device-provider` integrations (Reolink/ONVIF) keep the
25206
25314
  * legacy config → discovery flow.
25207
25315
  */
25208
25316
  kind: _enum(["device-adoption", "device-provider"]),
@@ -27759,13 +27867,31 @@ var RecordingManifestSchema = object({
27759
27867
  */
27760
27868
  playbackEndpoints: array(string())
27761
27869
  });
27762
- /** Recording storage usage for one camera (bytes on disk across all its
27763
- * profiles/subtrees/locations on this node). */
27870
+ /**
27871
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
27872
+ * across every profile and every resolvable location on this node.
27873
+ *
27874
+ * ## Archive vs residency, which is the whole point of this shape (D418)
27875
+ *
27876
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
27877
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
27878
+ * this camera writes to, so the two can be added up and compared.
27879
+ *
27880
+ * They were not always. Until D418 this row came from the recorder's in-RAM
27881
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
27882
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
27883
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
27884
+ * that said otherwise, and two people read it as retention eating recent
27885
+ * footage. Residency is still reported, under `residentBytes` /
27886
+ * `residentOldestMs`, which answer a different question and say so.
27887
+ */
27764
27888
  var RecordingDeviceUsageSchema = object({
27765
27889
  deviceId: number(),
27890
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
27891
+ * recordings locations. */
27766
27892
  usedBytes: number(),
27767
27893
  /**
27768
- * Start of this camera's OLDEST indexed segment, across every profile and
27894
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
27769
27895
  * location — the "Oldest footage" column in Recordings → Storage, and the
27770
27896
  * only honest answer to "is retention actually holding?" per camera.
27771
27897
  *
@@ -27775,7 +27901,24 @@ var RecordingDeviceUsageSchema = object({
27775
27901
  * (types) and the addon ship on different trains, and the addon is usually
27776
27902
  * the later of the two.
27777
27903
  */
27778
- oldestMs: number().nullable().optional()
27904
+ oldestMs: number().nullable().optional(),
27905
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
27906
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
27907
+ segments: number().optional(),
27908
+ /**
27909
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
27910
+ * its RAM index — the live tail plus the hours a reader hydrated.
27911
+ *
27912
+ * It answers "what can be served without touching the disk", never "how much
27913
+ * footage this camera has". A UI that renders it must label it as such, and
27914
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
27915
+ * it, and so does a node whose ledger could not answer.
27916
+ */
27917
+ residentBytes: number().optional(),
27918
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
27919
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
27920
+ * retention. `null` = nothing resident. */
27921
+ residentOldestMs: number().nullable().optional()
27779
27922
  });
27780
27923
  /** Recording storage usage + capacity for one storage location. */
27781
27924
  var RecordingLocationUsageSchema = object({
@@ -27813,6 +27956,17 @@ var RecordingStorageUsageSchema = object({
27813
27956
  totalUsedBytes: number(),
27814
27957
  devices: array(RecordingDeviceUsageSchema),
27815
27958
  /**
27959
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
27960
+ * disks (D418).
27961
+ *
27962
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
27963
+ * declared no ledger and the recorder REFUSED rather than answering the
27964
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
27965
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
27966
+ * and its rows are residency.
27967
+ */
27968
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
27969
+ /**
27816
27970
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
27817
27971
  * of the payload without it: the hub serves this method through a BAKED
27818
27972
  * router, so a hub whose framework train predates a change to this array
@@ -34200,6 +34354,24 @@ Object.freeze({
34200
34354
  addonId: null,
34201
34355
  access: "view"
34202
34356
  },
34357
+ "pipelineRunner.getParkedTrackFrame": {
34358
+ capName: "pipeline-runner",
34359
+ capScope: "system",
34360
+ addonId: null,
34361
+ access: "view"
34362
+ },
34363
+ "pipelineRunner.parkTrackFrame": {
34364
+ capName: "pipeline-runner",
34365
+ capScope: "system",
34366
+ addonId: null,
34367
+ access: "create"
34368
+ },
34369
+ "pipelineRunner.releaseParkedTrackFrames": {
34370
+ capName: "pipeline-runner",
34371
+ capScope: "system",
34372
+ addonId: null,
34373
+ access: "create"
34374
+ },
34203
34375
  "pipelineRunner.reportMotion": {
34204
34376
  capName: "pipeline-runner",
34205
34377
  capScope: "system",
@@ -37477,6 +37649,21 @@ Object.freeze({
37477
37649
  form: "single",
37478
37650
  optional: false
37479
37651
  }],
37652
+ "pipelineRunner.getParkedTrackFrame": [{
37653
+ name: "deviceId",
37654
+ form: "single",
37655
+ optional: false
37656
+ }],
37657
+ "pipelineRunner.parkTrackFrame": [{
37658
+ name: "deviceId",
37659
+ form: "single",
37660
+ optional: false
37661
+ }],
37662
+ "pipelineRunner.releaseParkedTrackFrames": [{
37663
+ name: "deviceId",
37664
+ form: "single",
37665
+ optional: false
37666
+ }],
37480
37667
  "pipelineRunner.reportMotion": [{
37481
37668
  name: "deviceId",
37482
37669
  form: "single",
@@ -38168,7 +38355,7 @@ var PreviewTextField = string().max(200);
38168
38355
  * Whether the notification's preview is a STILL or a short animation.
38169
38356
  *
38170
38357
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
38171
- * rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
38358
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
38172
38359
  * night reads better as three seconds of motion than as one frame of it. Both
38173
38360
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
38174
38361
  * simply applies it to a dozen frames sampled across the render and assembles
@@ -39,7 +39,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
40
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
41
41
  //#endregion
42
- //#region ../types/dist/event-category-CnLqLOKs.mjs
42
+ //#region ../types/dist/event-category-DAXzJeTX.mjs
43
43
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
44
44
  EventCategory["SystemBoot"] = "system.boot";
45
45
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -631,7 +631,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
631
631
  * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
632
632
  */
633
633
  EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
634
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
635
634
  EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
636
635
  /**
637
636
  * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
@@ -12596,7 +12595,7 @@ DeviceType.Camera;
12596
12595
  var SourceInfoSchema = object({
12597
12596
  /** Live dispatch key — mutable when the source system allows rename. */
12598
12597
  id: string(),
12599
- /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'frigate'. */
12598
+ /** Source system tag — e.g. 'homeassistant' | 'reolink' | 'onvif'. */
12600
12599
  system: string(),
12601
12600
  /** Immutable upstream identifier when available (HA `unique_id`, … ).
12602
12601
  * Used to detect rename when `id` changes. */
@@ -13083,7 +13082,7 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13083
13082
  * - `device-management.router.ts` (deleted in Phase 2)
13084
13083
  * - `device-ops.router.ts` (compat layer — deleted; device-provider ops absorbed here)
13085
13084
  *
13086
- * All device provider addons (rtsp, onvif, frigate, …) are hub-local: they may
13085
+ * All device provider addons (rtsp, onvif, …) are hub-local: they may
13087
13086
  * fork into separate processes but never run on remote cluster agents. Therefore:
13088
13087
  * - No nodeId routing needed — this is a pure hub singleton.
13089
13088
  * - The hub's DeviceRegistry is the single source of truth for all live devices.
@@ -17112,7 +17111,7 @@ var NcRuleInputSchema = object({
17112
17111
  * The measured seven-person arrival on device 590 spans 110 s with every
17113
17112
  * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
17114
17113
  * idle cutoff holds it as one and ends it when the arrival actually ends.
17115
- * 30 is Frigate's shipped value for the same decision.
17114
+ * 30 s is a widely shipped value for the same decision.
17116
17115
  *
17117
17116
  * ### What it replaces
17118
17117
  *
@@ -19154,6 +19153,13 @@ var ZoneCrossingSchema = object({
19154
19153
  /** Zone display name at crossing time (falls back to the id). */
19155
19154
  zoneName: string().optional()
19156
19155
  });
19156
+ /** One zone a box was stamped with, and how much of the BOX fell inside it. */
19157
+ var ZoneOverlapSchema = object({
19158
+ zoneId: string(),
19159
+ zoneName: string().optional(),
19160
+ /** Percentage of the BOX area inside the zone, 0–100. */
19161
+ overlapPct: number()
19162
+ });
19157
19163
  var ObjectEventSchema = object({
19158
19164
  ...BaseEventFields,
19159
19165
  kind: literal("object"),
@@ -19178,6 +19184,35 @@ var ObjectEventSchema = object({
19178
19184
  bbox: BoundingBoxSchema.optional(),
19179
19185
  /** Heavy JSON — omitted in slim projection. */
19180
19186
  zones: array(string()).readonly().optional(),
19187
+ /**
19188
+ * How far inside each stamped zone the box actually was, as a percentage of
19189
+ * the BOX area.
19190
+ *
19191
+ * Membership alone cannot express "at least a third of it inside", which is
19192
+ * the bar an operator argues with — and the number already existed: the
19193
+ * engine computed it, the pipeline logged it, and then discarded it. It
19194
+ * matters most on the events that carry no track, where a rule has nothing
19195
+ * else to judge by.
19196
+ */
19197
+ zoneOverlaps: array(ZoneOverlapSchema).readonly().optional(),
19198
+ /**
19199
+ * Present when this event describes a detection the TRACKER refused, so it
19200
+ * has no track and never will.
19201
+ *
19202
+ * A zone-scoped immediate rule asks "is there an animal in the flowerbed
19203
+ * NOW?"; tracking answers a different question ("is this the same subject as
19204
+ * before?") whose confirmation threshold presumes continuity. On 2026-09-09 a
19205
+ * dog at 0.7729, 100% inside a watched zone, was refused three times in six
19206
+ * seconds with three different candidate ids — seven analysed frames in the
19207
+ * window, the subject in a handful of them, metres apart — and produced no
19208
+ * event at all.
19209
+ *
19210
+ * It is NAMED rather than merely implied by a missing `trackId`: an event
19211
+ * with no track and no reason is indistinguishable from a track whose media
19212
+ * went missing, and a consumer that asks this one for a best shot or a
19213
+ * history is asking for something that never existed.
19214
+ */
19215
+ spawnRefused: string().optional(),
19181
19216
  /** Omitted in slim projection. */
19182
19217
  state: TrackStateSchema.optional(),
19183
19218
  /**
@@ -20251,6 +20286,60 @@ var NativeCropResultSchema = object({
20251
20286
  */
20252
20287
  tier: _enum(["native", "ram-fullframe"]).optional()
20253
20288
  });
20289
+ /**
20290
+ * What a PARKED frame is for — the `kind` half of the `(deviceId, trackId,
20291
+ * kind)` key ([D422](../../../docs/decisions/adr-0422-a-relevant-frame-is-parked-when-it-becomes-relevant.md)).
20292
+ *
20293
+ * - `keyFrame` — the track's clean best FULL frame at native width (capped by
20294
+ * the caller's `maxWidth`). What `keyFrame` + `keyFrameSmall` are written
20295
+ * from, and what the detail plane's full-frame rung cuts from.
20296
+ * - `thumbnail` — the best-shot subject slab: the caller's 16:9 central-square
20297
+ * window, cut by the runner at uncapped native resolution.
20298
+ *
20299
+ * The runner never interprets `trackId`; it is an opaque key the hub owns.
20300
+ */
20301
+ var ParkedFrameKindSchema = _enum(["keyFrame", "thumbnail"]);
20302
+ /**
20303
+ * Why a park did NOT happen. Every one is logged on the runner with
20304
+ * `tags: { deviceId }` and the handle's measured age, because a handle already
20305
+ * gone at park time IS the defect the 150–500 ms window exists to prevent —
20306
+ * never a reason to retry in a loop.
20307
+ *
20308
+ * - `handle-gone` — the native surface no longer resolves this handle (never
20309
+ * registered, past the lease bound, or the worker released it). `detail`
20310
+ * carries the runner's own miss reason.
20311
+ * - `encode-failed` — pixels resolved and the JPEG encode threw.
20312
+ */
20313
+ var ParkRefusalSchema = _enum(["handle-gone", "encode-failed"]);
20314
+ /** Answer to `parkTrackFrame` — a discriminated union, so "not parked" always names why. */
20315
+ var ParkTrackFrameResultSchema = discriminatedUnion("parked", [object({
20316
+ parked: literal(true),
20317
+ /** The node holding the parcel — what a later retrieval must be pinned to. */
20318
+ nodeId: string(),
20319
+ width: number().int().positive(),
20320
+ height: number().int().positive(),
20321
+ /** JPEG bytes the parcel costs on the node. */
20322
+ bytes: number().int().nonnegative(),
20323
+ /** Whether this park replaced a parcel already held for the key. */
20324
+ replaced: boolean()
20325
+ }), object({
20326
+ parked: literal(false),
20327
+ nodeId: string(),
20328
+ reason: ParkRefusalSchema,
20329
+ /** Age of the handle at the park, on the runner's clock; null if unknown. */
20330
+ handleAgeMs: number().nullable(),
20331
+ detail: string().optional()
20332
+ })]);
20333
+ /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
20334
+ var ParkedTrackFrameSchema = object({
20335
+ jpeg: string(),
20336
+ width: number().int().positive(),
20337
+ height: number().int().positive(),
20338
+ /** The frame instant the parcel shows (the caller's clock, echoed back). */
20339
+ timestamp: number(),
20340
+ /** When the node stored it (the node's clock). */
20341
+ parkedAt: number()
20342
+ });
20254
20343
  /** Parent detection context passed to `runDetailSubtree` — the crop's
20255
20344
  * originating detection, in FRAME-space coordinates. Reuses
20256
20345
  * `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
@@ -20640,7 +20729,7 @@ var RunnerCameraConfigSchema = object({
20640
20729
  * - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
20641
20730
  * analyzer providers) running on decoded frames in this runner.
20642
20731
  * - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
20643
- * analytics, Frigate motion). The provider emits ProviderMotion
20732
+ * analytics). The provider emits ProviderMotion
20644
20733
  * events; the orchestrator forwards to `reportMotion`.
20645
20734
  */
20646
20735
  motionSources: MotionSourcesSchema.default(["analyzer"]),
@@ -20882,7 +20971,26 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
20882
20971
  className: string(),
20883
20972
  /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
20884
20973
  sourceDeviceId: number()
20885
- }), RunStatelessStepResultSchema, { kind: "mutation" });
20974
+ }), RunStatelessStepResultSchema, { kind: "mutation" }), method(object({
20975
+ deviceId: number(),
20976
+ /** Opaque to the runner — the hub's track id. */
20977
+ trackId: string(),
20978
+ kind: ParkedFrameKindSchema,
20979
+ handle: FrameHandleSchema,
20980
+ /** Normalized [0,1] window to cut. `{0,0,1,1}` for a full frame. */
20981
+ bbox: NativeCropBboxSchema,
20982
+ /** Width cap for a full-frame park (the `keyFrame` native cap). */
20983
+ maxWidth: number().int().positive().optional(),
20984
+ /** The frame instant (caller's clock) — orders parks for one key. */
20985
+ timestamp: number()
20986
+ }), ParkTrackFrameResultSchema, { kind: "mutation" }), method(object({
20987
+ deviceId: number(),
20988
+ trackId: string(),
20989
+ kind: ParkedFrameKindSchema
20990
+ }), ParkedTrackFrameSchema.nullable()), method(object({
20991
+ deviceId: number(),
20992
+ trackId: string()
20993
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
20886
20994
  var CameraPipelineConfigSchema = object({
20887
20995
  engine: PipelineEngineChoiceSchema.optional(),
20888
20996
  steps: array(PipelineStepInputSchema).readonly(),
@@ -23811,7 +23919,7 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
23811
23919
  *
23812
23920
  * Implementations:
23813
23921
  * - Each camera-providing addon registers this cap natively per
23814
- * device (Reolink, Hikvision, RTSP, Frigate, ONVIF self).
23922
+ * device (Reolink, Hikvision, RTSP, ONVIF self).
23815
23923
  * - Returns a snapshot from the persisted device config blob — no
23816
23924
  * extra round-trip to the camera, no probing.
23817
23925
  * - The cap is admin-only (`auth: 'admin'`) — credentials must not
@@ -25197,7 +25305,7 @@ var AvailableIntegrationTypeSchema = object({
25197
25305
  * Which integration-marker cap the addon declared, so the wizard can
25198
25306
  * branch on CAP — never on addon name. `device-adoption` integrations
25199
25307
  * (Home Assistant, …) route through the broker step (Approach A);
25200
- * `device-provider` integrations (Reolink/Frigate/ONVIF) keep the
25308
+ * `device-provider` integrations (Reolink/ONVIF) keep the
25201
25309
  * legacy config → discovery flow.
25202
25310
  */
25203
25311
  kind: _enum(["device-adoption", "device-provider"]),
@@ -27754,13 +27862,31 @@ var RecordingManifestSchema = object({
27754
27862
  */
27755
27863
  playbackEndpoints: array(string())
27756
27864
  });
27757
- /** Recording storage usage for one camera (bytes on disk across all its
27758
- * profiles/subtrees/locations on this node). */
27865
+ /**
27866
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
27867
+ * across every profile and every resolvable location on this node.
27868
+ *
27869
+ * ## Archive vs residency, which is the whole point of this shape (D418)
27870
+ *
27871
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
27872
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
27873
+ * this camera writes to, so the two can be added up and compared.
27874
+ *
27875
+ * They were not always. Until D418 this row came from the recorder's in-RAM
27876
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
27877
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
27878
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
27879
+ * that said otherwise, and two people read it as retention eating recent
27880
+ * footage. Residency is still reported, under `residentBytes` /
27881
+ * `residentOldestMs`, which answer a different question and say so.
27882
+ */
27759
27883
  var RecordingDeviceUsageSchema = object({
27760
27884
  deviceId: number(),
27885
+ /** Bytes the ARCHIVE holds for this camera on this node's resolvable
27886
+ * recordings locations. */
27761
27887
  usedBytes: number(),
27762
27888
  /**
27763
- * Start of this camera's OLDEST indexed segment, across every profile and
27889
+ * Start of this camera's OLDEST ARCHIVED segment, across every profile and
27764
27890
  * location — the "Oldest footage" column in Recordings → Storage, and the
27765
27891
  * only honest answer to "is retention actually holding?" per camera.
27766
27892
  *
@@ -27770,7 +27896,24 @@ var RecordingDeviceUsageSchema = object({
27770
27896
  * (types) and the addon ship on different trains, and the addon is usually
27771
27897
  * the later of the two.
27772
27898
  */
27773
- oldestMs: number().nullable().optional()
27899
+ oldestMs: number().nullable().optional(),
27900
+ /** Segments the archive holds for this camera — the "M" beside `usedBytes`.
27901
+ * OPTIONAL for the same train-skew reason as `oldestMs`. */
27902
+ segments: number().optional(),
27903
+ /**
27904
+ * RESIDENCY, not the archive: bytes the recorder process currently holds in
27905
+ * its RAM index — the live tail plus the hours a reader hydrated.
27906
+ *
27907
+ * It answers "what can be served without touching the disk", never "how much
27908
+ * footage this camera has". A UI that renders it must label it as such, and
27909
+ * must never total it as storage. OPTIONAL: a recorder predating D418 omits
27910
+ * it, and so does a node whose ledger could not answer.
27911
+ */
27912
+ residentBytes: number().optional(),
27913
+ /** The residency counterpart of `oldestMs` — the oldest segment currently in
27914
+ * RAM, which on a healthy recorder is minutes ago and means nothing about
27915
+ * retention. `null` = nothing resident. */
27916
+ residentOldestMs: number().nullable().optional()
27774
27917
  });
27775
27918
  /** Recording storage usage + capacity for one storage location. */
27776
27919
  var RecordingLocationUsageSchema = object({
@@ -27808,6 +27951,17 @@ var RecordingStorageUsageSchema = object({
27808
27951
  totalUsedBytes: number(),
27809
27952
  devices: array(RecordingDeviceUsageSchema),
27810
27953
  /**
27954
+ * WHERE `devices[]` came from, so an empty table is never mistaken for empty
27955
+ * disks (D418).
27956
+ *
27957
+ * `archive` = the durable hour ledger answered. `unavailable` = this node
27958
+ * declared no ledger and the recorder REFUSED rather than answering the
27959
+ * question with the resident index (D295 rule 3) — the rows are UNKNOWN, not
27960
+ * zero, and a UI must say so. OPTIONAL: a recorder predating D418 omits it,
27961
+ * and its rows are residency.
27962
+ */
27963
+ devicesRead: _enum(["archive", "unavailable"]).optional(),
27964
+ /**
27811
27965
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
27812
27966
  * of the payload without it: the hub serves this method through a BAKED
27813
27967
  * router, so a hub whose framework train predates a change to this array
@@ -34195,6 +34349,24 @@ Object.freeze({
34195
34349
  addonId: null,
34196
34350
  access: "view"
34197
34351
  },
34352
+ "pipelineRunner.getParkedTrackFrame": {
34353
+ capName: "pipeline-runner",
34354
+ capScope: "system",
34355
+ addonId: null,
34356
+ access: "view"
34357
+ },
34358
+ "pipelineRunner.parkTrackFrame": {
34359
+ capName: "pipeline-runner",
34360
+ capScope: "system",
34361
+ addonId: null,
34362
+ access: "create"
34363
+ },
34364
+ "pipelineRunner.releaseParkedTrackFrames": {
34365
+ capName: "pipeline-runner",
34366
+ capScope: "system",
34367
+ addonId: null,
34368
+ access: "create"
34369
+ },
34198
34370
  "pipelineRunner.reportMotion": {
34199
34371
  capName: "pipeline-runner",
34200
34372
  capScope: "system",
@@ -37472,6 +37644,21 @@ Object.freeze({
37472
37644
  form: "single",
37473
37645
  optional: false
37474
37646
  }],
37647
+ "pipelineRunner.getParkedTrackFrame": [{
37648
+ name: "deviceId",
37649
+ form: "single",
37650
+ optional: false
37651
+ }],
37652
+ "pipelineRunner.parkTrackFrame": [{
37653
+ name: "deviceId",
37654
+ form: "single",
37655
+ optional: false
37656
+ }],
37657
+ "pipelineRunner.releaseParkedTrackFrames": [{
37658
+ name: "deviceId",
37659
+ form: "single",
37660
+ optional: false
37661
+ }],
37475
37662
  "pipelineRunner.reportMotion": [{
37476
37663
  name: "deviceId",
37477
37664
  form: "single",
@@ -38163,7 +38350,7 @@ var PreviewTextField = string().max(200);
38163
38350
  * Whether the notification's preview is a STILL or a short animation.
38164
38351
  *
38165
38352
  * The operator's ask, verbatim: *"inviato come gif o video (come per le altre
38166
- * rule)"* — his Scrypted advanced-notifier has a `gifRule`, and a ten-hour
38353
+ * rule)"* — the other rules already offer an animated preview, and a ten-hour
38167
38354
  * night reads better as three seconds of motion than as one frame of it. Both
38168
38355
  * modes get the SAME treatment (blurred frame, large centred title); `'gif'`
38169
38356
  * simply applies it to a dozen frames sampled across the render and assembles
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-mqtt-broker",
3
- "version": "1.2.89",
3
+ "version": "1.2.92",
4
4
  "description": "MQTT broker registry addon for CamStack — manages external broker entries + an optional embedded aedes broker. Consumers spin up their own `mqtt.js` clients via the `mqtt-broker` cap.",
5
5
  "keywords": [
6
6
  "camstack",