@camstack/addon-matter-broker 0.1.22 → 0.1.24

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/addon.js CHANGED
@@ -13,7 +13,7 @@ let node_stream_promises = require("node:stream/promises");
13
13
  let node_net = require("node:net");
14
14
  let node_dgram = require("node:dgram");
15
15
  node_dgram = require_esm.__toESM(node_dgram, 1);
16
- //#region ../types/dist/event-category-H4AVePnn.mjs
16
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
17
17
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
18
18
  EventCategory["SystemBoot"] = "system.boot";
19
19
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -166,6 +166,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
166
166
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
167
167
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
168
168
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
169
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
170
+ * progress bar the client reconciles via `recordingExport.getExport`. */
171
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
172
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
173
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
169
174
  EventCategory["DetectionEvent"] = "detection.event";
170
175
  EventCategory["SessionTrackNew"] = "session.track.new";
171
176
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -455,6 +460,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
455
460
  */
456
461
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
457
462
  /**
463
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
464
+ * a package zone — a newly-appeared stationary object of a package class
465
+ * that cleared the class / zone / dwell / size gates. Payload:
466
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
467
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
468
+ * Telemetry (D8): the durable record is the `package-events` store row;
469
+ * this bus topic drives notifier rules + live UI. See
470
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
471
+ */
472
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
473
+ /**
474
+ * Fired by `addon-post-analysis` when a previously-delivered package
475
+ * leaves its zone (the stationary entry departed — moved or swept).
476
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
477
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
478
+ * Telemetry (D8). See package-zones-design §5.2.
479
+ */
480
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
481
+ /**
458
482
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
459
483
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
460
484
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5207,6 +5231,25 @@ function preprocess(fn, schema) {
5207
5231
  out: schema
5208
5232
  });
5209
5233
  }
5234
+ //#endregion
5235
+ //#region ../../node_modules/zod/v4/classic/compat.js
5236
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5237
+ var ZodIssueCode = {
5238
+ invalid_type: "invalid_type",
5239
+ too_big: "too_big",
5240
+ too_small: "too_small",
5241
+ invalid_format: "invalid_format",
5242
+ not_multiple_of: "not_multiple_of",
5243
+ unrecognized_keys: "unrecognized_keys",
5244
+ invalid_union: "invalid_union",
5245
+ invalid_key: "invalid_key",
5246
+ invalid_element: "invalid_element",
5247
+ invalid_value: "invalid_value",
5248
+ custom: "custom"
5249
+ };
5250
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5251
+ var ZodFirstPartyTypeKind;
5252
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5210
5253
  Object.fromEntries([
5211
5254
  {
5212
5255
  id: "overview",
@@ -7329,6 +7372,62 @@ var RecordingConfigSchema = object({
7329
7372
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7330
7373
  });
7331
7374
  /**
7375
+ * Ops-log — the durable, append-only operations audit shared by the
7376
+ * recordings and events management surfaces.
7377
+ *
7378
+ * ONE row shape is reused for both domains so a single "Activity" view can
7379
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7380
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7381
+ * management operation, WHY it ran (reason), and its measurable effect
7382
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7383
+ * never fail the operation it records.
7384
+ */
7385
+ /** Which management domain the operation belongs to. */
7386
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7387
+ /** The kind of management operation performed. */
7388
+ var OpsLogOpSchema = _enum([
7389
+ "prune",
7390
+ "manual-delete",
7391
+ "rescan",
7392
+ "retention-run"
7393
+ ]);
7394
+ /** Why the operation ran. */
7395
+ var OpsLogReasonSchema = _enum([
7396
+ "retention",
7397
+ "quota",
7398
+ "manual",
7399
+ "operator"
7400
+ ]);
7401
+ /** One audit row, shared verbatim by both domains. */
7402
+ var OpsLogEntrySchema = object({
7403
+ /** Unique row id. */
7404
+ id: string$2(),
7405
+ /** Epoch ms the operation completed. */
7406
+ at: number(),
7407
+ domain: OpsLogDomainSchema,
7408
+ op: OpsLogOpSchema,
7409
+ reason: OpsLogReasonSchema,
7410
+ /** The camera the op targeted; null for a cluster/global op. */
7411
+ deviceId: number().nullable(),
7412
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7413
+ nodeId: string$2(),
7414
+ /** Buckets / rows deleted (op-specific unit). */
7415
+ itemsAffected: number(),
7416
+ /** Bytes reclaimed by the op (0 when not measurable). */
7417
+ bytesReclaimed: number(),
7418
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7419
+ detail: string$2().nullable(),
7420
+ /** Who/what triggered the op. */
7421
+ actor: string$2()
7422
+ });
7423
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7424
+ var OpsLogQueryInputSchema = object({
7425
+ /** Restrict to a single camera; omit for every row. */
7426
+ deviceId: number().optional(),
7427
+ /** Max rows returned, newest-first. */
7428
+ limit: number().int().min(1).max(1e3).optional()
7429
+ });
7430
+ /**
7332
7431
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7333
7432
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7334
7433
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14684,7 +14783,11 @@ array(ZoneRuleSchema).readonly();
14684
14783
  * Extend the enum here when a new gating consumer comes online (audio
14685
14784
  * gating, alert filtering, …) — no other surface needs to change.
14686
14785
  */
14687
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14786
+ var ZoneRuleStageEnum = _enum([
14787
+ "motion",
14788
+ "detection",
14789
+ "package"
14790
+ ]);
14688
14791
  /**
14689
14792
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14690
14793
  * `state` getter looks up the cap definition here to construct a
@@ -14778,16 +14881,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14778
14881
  })
14779
14882
  },
14780
14883
  /**
14781
- * Runtime-state slice — both stages mirrored together so consumers
14884
+ * Runtime-state slice — every stage mirrored together so consumers
14782
14885
  * see one reactive handle (`device.state.zoneRules.value`) instead
14783
- * of two. Bulk-replace mutations on either stage write the full
14784
- * `{motion, detection}` shape, so subscribers always get the
14886
+ * of one per stage. Bulk-replace mutations on any stage write the full
14887
+ * `{motion, detection, package}` shape, so subscribers always get the
14785
14888
  * complete current set. Consumers that only care about one stage
14786
14889
  * just read the matching property.
14890
+ *
14891
+ * `package` backs the package-drop detector — a package zone is a
14892
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14893
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14894
+ * The orchestrator provider writes this stage as a first-class slice
14895
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14896
+ * package}` shape, so consumers read the current package rules directly
14897
+ * off `device.state.zoneRules.value.package`.
14787
14898
  */
14788
14899
  runtimeState: object({
14789
14900
  motion: array(ZoneRuleSchema).readonly(),
14790
- detection: array(ZoneRuleSchema).readonly()
14901
+ detection: array(ZoneRuleSchema).readonly(),
14902
+ package: array(ZoneRuleSchema).readonly()
14791
14903
  })
14792
14904
  },
14793
14905
  zones: zonesCapability
@@ -18815,6 +18927,7 @@ var EventKindIconSchema = _enum([
18815
18927
  "smoke",
18816
18928
  "water",
18817
18929
  "button",
18930
+ "package",
18818
18931
  "generic"
18819
18932
  ]);
18820
18933
  var EventKindCategorySchema = _enum([
@@ -18822,7 +18935,8 @@ var EventKindCategorySchema = _enum([
18822
18935
  "audio",
18823
18936
  "detection",
18824
18937
  "sensor",
18825
- "custom"
18938
+ "custom",
18939
+ "package"
18826
18940
  ]);
18827
18941
  var EventKindDescriptorSchema = object({
18828
18942
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19163,6 +19277,26 @@ var TrackCascadeCountsSchema = object({
19163
19277
  /** Per-track CLIP search vectors removed (best-effort). */
19164
19278
  embeddings: number().int()
19165
19279
  });
19280
+ /** Event-store footprint for one camera. */
19281
+ var EventStoreDeviceFootprintSchema = object({
19282
+ deviceId: number(),
19283
+ /** Persisted event rows (motion + object + audio) for the camera. */
19284
+ rows: number().int(),
19285
+ /** Event-owned media bytes on disk for the camera. */
19286
+ bytes: number().int()
19287
+ });
19288
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19289
+ var EventStoreFootprintSchema = object({
19290
+ totalRows: number().int(),
19291
+ totalBytes: number().int(),
19292
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19293
+ });
19294
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19295
+ var EventPruneCountsSchema = object({
19296
+ motion: number().int(),
19297
+ object: number().int(),
19298
+ audio: number().int()
19299
+ });
19166
19300
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19167
19301
  deviceId: number(),
19168
19302
  trackId: string$2()
@@ -19226,6 +19360,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19226
19360
  }), {
19227
19361
  kind: "mutation",
19228
19362
  auth: "admin"
19363
+ }), method(object({}), EventStoreFootprintSchema, {
19364
+ kind: "query",
19365
+ auth: "admin"
19366
+ }), method(object({
19367
+ olderThanMs: number(),
19368
+ reason: OpsLogReasonSchema.optional()
19369
+ }), EventPruneCountsSchema, {
19370
+ kind: "mutation",
19371
+ auth: "admin"
19372
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19373
+ kind: "mutation",
19374
+ auth: "admin"
19375
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19376
+ kind: "query",
19377
+ auth: "admin"
19229
19378
  }), method(object({
19230
19379
  eventId: string$2(),
19231
19380
  kind: MediaFileKindEnum.optional()
@@ -22476,13 +22625,131 @@ method(object({
22476
22625
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22477
22626
  kind: "mutation",
22478
22627
  auth: "admin"
22479
- }), method(object({ deviceId: number() }), object({
22628
+ }), method(object({
22629
+ deviceId: number(),
22630
+ reason: OpsLogReasonSchema.optional()
22631
+ }), object({
22480
22632
  floorMs: number().nullable(),
22481
22633
  deletedBuckets: number().int(),
22482
22634
  reclaimedBytes: number().int()
22483
22635
  }), {
22484
22636
  kind: "mutation",
22485
22637
  auth: "admin"
22638
+ }), method(object({
22639
+ deviceId: number(),
22640
+ fromMs: number().optional(),
22641
+ toMs: number().optional()
22642
+ }), object({
22643
+ deletedBuckets: number().int(),
22644
+ reclaimedBytes: number().int()
22645
+ }), {
22646
+ kind: "mutation",
22647
+ auth: "admin"
22648
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22649
+ kind: "query",
22650
+ auth: "admin"
22651
+ });
22652
+ /**
22653
+ * `recordingExport` cap — render a footage time range into a single downloadable
22654
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22655
+ * bounded lifetime with a durable history, auto-expiry, and optional
22656
+ * delete-after-download.
22657
+ *
22658
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22659
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22660
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22661
+ * input and dispatch to that single provider; the render runs on the node that
22662
+ * owns the footage (no cross-node segment transfer). Download rides the
22663
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22664
+ * SQLite); history rows survive file deletion for audit.
22665
+ */
22666
+ /** Playback-speed multiplier for the render (1 = realtime). */
22667
+ var ExportSpeedSchema = number().min(.25).max(32);
22668
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22669
+ var ExportTimelapseSchema = object({
22670
+ everyMs: number().int().positive(),
22671
+ outputFps: number().int().min(1).max(60).optional()
22672
+ });
22673
+ /**
22674
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22675
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22676
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22677
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22678
+ */
22679
+ var ExportOptionsSchema = object({
22680
+ speed: ExportSpeedSchema.optional(),
22681
+ timelapse: ExportTimelapseSchema.optional(),
22682
+ includeAudio: boolean(),
22683
+ maxLifeMs: number().int().positive(),
22684
+ deleteAfterDownload: boolean(),
22685
+ title: string$2().max(200).optional()
22686
+ }).superRefine((v, ctx) => {
22687
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22688
+ code: ZodIssueCode.custom,
22689
+ message: "speed and timelapse are mutually exclusive",
22690
+ path: ["timelapse"]
22691
+ });
22692
+ });
22693
+ var ExportStateSchema = _enum([
22694
+ "queued",
22695
+ "rendering",
22696
+ "ready",
22697
+ "failed",
22698
+ "expired",
22699
+ "deleted"
22700
+ ]);
22701
+ /** One export job / history row. */
22702
+ var ExportRecordSchema = object({
22703
+ id: string$2(),
22704
+ deviceId: number(),
22705
+ profile: string$2(),
22706
+ fromMs: number(),
22707
+ toMs: number(),
22708
+ options: ExportOptionsSchema,
22709
+ state: ExportStateSchema,
22710
+ /** 0–100 while rendering; null otherwise. */
22711
+ progressPct: number().nullable(),
22712
+ /** File size once ready; null before. */
22713
+ fileBytes: number().nullable(),
22714
+ expiresAt: number(),
22715
+ deleteAfterDownload: boolean(),
22716
+ /** Epoch of the first complete download; null until then. */
22717
+ downloadedAt: number().nullable(),
22718
+ createdAt: number(),
22719
+ /** User id/name that requested the export. */
22720
+ createdBy: string$2(),
22721
+ /** Failure reason when state is 'failed'; null otherwise. */
22722
+ error: string$2().nullable()
22723
+ });
22724
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22725
+ var ExportDownloadSchema = object({
22726
+ url: string$2(),
22727
+ endpoints: array(string$2())
22728
+ });
22729
+ method(object({
22730
+ deviceId: number(),
22731
+ profile: string$2(),
22732
+ fromMs: number(),
22733
+ toMs: number(),
22734
+ options: ExportOptionsSchema
22735
+ }), ExportRecordSchema, {
22736
+ kind: "mutation",
22737
+ auth: "protected"
22738
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22739
+ kind: "query",
22740
+ auth: "protected"
22741
+ }), method(object({ exportId: string$2() }), ExportRecordSchema, {
22742
+ kind: "query",
22743
+ auth: "protected"
22744
+ }), method(object({ exportId: string$2() }), ExportRecordSchema, {
22745
+ kind: "mutation",
22746
+ auth: "protected"
22747
+ }), method(object({ exportId: string$2() }), ExportRecordSchema, {
22748
+ kind: "mutation",
22749
+ auth: "protected"
22750
+ }), method(object({ exportId: string$2() }), ExportDownloadSchema, {
22751
+ kind: "query",
22752
+ auth: "protected"
22486
22753
  });
22487
22754
  /**
22488
22755
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25502,6 +25769,12 @@ Object.freeze({
25502
25769
  addonId: null,
25503
25770
  access: "delete"
25504
25771
  },
25772
+ "pipelineAnalytics.deleteDeviceEvents": {
25773
+ capName: "pipeline-analytics",
25774
+ capScope: "device",
25775
+ addonId: null,
25776
+ access: "delete"
25777
+ },
25505
25778
  "pipelineAnalytics.deleteTracks": {
25506
25779
  capName: "pipeline-analytics",
25507
25780
  capScope: "device",
@@ -25532,6 +25805,12 @@ Object.freeze({
25532
25805
  addonId: null,
25533
25806
  access: "view"
25534
25807
  },
25808
+ "pipelineAnalytics.getEventStoreFootprint": {
25809
+ capName: "pipeline-analytics",
25810
+ capScope: "device",
25811
+ addonId: null,
25812
+ access: "view"
25813
+ },
25535
25814
  "pipelineAnalytics.getKeyEvents": {
25536
25815
  capName: "pipeline-analytics",
25537
25816
  capScope: "device",
@@ -25574,6 +25853,12 @@ Object.freeze({
25574
25853
  addonId: null,
25575
25854
  access: "view"
25576
25855
  },
25856
+ "pipelineAnalytics.listOpsLog": {
25857
+ capName: "pipeline-analytics",
25858
+ capScope: "device",
25859
+ addonId: null,
25860
+ access: "view"
25861
+ },
25577
25862
  "pipelineAnalytics.listRecentTracks": {
25578
25863
  capName: "pipeline-analytics",
25579
25864
  capScope: "device",
@@ -25586,6 +25871,12 @@ Object.freeze({
25586
25871
  addonId: null,
25587
25872
  access: "view"
25588
25873
  },
25874
+ "pipelineAnalytics.pruneEvents": {
25875
+ capName: "pipeline-analytics",
25876
+ capScope: "device",
25877
+ addonId: null,
25878
+ access: "create"
25879
+ },
25589
25880
  "pipelineAnalytics.pruneEventsBefore": {
25590
25881
  capName: "pipeline-analytics",
25591
25882
  capScope: "device",
@@ -26348,6 +26639,12 @@ Object.freeze({
26348
26639
  addonId: null,
26349
26640
  access: "create"
26350
26641
  },
26642
+ "recording.deleteFootprint": {
26643
+ capName: "recording",
26644
+ capScope: "system",
26645
+ addonId: null,
26646
+ access: "delete"
26647
+ },
26351
26648
  "recording.getAvailability": {
26352
26649
  capName: "recording",
26353
26650
  capScope: "system",
@@ -26378,6 +26675,12 @@ Object.freeze({
26378
26675
  addonId: null,
26379
26676
  access: "view"
26380
26677
  },
26678
+ "recording.listOpsLog": {
26679
+ capName: "recording",
26680
+ capScope: "system",
26681
+ addonId: null,
26682
+ access: "view"
26683
+ },
26381
26684
  "recording.locateSegment": {
26382
26685
  capName: "recording",
26383
26686
  capScope: "system",
@@ -26408,6 +26711,42 @@ Object.freeze({
26408
26711
  addonId: null,
26409
26712
  access: "create"
26410
26713
  },
26714
+ "recordingExport.cancelExport": {
26715
+ capName: "recordingExport",
26716
+ capScope: "system",
26717
+ addonId: null,
26718
+ access: "create"
26719
+ },
26720
+ "recordingExport.createExport": {
26721
+ capName: "recordingExport",
26722
+ capScope: "system",
26723
+ addonId: null,
26724
+ access: "create"
26725
+ },
26726
+ "recordingExport.deleteExport": {
26727
+ capName: "recordingExport",
26728
+ capScope: "system",
26729
+ addonId: null,
26730
+ access: "delete"
26731
+ },
26732
+ "recordingExport.getDownloadUrl": {
26733
+ capName: "recordingExport",
26734
+ capScope: "system",
26735
+ addonId: null,
26736
+ access: "view"
26737
+ },
26738
+ "recordingExport.getExport": {
26739
+ capName: "recordingExport",
26740
+ capScope: "system",
26741
+ addonId: null,
26742
+ access: "view"
26743
+ },
26744
+ "recordingExport.listExports": {
26745
+ capName: "recordingExport",
26746
+ capScope: "system",
26747
+ addonId: null,
26748
+ access: "view"
26749
+ },
26411
26750
  "sceneMonitor.captureReference": {
26412
26751
  capName: "scene-monitor",
26413
26752
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -11,7 +11,7 @@ import { networkInterfaces, tmpdir, uptime } from "node:os";
11
11
  import { finished } from "node:stream/promises";
12
12
  import { createConnection, createServer as createServer$2 } from "node:net";
13
13
  import * as dgram from "node:dgram";
14
- //#region ../types/dist/event-category-H4AVePnn.mjs
14
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
15
15
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
16
16
  EventCategory["SystemBoot"] = "system.boot";
17
17
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -164,6 +164,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
164
164
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
165
165
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
166
166
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
167
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
168
+ * progress bar the client reconciles via `recordingExport.getExport`. */
169
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
170
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
171
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
167
172
  EventCategory["DetectionEvent"] = "detection.event";
168
173
  EventCategory["SessionTrackNew"] = "session.track.new";
169
174
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -453,6 +458,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
453
458
  */
454
459
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
455
460
  /**
461
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
462
+ * a package zone — a newly-appeared stationary object of a package class
463
+ * that cleared the class / zone / dwell / size gates. Payload:
464
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
465
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
466
+ * Telemetry (D8): the durable record is the `package-events` store row;
467
+ * this bus topic drives notifier rules + live UI. See
468
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
469
+ */
470
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
471
+ /**
472
+ * Fired by `addon-post-analysis` when a previously-delivered package
473
+ * leaves its zone (the stationary entry departed — moved or swept).
474
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
475
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
476
+ * Telemetry (D8). See package-zones-design §5.2.
477
+ */
478
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
479
+ /**
456
480
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
457
481
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
458
482
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5205,6 +5229,25 @@ function preprocess(fn, schema) {
5205
5229
  out: schema
5206
5230
  });
5207
5231
  }
5232
+ //#endregion
5233
+ //#region ../../node_modules/zod/v4/classic/compat.js
5234
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5235
+ var ZodIssueCode = {
5236
+ invalid_type: "invalid_type",
5237
+ too_big: "too_big",
5238
+ too_small: "too_small",
5239
+ invalid_format: "invalid_format",
5240
+ not_multiple_of: "not_multiple_of",
5241
+ unrecognized_keys: "unrecognized_keys",
5242
+ invalid_union: "invalid_union",
5243
+ invalid_key: "invalid_key",
5244
+ invalid_element: "invalid_element",
5245
+ invalid_value: "invalid_value",
5246
+ custom: "custom"
5247
+ };
5248
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5249
+ var ZodFirstPartyTypeKind;
5250
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5208
5251
  Object.fromEntries([
5209
5252
  {
5210
5253
  id: "overview",
@@ -7327,6 +7370,62 @@ var RecordingConfigSchema = object({
7327
7370
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7328
7371
  });
7329
7372
  /**
7373
+ * Ops-log — the durable, append-only operations audit shared by the
7374
+ * recordings and events management surfaces.
7375
+ *
7376
+ * ONE row shape is reused for both domains so a single "Activity" view can
7377
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7378
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7379
+ * management operation, WHY it ran (reason), and its measurable effect
7380
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7381
+ * never fail the operation it records.
7382
+ */
7383
+ /** Which management domain the operation belongs to. */
7384
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7385
+ /** The kind of management operation performed. */
7386
+ var OpsLogOpSchema = _enum([
7387
+ "prune",
7388
+ "manual-delete",
7389
+ "rescan",
7390
+ "retention-run"
7391
+ ]);
7392
+ /** Why the operation ran. */
7393
+ var OpsLogReasonSchema = _enum([
7394
+ "retention",
7395
+ "quota",
7396
+ "manual",
7397
+ "operator"
7398
+ ]);
7399
+ /** One audit row, shared verbatim by both domains. */
7400
+ var OpsLogEntrySchema = object({
7401
+ /** Unique row id. */
7402
+ id: string$2(),
7403
+ /** Epoch ms the operation completed. */
7404
+ at: number(),
7405
+ domain: OpsLogDomainSchema,
7406
+ op: OpsLogOpSchema,
7407
+ reason: OpsLogReasonSchema,
7408
+ /** The camera the op targeted; null for a cluster/global op. */
7409
+ deviceId: number().nullable(),
7410
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7411
+ nodeId: string$2(),
7412
+ /** Buckets / rows deleted (op-specific unit). */
7413
+ itemsAffected: number(),
7414
+ /** Bytes reclaimed by the op (0 when not measurable). */
7415
+ bytesReclaimed: number(),
7416
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7417
+ detail: string$2().nullable(),
7418
+ /** Who/what triggered the op. */
7419
+ actor: string$2()
7420
+ });
7421
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7422
+ var OpsLogQueryInputSchema = object({
7423
+ /** Restrict to a single camera; omit for every row. */
7424
+ deviceId: number().optional(),
7425
+ /** Max rows returned, newest-first. */
7426
+ limit: number().int().min(1).max(1e3).optional()
7427
+ });
7428
+ /**
7330
7429
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7331
7430
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7332
7431
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14682,7 +14781,11 @@ array(ZoneRuleSchema).readonly();
14682
14781
  * Extend the enum here when a new gating consumer comes online (audio
14683
14782
  * gating, alert filtering, …) — no other surface needs to change.
14684
14783
  */
14685
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14784
+ var ZoneRuleStageEnum = _enum([
14785
+ "motion",
14786
+ "detection",
14787
+ "package"
14788
+ ]);
14686
14789
  /**
14687
14790
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14688
14791
  * `state` getter looks up the cap definition here to construct a
@@ -14776,16 +14879,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14776
14879
  })
14777
14880
  },
14778
14881
  /**
14779
- * Runtime-state slice — both stages mirrored together so consumers
14882
+ * Runtime-state slice — every stage mirrored together so consumers
14780
14883
  * see one reactive handle (`device.state.zoneRules.value`) instead
14781
- * of two. Bulk-replace mutations on either stage write the full
14782
- * `{motion, detection}` shape, so subscribers always get the
14884
+ * of one per stage. Bulk-replace mutations on any stage write the full
14885
+ * `{motion, detection, package}` shape, so subscribers always get the
14783
14886
  * complete current set. Consumers that only care about one stage
14784
14887
  * just read the matching property.
14888
+ *
14889
+ * `package` backs the package-drop detector — a package zone is a
14890
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14891
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14892
+ * The orchestrator provider writes this stage as a first-class slice
14893
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14894
+ * package}` shape, so consumers read the current package rules directly
14895
+ * off `device.state.zoneRules.value.package`.
14785
14896
  */
14786
14897
  runtimeState: object({
14787
14898
  motion: array(ZoneRuleSchema).readonly(),
14788
- detection: array(ZoneRuleSchema).readonly()
14899
+ detection: array(ZoneRuleSchema).readonly(),
14900
+ package: array(ZoneRuleSchema).readonly()
14789
14901
  })
14790
14902
  },
14791
14903
  zones: zonesCapability
@@ -18813,6 +18925,7 @@ var EventKindIconSchema = _enum([
18813
18925
  "smoke",
18814
18926
  "water",
18815
18927
  "button",
18928
+ "package",
18816
18929
  "generic"
18817
18930
  ]);
18818
18931
  var EventKindCategorySchema = _enum([
@@ -18820,7 +18933,8 @@ var EventKindCategorySchema = _enum([
18820
18933
  "audio",
18821
18934
  "detection",
18822
18935
  "sensor",
18823
- "custom"
18936
+ "custom",
18937
+ "package"
18824
18938
  ]);
18825
18939
  var EventKindDescriptorSchema = object({
18826
18940
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19161,6 +19275,26 @@ var TrackCascadeCountsSchema = object({
19161
19275
  /** Per-track CLIP search vectors removed (best-effort). */
19162
19276
  embeddings: number().int()
19163
19277
  });
19278
+ /** Event-store footprint for one camera. */
19279
+ var EventStoreDeviceFootprintSchema = object({
19280
+ deviceId: number(),
19281
+ /** Persisted event rows (motion + object + audio) for the camera. */
19282
+ rows: number().int(),
19283
+ /** Event-owned media bytes on disk for the camera. */
19284
+ bytes: number().int()
19285
+ });
19286
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19287
+ var EventStoreFootprintSchema = object({
19288
+ totalRows: number().int(),
19289
+ totalBytes: number().int(),
19290
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19291
+ });
19292
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19293
+ var EventPruneCountsSchema = object({
19294
+ motion: number().int(),
19295
+ object: number().int(),
19296
+ audio: number().int()
19297
+ });
19164
19298
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19165
19299
  deviceId: number(),
19166
19300
  trackId: string$2()
@@ -19224,6 +19358,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19224
19358
  }), {
19225
19359
  kind: "mutation",
19226
19360
  auth: "admin"
19361
+ }), method(object({}), EventStoreFootprintSchema, {
19362
+ kind: "query",
19363
+ auth: "admin"
19364
+ }), method(object({
19365
+ olderThanMs: number(),
19366
+ reason: OpsLogReasonSchema.optional()
19367
+ }), EventPruneCountsSchema, {
19368
+ kind: "mutation",
19369
+ auth: "admin"
19370
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19371
+ kind: "mutation",
19372
+ auth: "admin"
19373
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19374
+ kind: "query",
19375
+ auth: "admin"
19227
19376
  }), method(object({
19228
19377
  eventId: string$2(),
19229
19378
  kind: MediaFileKindEnum.optional()
@@ -22474,13 +22623,131 @@ method(object({
22474
22623
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22475
22624
  kind: "mutation",
22476
22625
  auth: "admin"
22477
- }), method(object({ deviceId: number() }), object({
22626
+ }), method(object({
22627
+ deviceId: number(),
22628
+ reason: OpsLogReasonSchema.optional()
22629
+ }), object({
22478
22630
  floorMs: number().nullable(),
22479
22631
  deletedBuckets: number().int(),
22480
22632
  reclaimedBytes: number().int()
22481
22633
  }), {
22482
22634
  kind: "mutation",
22483
22635
  auth: "admin"
22636
+ }), method(object({
22637
+ deviceId: number(),
22638
+ fromMs: number().optional(),
22639
+ toMs: number().optional()
22640
+ }), object({
22641
+ deletedBuckets: number().int(),
22642
+ reclaimedBytes: number().int()
22643
+ }), {
22644
+ kind: "mutation",
22645
+ auth: "admin"
22646
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22647
+ kind: "query",
22648
+ auth: "admin"
22649
+ });
22650
+ /**
22651
+ * `recordingExport` cap — render a footage time range into a single downloadable
22652
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22653
+ * bounded lifetime with a durable history, auto-expiry, and optional
22654
+ * delete-after-download.
22655
+ *
22656
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22657
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22658
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22659
+ * input and dispatch to that single provider; the render runs on the node that
22660
+ * owns the footage (no cross-node segment transfer). Download rides the
22661
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22662
+ * SQLite); history rows survive file deletion for audit.
22663
+ */
22664
+ /** Playback-speed multiplier for the render (1 = realtime). */
22665
+ var ExportSpeedSchema = number().min(.25).max(32);
22666
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22667
+ var ExportTimelapseSchema = object({
22668
+ everyMs: number().int().positive(),
22669
+ outputFps: number().int().min(1).max(60).optional()
22670
+ });
22671
+ /**
22672
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22673
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22674
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22675
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22676
+ */
22677
+ var ExportOptionsSchema = object({
22678
+ speed: ExportSpeedSchema.optional(),
22679
+ timelapse: ExportTimelapseSchema.optional(),
22680
+ includeAudio: boolean(),
22681
+ maxLifeMs: number().int().positive(),
22682
+ deleteAfterDownload: boolean(),
22683
+ title: string$2().max(200).optional()
22684
+ }).superRefine((v, ctx) => {
22685
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22686
+ code: ZodIssueCode.custom,
22687
+ message: "speed and timelapse are mutually exclusive",
22688
+ path: ["timelapse"]
22689
+ });
22690
+ });
22691
+ var ExportStateSchema = _enum([
22692
+ "queued",
22693
+ "rendering",
22694
+ "ready",
22695
+ "failed",
22696
+ "expired",
22697
+ "deleted"
22698
+ ]);
22699
+ /** One export job / history row. */
22700
+ var ExportRecordSchema = object({
22701
+ id: string$2(),
22702
+ deviceId: number(),
22703
+ profile: string$2(),
22704
+ fromMs: number(),
22705
+ toMs: number(),
22706
+ options: ExportOptionsSchema,
22707
+ state: ExportStateSchema,
22708
+ /** 0–100 while rendering; null otherwise. */
22709
+ progressPct: number().nullable(),
22710
+ /** File size once ready; null before. */
22711
+ fileBytes: number().nullable(),
22712
+ expiresAt: number(),
22713
+ deleteAfterDownload: boolean(),
22714
+ /** Epoch of the first complete download; null until then. */
22715
+ downloadedAt: number().nullable(),
22716
+ createdAt: number(),
22717
+ /** User id/name that requested the export. */
22718
+ createdBy: string$2(),
22719
+ /** Failure reason when state is 'failed'; null otherwise. */
22720
+ error: string$2().nullable()
22721
+ });
22722
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22723
+ var ExportDownloadSchema = object({
22724
+ url: string$2(),
22725
+ endpoints: array(string$2())
22726
+ });
22727
+ method(object({
22728
+ deviceId: number(),
22729
+ profile: string$2(),
22730
+ fromMs: number(),
22731
+ toMs: number(),
22732
+ options: ExportOptionsSchema
22733
+ }), ExportRecordSchema, {
22734
+ kind: "mutation",
22735
+ auth: "protected"
22736
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22737
+ kind: "query",
22738
+ auth: "protected"
22739
+ }), method(object({ exportId: string$2() }), ExportRecordSchema, {
22740
+ kind: "query",
22741
+ auth: "protected"
22742
+ }), method(object({ exportId: string$2() }), ExportRecordSchema, {
22743
+ kind: "mutation",
22744
+ auth: "protected"
22745
+ }), method(object({ exportId: string$2() }), ExportRecordSchema, {
22746
+ kind: "mutation",
22747
+ auth: "protected"
22748
+ }), method(object({ exportId: string$2() }), ExportDownloadSchema, {
22749
+ kind: "query",
22750
+ auth: "protected"
22484
22751
  });
22485
22752
  /**
22486
22753
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25500,6 +25767,12 @@ Object.freeze({
25500
25767
  addonId: null,
25501
25768
  access: "delete"
25502
25769
  },
25770
+ "pipelineAnalytics.deleteDeviceEvents": {
25771
+ capName: "pipeline-analytics",
25772
+ capScope: "device",
25773
+ addonId: null,
25774
+ access: "delete"
25775
+ },
25503
25776
  "pipelineAnalytics.deleteTracks": {
25504
25777
  capName: "pipeline-analytics",
25505
25778
  capScope: "device",
@@ -25530,6 +25803,12 @@ Object.freeze({
25530
25803
  addonId: null,
25531
25804
  access: "view"
25532
25805
  },
25806
+ "pipelineAnalytics.getEventStoreFootprint": {
25807
+ capName: "pipeline-analytics",
25808
+ capScope: "device",
25809
+ addonId: null,
25810
+ access: "view"
25811
+ },
25533
25812
  "pipelineAnalytics.getKeyEvents": {
25534
25813
  capName: "pipeline-analytics",
25535
25814
  capScope: "device",
@@ -25572,6 +25851,12 @@ Object.freeze({
25572
25851
  addonId: null,
25573
25852
  access: "view"
25574
25853
  },
25854
+ "pipelineAnalytics.listOpsLog": {
25855
+ capName: "pipeline-analytics",
25856
+ capScope: "device",
25857
+ addonId: null,
25858
+ access: "view"
25859
+ },
25575
25860
  "pipelineAnalytics.listRecentTracks": {
25576
25861
  capName: "pipeline-analytics",
25577
25862
  capScope: "device",
@@ -25584,6 +25869,12 @@ Object.freeze({
25584
25869
  addonId: null,
25585
25870
  access: "view"
25586
25871
  },
25872
+ "pipelineAnalytics.pruneEvents": {
25873
+ capName: "pipeline-analytics",
25874
+ capScope: "device",
25875
+ addonId: null,
25876
+ access: "create"
25877
+ },
25587
25878
  "pipelineAnalytics.pruneEventsBefore": {
25588
25879
  capName: "pipeline-analytics",
25589
25880
  capScope: "device",
@@ -26346,6 +26637,12 @@ Object.freeze({
26346
26637
  addonId: null,
26347
26638
  access: "create"
26348
26639
  },
26640
+ "recording.deleteFootprint": {
26641
+ capName: "recording",
26642
+ capScope: "system",
26643
+ addonId: null,
26644
+ access: "delete"
26645
+ },
26349
26646
  "recording.getAvailability": {
26350
26647
  capName: "recording",
26351
26648
  capScope: "system",
@@ -26376,6 +26673,12 @@ Object.freeze({
26376
26673
  addonId: null,
26377
26674
  access: "view"
26378
26675
  },
26676
+ "recording.listOpsLog": {
26677
+ capName: "recording",
26678
+ capScope: "system",
26679
+ addonId: null,
26680
+ access: "view"
26681
+ },
26379
26682
  "recording.locateSegment": {
26380
26683
  capName: "recording",
26381
26684
  capScope: "system",
@@ -26406,6 +26709,42 @@ Object.freeze({
26406
26709
  addonId: null,
26407
26710
  access: "create"
26408
26711
  },
26712
+ "recordingExport.cancelExport": {
26713
+ capName: "recordingExport",
26714
+ capScope: "system",
26715
+ addonId: null,
26716
+ access: "create"
26717
+ },
26718
+ "recordingExport.createExport": {
26719
+ capName: "recordingExport",
26720
+ capScope: "system",
26721
+ addonId: null,
26722
+ access: "create"
26723
+ },
26724
+ "recordingExport.deleteExport": {
26725
+ capName: "recordingExport",
26726
+ capScope: "system",
26727
+ addonId: null,
26728
+ access: "delete"
26729
+ },
26730
+ "recordingExport.getDownloadUrl": {
26731
+ capName: "recordingExport",
26732
+ capScope: "system",
26733
+ addonId: null,
26734
+ access: "view"
26735
+ },
26736
+ "recordingExport.getExport": {
26737
+ capName: "recordingExport",
26738
+ capScope: "system",
26739
+ addonId: null,
26740
+ access: "view"
26741
+ },
26742
+ "recordingExport.listExports": {
26743
+ capName: "recordingExport",
26744
+ capScope: "system",
26745
+ addonId: null,
26746
+ access: "view"
26747
+ },
26409
26748
  "sceneMonitor.captureReference": {
26410
26749
  capName: "scene-monitor",
26411
26750
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-matter-broker",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
5
5
  "keywords": [
6
6
  "camstack",