@camstack/addon-provider-rtsp 1.1.25 → 1.1.27

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
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  let node_net = require("node:net");
25
25
  node_net = __toESM(node_net);
26
- //#region ../types/dist/event-category-H4AVePnn.mjs
26
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -176,6 +176,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
176
176
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
177
177
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
178
178
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
179
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
180
+ * progress bar the client reconciles via `recordingExport.getExport`. */
181
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
182
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
183
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
179
184
  EventCategory["DetectionEvent"] = "detection.event";
180
185
  EventCategory["SessionTrackNew"] = "session.track.new";
181
186
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -465,6 +470,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
465
470
  */
466
471
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
467
472
  /**
473
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
474
+ * a package zone — a newly-appeared stationary object of a package class
475
+ * that cleared the class / zone / dwell / size gates. Payload:
476
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
477
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
478
+ * Telemetry (D8): the durable record is the `package-events` store row;
479
+ * this bus topic drives notifier rules + live UI. See
480
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
481
+ */
482
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
483
+ /**
484
+ * Fired by `addon-post-analysis` when a previously-delivered package
485
+ * leaves its zone (the stationary entry departed — moved or swept).
486
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
487
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
488
+ * Telemetry (D8). See package-zones-design §5.2.
489
+ */
490
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
491
+ /**
468
492
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
469
493
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
470
494
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5217,6 +5241,25 @@ function preprocess(fn, schema) {
5217
5241
  out: schema
5218
5242
  });
5219
5243
  }
5244
+ //#endregion
5245
+ //#region ../../node_modules/zod/v4/classic/compat.js
5246
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5247
+ var ZodIssueCode = {
5248
+ invalid_type: "invalid_type",
5249
+ too_big: "too_big",
5250
+ too_small: "too_small",
5251
+ invalid_format: "invalid_format",
5252
+ not_multiple_of: "not_multiple_of",
5253
+ unrecognized_keys: "unrecognized_keys",
5254
+ invalid_union: "invalid_union",
5255
+ invalid_key: "invalid_key",
5256
+ invalid_element: "invalid_element",
5257
+ invalid_value: "invalid_value",
5258
+ custom: "custom"
5259
+ };
5260
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5261
+ var ZodFirstPartyTypeKind;
5262
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5220
5263
  Object.fromEntries([
5221
5264
  {
5222
5265
  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(),
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(),
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().nullable(),
7418
+ /** Who/what triggered the op. */
7419
+ actor: string()
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
@@ -14780,7 +14879,11 @@ array(ZoneRuleSchema).readonly();
14780
14879
  * Extend the enum here when a new gating consumer comes online (audio
14781
14880
  * gating, alert filtering, …) — no other surface needs to change.
14782
14881
  */
14783
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14882
+ var ZoneRuleStageEnum = _enum([
14883
+ "motion",
14884
+ "detection",
14885
+ "package"
14886
+ ]);
14784
14887
  /**
14785
14888
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14786
14889
  * `state` getter looks up the cap definition here to construct a
@@ -14874,16 +14977,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14874
14977
  })
14875
14978
  },
14876
14979
  /**
14877
- * Runtime-state slice — both stages mirrored together so consumers
14980
+ * Runtime-state slice — every stage mirrored together so consumers
14878
14981
  * see one reactive handle (`device.state.zoneRules.value`) instead
14879
- * of two. Bulk-replace mutations on either stage write the full
14880
- * `{motion, detection}` shape, so subscribers always get the
14982
+ * of one per stage. Bulk-replace mutations on any stage write the full
14983
+ * `{motion, detection, package}` shape, so subscribers always get the
14881
14984
  * complete current set. Consumers that only care about one stage
14882
14985
  * just read the matching property.
14986
+ *
14987
+ * `package` backs the package-drop detector — a package zone is a
14988
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14989
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14990
+ * The orchestrator provider writes this stage as a first-class slice
14991
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14992
+ * package}` shape, so consumers read the current package rules directly
14993
+ * off `device.state.zoneRules.value.package`.
14883
14994
  */
14884
14995
  runtimeState: object({
14885
14996
  motion: array(ZoneRuleSchema).readonly(),
14886
- detection: array(ZoneRuleSchema).readonly()
14997
+ detection: array(ZoneRuleSchema).readonly(),
14998
+ package: array(ZoneRuleSchema).readonly()
14887
14999
  })
14888
15000
  },
14889
15001
  zones: zonesCapability
@@ -18845,6 +18957,7 @@ var EventKindIconSchema = _enum([
18845
18957
  "smoke",
18846
18958
  "water",
18847
18959
  "button",
18960
+ "package",
18848
18961
  "generic"
18849
18962
  ]);
18850
18963
  var EventKindCategorySchema = _enum([
@@ -18852,7 +18965,8 @@ var EventKindCategorySchema = _enum([
18852
18965
  "audio",
18853
18966
  "detection",
18854
18967
  "sensor",
18855
- "custom"
18968
+ "custom",
18969
+ "package"
18856
18970
  ]);
18857
18971
  var EventKindDescriptorSchema = object({
18858
18972
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19193,6 +19307,26 @@ var TrackCascadeCountsSchema = object({
19193
19307
  /** Per-track CLIP search vectors removed (best-effort). */
19194
19308
  embeddings: number().int()
19195
19309
  });
19310
+ /** Event-store footprint for one camera. */
19311
+ var EventStoreDeviceFootprintSchema = object({
19312
+ deviceId: number(),
19313
+ /** Persisted event rows (motion + object + audio) for the camera. */
19314
+ rows: number().int(),
19315
+ /** Event-owned media bytes on disk for the camera. */
19316
+ bytes: number().int()
19317
+ });
19318
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19319
+ var EventStoreFootprintSchema = object({
19320
+ totalRows: number().int(),
19321
+ totalBytes: number().int(),
19322
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19323
+ });
19324
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19325
+ var EventPruneCountsSchema = object({
19326
+ motion: number().int(),
19327
+ object: number().int(),
19328
+ audio: number().int()
19329
+ });
19196
19330
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19197
19331
  deviceId: number(),
19198
19332
  trackId: string()
@@ -19256,6 +19390,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19256
19390
  }), {
19257
19391
  kind: "mutation",
19258
19392
  auth: "admin"
19393
+ }), method(object({}), EventStoreFootprintSchema, {
19394
+ kind: "query",
19395
+ auth: "admin"
19396
+ }), method(object({
19397
+ olderThanMs: number(),
19398
+ reason: OpsLogReasonSchema.optional()
19399
+ }), EventPruneCountsSchema, {
19400
+ kind: "mutation",
19401
+ auth: "admin"
19402
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19403
+ kind: "mutation",
19404
+ auth: "admin"
19405
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19406
+ kind: "query",
19407
+ auth: "admin"
19259
19408
  }), method(object({
19260
19409
  eventId: string(),
19261
19410
  kind: MediaFileKindEnum.optional()
@@ -22540,13 +22689,131 @@ method(object({
22540
22689
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22541
22690
  kind: "mutation",
22542
22691
  auth: "admin"
22543
- }), method(object({ deviceId: number() }), object({
22692
+ }), method(object({
22693
+ deviceId: number(),
22694
+ reason: OpsLogReasonSchema.optional()
22695
+ }), object({
22544
22696
  floorMs: number().nullable(),
22545
22697
  deletedBuckets: number().int(),
22546
22698
  reclaimedBytes: number().int()
22547
22699
  }), {
22548
22700
  kind: "mutation",
22549
22701
  auth: "admin"
22702
+ }), method(object({
22703
+ deviceId: number(),
22704
+ fromMs: number().optional(),
22705
+ toMs: number().optional()
22706
+ }), object({
22707
+ deletedBuckets: number().int(),
22708
+ reclaimedBytes: number().int()
22709
+ }), {
22710
+ kind: "mutation",
22711
+ auth: "admin"
22712
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22713
+ kind: "query",
22714
+ auth: "admin"
22715
+ });
22716
+ /**
22717
+ * `recordingExport` cap — render a footage time range into a single downloadable
22718
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22719
+ * bounded lifetime with a durable history, auto-expiry, and optional
22720
+ * delete-after-download.
22721
+ *
22722
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22723
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22724
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22725
+ * input and dispatch to that single provider; the render runs on the node that
22726
+ * owns the footage (no cross-node segment transfer). Download rides the
22727
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22728
+ * SQLite); history rows survive file deletion for audit.
22729
+ */
22730
+ /** Playback-speed multiplier for the render (1 = realtime). */
22731
+ var ExportSpeedSchema = number().min(.25).max(32);
22732
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22733
+ var ExportTimelapseSchema = object({
22734
+ everyMs: number().int().positive(),
22735
+ outputFps: number().int().min(1).max(60).optional()
22736
+ });
22737
+ /**
22738
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22739
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22740
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22741
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22742
+ */
22743
+ var ExportOptionsSchema = object({
22744
+ speed: ExportSpeedSchema.optional(),
22745
+ timelapse: ExportTimelapseSchema.optional(),
22746
+ includeAudio: boolean(),
22747
+ maxLifeMs: number().int().positive(),
22748
+ deleteAfterDownload: boolean(),
22749
+ title: string().max(200).optional()
22750
+ }).superRefine((v, ctx) => {
22751
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22752
+ code: ZodIssueCode.custom,
22753
+ message: "speed and timelapse are mutually exclusive",
22754
+ path: ["timelapse"]
22755
+ });
22756
+ });
22757
+ var ExportStateSchema = _enum([
22758
+ "queued",
22759
+ "rendering",
22760
+ "ready",
22761
+ "failed",
22762
+ "expired",
22763
+ "deleted"
22764
+ ]);
22765
+ /** One export job / history row. */
22766
+ var ExportRecordSchema = object({
22767
+ id: string(),
22768
+ deviceId: number(),
22769
+ profile: string(),
22770
+ fromMs: number(),
22771
+ toMs: number(),
22772
+ options: ExportOptionsSchema,
22773
+ state: ExportStateSchema,
22774
+ /** 0–100 while rendering; null otherwise. */
22775
+ progressPct: number().nullable(),
22776
+ /** File size once ready; null before. */
22777
+ fileBytes: number().nullable(),
22778
+ expiresAt: number(),
22779
+ deleteAfterDownload: boolean(),
22780
+ /** Epoch of the first complete download; null until then. */
22781
+ downloadedAt: number().nullable(),
22782
+ createdAt: number(),
22783
+ /** User id/name that requested the export. */
22784
+ createdBy: string(),
22785
+ /** Failure reason when state is 'failed'; null otherwise. */
22786
+ error: string().nullable()
22787
+ });
22788
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22789
+ var ExportDownloadSchema = object({
22790
+ url: string(),
22791
+ endpoints: array(string())
22792
+ });
22793
+ method(object({
22794
+ deviceId: number(),
22795
+ profile: string(),
22796
+ fromMs: number(),
22797
+ toMs: number(),
22798
+ options: ExportOptionsSchema
22799
+ }), ExportRecordSchema, {
22800
+ kind: "mutation",
22801
+ auth: "protected"
22802
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22803
+ kind: "query",
22804
+ auth: "protected"
22805
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22806
+ kind: "query",
22807
+ auth: "protected"
22808
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22809
+ kind: "mutation",
22810
+ auth: "protected"
22811
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22812
+ kind: "mutation",
22813
+ auth: "protected"
22814
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22815
+ kind: "query",
22816
+ auth: "protected"
22550
22817
  });
22551
22818
  /**
22552
22819
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25579,6 +25846,12 @@ Object.freeze({
25579
25846
  addonId: null,
25580
25847
  access: "delete"
25581
25848
  },
25849
+ "pipelineAnalytics.deleteDeviceEvents": {
25850
+ capName: "pipeline-analytics",
25851
+ capScope: "device",
25852
+ addonId: null,
25853
+ access: "delete"
25854
+ },
25582
25855
  "pipelineAnalytics.deleteTracks": {
25583
25856
  capName: "pipeline-analytics",
25584
25857
  capScope: "device",
@@ -25609,6 +25882,12 @@ Object.freeze({
25609
25882
  addonId: null,
25610
25883
  access: "view"
25611
25884
  },
25885
+ "pipelineAnalytics.getEventStoreFootprint": {
25886
+ capName: "pipeline-analytics",
25887
+ capScope: "device",
25888
+ addonId: null,
25889
+ access: "view"
25890
+ },
25612
25891
  "pipelineAnalytics.getKeyEvents": {
25613
25892
  capName: "pipeline-analytics",
25614
25893
  capScope: "device",
@@ -25651,6 +25930,12 @@ Object.freeze({
25651
25930
  addonId: null,
25652
25931
  access: "view"
25653
25932
  },
25933
+ "pipelineAnalytics.listOpsLog": {
25934
+ capName: "pipeline-analytics",
25935
+ capScope: "device",
25936
+ addonId: null,
25937
+ access: "view"
25938
+ },
25654
25939
  "pipelineAnalytics.listRecentTracks": {
25655
25940
  capName: "pipeline-analytics",
25656
25941
  capScope: "device",
@@ -25663,6 +25948,12 @@ Object.freeze({
25663
25948
  addonId: null,
25664
25949
  access: "view"
25665
25950
  },
25951
+ "pipelineAnalytics.pruneEvents": {
25952
+ capName: "pipeline-analytics",
25953
+ capScope: "device",
25954
+ addonId: null,
25955
+ access: "create"
25956
+ },
25666
25957
  "pipelineAnalytics.pruneEventsBefore": {
25667
25958
  capName: "pipeline-analytics",
25668
25959
  capScope: "device",
@@ -26425,6 +26716,12 @@ Object.freeze({
26425
26716
  addonId: null,
26426
26717
  access: "create"
26427
26718
  },
26719
+ "recording.deleteFootprint": {
26720
+ capName: "recording",
26721
+ capScope: "system",
26722
+ addonId: null,
26723
+ access: "delete"
26724
+ },
26428
26725
  "recording.getAvailability": {
26429
26726
  capName: "recording",
26430
26727
  capScope: "system",
@@ -26455,6 +26752,12 @@ Object.freeze({
26455
26752
  addonId: null,
26456
26753
  access: "view"
26457
26754
  },
26755
+ "recording.listOpsLog": {
26756
+ capName: "recording",
26757
+ capScope: "system",
26758
+ addonId: null,
26759
+ access: "view"
26760
+ },
26458
26761
  "recording.locateSegment": {
26459
26762
  capName: "recording",
26460
26763
  capScope: "system",
@@ -26485,6 +26788,42 @@ Object.freeze({
26485
26788
  addonId: null,
26486
26789
  access: "create"
26487
26790
  },
26791
+ "recordingExport.cancelExport": {
26792
+ capName: "recordingExport",
26793
+ capScope: "system",
26794
+ addonId: null,
26795
+ access: "create"
26796
+ },
26797
+ "recordingExport.createExport": {
26798
+ capName: "recordingExport",
26799
+ capScope: "system",
26800
+ addonId: null,
26801
+ access: "create"
26802
+ },
26803
+ "recordingExport.deleteExport": {
26804
+ capName: "recordingExport",
26805
+ capScope: "system",
26806
+ addonId: null,
26807
+ access: "delete"
26808
+ },
26809
+ "recordingExport.getDownloadUrl": {
26810
+ capName: "recordingExport",
26811
+ capScope: "system",
26812
+ addonId: null,
26813
+ access: "view"
26814
+ },
26815
+ "recordingExport.getExport": {
26816
+ capName: "recordingExport",
26817
+ capScope: "system",
26818
+ addonId: null,
26819
+ access: "view"
26820
+ },
26821
+ "recordingExport.listExports": {
26822
+ capName: "recordingExport",
26823
+ capScope: "system",
26824
+ addonId: null,
26825
+ access: "view"
26826
+ },
26488
26827
  "sceneMonitor.captureReference": {
26489
26828
  capName: "scene-monitor",
26490
26829
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import net from "node:net";
2
- //#region ../types/dist/event-category-H4AVePnn.mjs
2
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -152,6 +152,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
152
152
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
153
153
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
154
154
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
155
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
156
+ * progress bar the client reconciles via `recordingExport.getExport`. */
157
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
158
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
159
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
155
160
  EventCategory["DetectionEvent"] = "detection.event";
156
161
  EventCategory["SessionTrackNew"] = "session.track.new";
157
162
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -441,6 +446,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
441
446
  */
442
447
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
443
448
  /**
449
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
450
+ * a package zone — a newly-appeared stationary object of a package class
451
+ * that cleared the class / zone / dwell / size gates. Payload:
452
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
453
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
454
+ * Telemetry (D8): the durable record is the `package-events` store row;
455
+ * this bus topic drives notifier rules + live UI. See
456
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
457
+ */
458
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
459
+ /**
460
+ * Fired by `addon-post-analysis` when a previously-delivered package
461
+ * leaves its zone (the stationary entry departed — moved or swept).
462
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
463
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
464
+ * Telemetry (D8). See package-zones-design §5.2.
465
+ */
466
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
467
+ /**
444
468
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
445
469
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
446
470
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5193,6 +5217,25 @@ function preprocess(fn, schema) {
5193
5217
  out: schema
5194
5218
  });
5195
5219
  }
5220
+ //#endregion
5221
+ //#region ../../node_modules/zod/v4/classic/compat.js
5222
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5223
+ var ZodIssueCode = {
5224
+ invalid_type: "invalid_type",
5225
+ too_big: "too_big",
5226
+ too_small: "too_small",
5227
+ invalid_format: "invalid_format",
5228
+ not_multiple_of: "not_multiple_of",
5229
+ unrecognized_keys: "unrecognized_keys",
5230
+ invalid_union: "invalid_union",
5231
+ invalid_key: "invalid_key",
5232
+ invalid_element: "invalid_element",
5233
+ invalid_value: "invalid_value",
5234
+ custom: "custom"
5235
+ };
5236
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5237
+ var ZodFirstPartyTypeKind;
5238
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5196
5239
  Object.fromEntries([
5197
5240
  {
5198
5241
  id: "overview",
@@ -7303,6 +7346,62 @@ var RecordingConfigSchema = object({
7303
7346
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7304
7347
  });
7305
7348
  /**
7349
+ * Ops-log — the durable, append-only operations audit shared by the
7350
+ * recordings and events management surfaces.
7351
+ *
7352
+ * ONE row shape is reused for both domains so a single "Activity" view can
7353
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7354
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7355
+ * management operation, WHY it ran (reason), and its measurable effect
7356
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7357
+ * never fail the operation it records.
7358
+ */
7359
+ /** Which management domain the operation belongs to. */
7360
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7361
+ /** The kind of management operation performed. */
7362
+ var OpsLogOpSchema = _enum([
7363
+ "prune",
7364
+ "manual-delete",
7365
+ "rescan",
7366
+ "retention-run"
7367
+ ]);
7368
+ /** Why the operation ran. */
7369
+ var OpsLogReasonSchema = _enum([
7370
+ "retention",
7371
+ "quota",
7372
+ "manual",
7373
+ "operator"
7374
+ ]);
7375
+ /** One audit row, shared verbatim by both domains. */
7376
+ var OpsLogEntrySchema = object({
7377
+ /** Unique row id. */
7378
+ id: string(),
7379
+ /** Epoch ms the operation completed. */
7380
+ at: number(),
7381
+ domain: OpsLogDomainSchema,
7382
+ op: OpsLogOpSchema,
7383
+ reason: OpsLogReasonSchema,
7384
+ /** The camera the op targeted; null for a cluster/global op. */
7385
+ deviceId: number().nullable(),
7386
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7387
+ nodeId: string(),
7388
+ /** Buckets / rows deleted (op-specific unit). */
7389
+ itemsAffected: number(),
7390
+ /** Bytes reclaimed by the op (0 when not measurable). */
7391
+ bytesReclaimed: number(),
7392
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7393
+ detail: string().nullable(),
7394
+ /** Who/what triggered the op. */
7395
+ actor: string()
7396
+ });
7397
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7398
+ var OpsLogQueryInputSchema = object({
7399
+ /** Restrict to a single camera; omit for every row. */
7400
+ deviceId: number().optional(),
7401
+ /** Max rows returned, newest-first. */
7402
+ limit: number().int().min(1).max(1e3).optional()
7403
+ });
7404
+ /**
7306
7405
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7307
7406
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7308
7407
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14756,7 +14855,11 @@ array(ZoneRuleSchema).readonly();
14756
14855
  * Extend the enum here when a new gating consumer comes online (audio
14757
14856
  * gating, alert filtering, …) — no other surface needs to change.
14758
14857
  */
14759
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14858
+ var ZoneRuleStageEnum = _enum([
14859
+ "motion",
14860
+ "detection",
14861
+ "package"
14862
+ ]);
14760
14863
  /**
14761
14864
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14762
14865
  * `state` getter looks up the cap definition here to construct a
@@ -14850,16 +14953,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14850
14953
  })
14851
14954
  },
14852
14955
  /**
14853
- * Runtime-state slice — both stages mirrored together so consumers
14956
+ * Runtime-state slice — every stage mirrored together so consumers
14854
14957
  * see one reactive handle (`device.state.zoneRules.value`) instead
14855
- * of two. Bulk-replace mutations on either stage write the full
14856
- * `{motion, detection}` shape, so subscribers always get the
14958
+ * of one per stage. Bulk-replace mutations on any stage write the full
14959
+ * `{motion, detection, package}` shape, so subscribers always get the
14857
14960
  * complete current set. Consumers that only care about one stage
14858
14961
  * just read the matching property.
14962
+ *
14963
+ * `package` backs the package-drop detector — a package zone is a
14964
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14965
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14966
+ * The orchestrator provider writes this stage as a first-class slice
14967
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14968
+ * package}` shape, so consumers read the current package rules directly
14969
+ * off `device.state.zoneRules.value.package`.
14859
14970
  */
14860
14971
  runtimeState: object({
14861
14972
  motion: array(ZoneRuleSchema).readonly(),
14862
- detection: array(ZoneRuleSchema).readonly()
14973
+ detection: array(ZoneRuleSchema).readonly(),
14974
+ package: array(ZoneRuleSchema).readonly()
14863
14975
  })
14864
14976
  },
14865
14977
  zones: zonesCapability
@@ -18821,6 +18933,7 @@ var EventKindIconSchema = _enum([
18821
18933
  "smoke",
18822
18934
  "water",
18823
18935
  "button",
18936
+ "package",
18824
18937
  "generic"
18825
18938
  ]);
18826
18939
  var EventKindCategorySchema = _enum([
@@ -18828,7 +18941,8 @@ var EventKindCategorySchema = _enum([
18828
18941
  "audio",
18829
18942
  "detection",
18830
18943
  "sensor",
18831
- "custom"
18944
+ "custom",
18945
+ "package"
18832
18946
  ]);
18833
18947
  var EventKindDescriptorSchema = object({
18834
18948
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19169,6 +19283,26 @@ var TrackCascadeCountsSchema = object({
19169
19283
  /** Per-track CLIP search vectors removed (best-effort). */
19170
19284
  embeddings: number().int()
19171
19285
  });
19286
+ /** Event-store footprint for one camera. */
19287
+ var EventStoreDeviceFootprintSchema = object({
19288
+ deviceId: number(),
19289
+ /** Persisted event rows (motion + object + audio) for the camera. */
19290
+ rows: number().int(),
19291
+ /** Event-owned media bytes on disk for the camera. */
19292
+ bytes: number().int()
19293
+ });
19294
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19295
+ var EventStoreFootprintSchema = object({
19296
+ totalRows: number().int(),
19297
+ totalBytes: number().int(),
19298
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19299
+ });
19300
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19301
+ var EventPruneCountsSchema = object({
19302
+ motion: number().int(),
19303
+ object: number().int(),
19304
+ audio: number().int()
19305
+ });
19172
19306
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19173
19307
  deviceId: number(),
19174
19308
  trackId: string()
@@ -19232,6 +19366,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19232
19366
  }), {
19233
19367
  kind: "mutation",
19234
19368
  auth: "admin"
19369
+ }), method(object({}), EventStoreFootprintSchema, {
19370
+ kind: "query",
19371
+ auth: "admin"
19372
+ }), method(object({
19373
+ olderThanMs: number(),
19374
+ reason: OpsLogReasonSchema.optional()
19375
+ }), EventPruneCountsSchema, {
19376
+ kind: "mutation",
19377
+ auth: "admin"
19378
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19379
+ kind: "mutation",
19380
+ auth: "admin"
19381
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19382
+ kind: "query",
19383
+ auth: "admin"
19235
19384
  }), method(object({
19236
19385
  eventId: string(),
19237
19386
  kind: MediaFileKindEnum.optional()
@@ -22516,13 +22665,131 @@ method(object({
22516
22665
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22517
22666
  kind: "mutation",
22518
22667
  auth: "admin"
22519
- }), method(object({ deviceId: number() }), object({
22668
+ }), method(object({
22669
+ deviceId: number(),
22670
+ reason: OpsLogReasonSchema.optional()
22671
+ }), object({
22520
22672
  floorMs: number().nullable(),
22521
22673
  deletedBuckets: number().int(),
22522
22674
  reclaimedBytes: number().int()
22523
22675
  }), {
22524
22676
  kind: "mutation",
22525
22677
  auth: "admin"
22678
+ }), method(object({
22679
+ deviceId: number(),
22680
+ fromMs: number().optional(),
22681
+ toMs: number().optional()
22682
+ }), object({
22683
+ deletedBuckets: number().int(),
22684
+ reclaimedBytes: number().int()
22685
+ }), {
22686
+ kind: "mutation",
22687
+ auth: "admin"
22688
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22689
+ kind: "query",
22690
+ auth: "admin"
22691
+ });
22692
+ /**
22693
+ * `recordingExport` cap — render a footage time range into a single downloadable
22694
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22695
+ * bounded lifetime with a durable history, auto-expiry, and optional
22696
+ * delete-after-download.
22697
+ *
22698
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22699
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22700
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22701
+ * input and dispatch to that single provider; the render runs on the node that
22702
+ * owns the footage (no cross-node segment transfer). Download rides the
22703
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22704
+ * SQLite); history rows survive file deletion for audit.
22705
+ */
22706
+ /** Playback-speed multiplier for the render (1 = realtime). */
22707
+ var ExportSpeedSchema = number().min(.25).max(32);
22708
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22709
+ var ExportTimelapseSchema = object({
22710
+ everyMs: number().int().positive(),
22711
+ outputFps: number().int().min(1).max(60).optional()
22712
+ });
22713
+ /**
22714
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22715
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22716
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22717
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22718
+ */
22719
+ var ExportOptionsSchema = object({
22720
+ speed: ExportSpeedSchema.optional(),
22721
+ timelapse: ExportTimelapseSchema.optional(),
22722
+ includeAudio: boolean(),
22723
+ maxLifeMs: number().int().positive(),
22724
+ deleteAfterDownload: boolean(),
22725
+ title: string().max(200).optional()
22726
+ }).superRefine((v, ctx) => {
22727
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22728
+ code: ZodIssueCode.custom,
22729
+ message: "speed and timelapse are mutually exclusive",
22730
+ path: ["timelapse"]
22731
+ });
22732
+ });
22733
+ var ExportStateSchema = _enum([
22734
+ "queued",
22735
+ "rendering",
22736
+ "ready",
22737
+ "failed",
22738
+ "expired",
22739
+ "deleted"
22740
+ ]);
22741
+ /** One export job / history row. */
22742
+ var ExportRecordSchema = object({
22743
+ id: string(),
22744
+ deviceId: number(),
22745
+ profile: string(),
22746
+ fromMs: number(),
22747
+ toMs: number(),
22748
+ options: ExportOptionsSchema,
22749
+ state: ExportStateSchema,
22750
+ /** 0–100 while rendering; null otherwise. */
22751
+ progressPct: number().nullable(),
22752
+ /** File size once ready; null before. */
22753
+ fileBytes: number().nullable(),
22754
+ expiresAt: number(),
22755
+ deleteAfterDownload: boolean(),
22756
+ /** Epoch of the first complete download; null until then. */
22757
+ downloadedAt: number().nullable(),
22758
+ createdAt: number(),
22759
+ /** User id/name that requested the export. */
22760
+ createdBy: string(),
22761
+ /** Failure reason when state is 'failed'; null otherwise. */
22762
+ error: string().nullable()
22763
+ });
22764
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22765
+ var ExportDownloadSchema = object({
22766
+ url: string(),
22767
+ endpoints: array(string())
22768
+ });
22769
+ method(object({
22770
+ deviceId: number(),
22771
+ profile: string(),
22772
+ fromMs: number(),
22773
+ toMs: number(),
22774
+ options: ExportOptionsSchema
22775
+ }), ExportRecordSchema, {
22776
+ kind: "mutation",
22777
+ auth: "protected"
22778
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22779
+ kind: "query",
22780
+ auth: "protected"
22781
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22782
+ kind: "query",
22783
+ auth: "protected"
22784
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22785
+ kind: "mutation",
22786
+ auth: "protected"
22787
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22788
+ kind: "mutation",
22789
+ auth: "protected"
22790
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22791
+ kind: "query",
22792
+ auth: "protected"
22526
22793
  });
22527
22794
  /**
22528
22795
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25555,6 +25822,12 @@ Object.freeze({
25555
25822
  addonId: null,
25556
25823
  access: "delete"
25557
25824
  },
25825
+ "pipelineAnalytics.deleteDeviceEvents": {
25826
+ capName: "pipeline-analytics",
25827
+ capScope: "device",
25828
+ addonId: null,
25829
+ access: "delete"
25830
+ },
25558
25831
  "pipelineAnalytics.deleteTracks": {
25559
25832
  capName: "pipeline-analytics",
25560
25833
  capScope: "device",
@@ -25585,6 +25858,12 @@ Object.freeze({
25585
25858
  addonId: null,
25586
25859
  access: "view"
25587
25860
  },
25861
+ "pipelineAnalytics.getEventStoreFootprint": {
25862
+ capName: "pipeline-analytics",
25863
+ capScope: "device",
25864
+ addonId: null,
25865
+ access: "view"
25866
+ },
25588
25867
  "pipelineAnalytics.getKeyEvents": {
25589
25868
  capName: "pipeline-analytics",
25590
25869
  capScope: "device",
@@ -25627,6 +25906,12 @@ Object.freeze({
25627
25906
  addonId: null,
25628
25907
  access: "view"
25629
25908
  },
25909
+ "pipelineAnalytics.listOpsLog": {
25910
+ capName: "pipeline-analytics",
25911
+ capScope: "device",
25912
+ addonId: null,
25913
+ access: "view"
25914
+ },
25630
25915
  "pipelineAnalytics.listRecentTracks": {
25631
25916
  capName: "pipeline-analytics",
25632
25917
  capScope: "device",
@@ -25639,6 +25924,12 @@ Object.freeze({
25639
25924
  addonId: null,
25640
25925
  access: "view"
25641
25926
  },
25927
+ "pipelineAnalytics.pruneEvents": {
25928
+ capName: "pipeline-analytics",
25929
+ capScope: "device",
25930
+ addonId: null,
25931
+ access: "create"
25932
+ },
25642
25933
  "pipelineAnalytics.pruneEventsBefore": {
25643
25934
  capName: "pipeline-analytics",
25644
25935
  capScope: "device",
@@ -26401,6 +26692,12 @@ Object.freeze({
26401
26692
  addonId: null,
26402
26693
  access: "create"
26403
26694
  },
26695
+ "recording.deleteFootprint": {
26696
+ capName: "recording",
26697
+ capScope: "system",
26698
+ addonId: null,
26699
+ access: "delete"
26700
+ },
26404
26701
  "recording.getAvailability": {
26405
26702
  capName: "recording",
26406
26703
  capScope: "system",
@@ -26431,6 +26728,12 @@ Object.freeze({
26431
26728
  addonId: null,
26432
26729
  access: "view"
26433
26730
  },
26731
+ "recording.listOpsLog": {
26732
+ capName: "recording",
26733
+ capScope: "system",
26734
+ addonId: null,
26735
+ access: "view"
26736
+ },
26434
26737
  "recording.locateSegment": {
26435
26738
  capName: "recording",
26436
26739
  capScope: "system",
@@ -26461,6 +26764,42 @@ Object.freeze({
26461
26764
  addonId: null,
26462
26765
  access: "create"
26463
26766
  },
26767
+ "recordingExport.cancelExport": {
26768
+ capName: "recordingExport",
26769
+ capScope: "system",
26770
+ addonId: null,
26771
+ access: "create"
26772
+ },
26773
+ "recordingExport.createExport": {
26774
+ capName: "recordingExport",
26775
+ capScope: "system",
26776
+ addonId: null,
26777
+ access: "create"
26778
+ },
26779
+ "recordingExport.deleteExport": {
26780
+ capName: "recordingExport",
26781
+ capScope: "system",
26782
+ addonId: null,
26783
+ access: "delete"
26784
+ },
26785
+ "recordingExport.getDownloadUrl": {
26786
+ capName: "recordingExport",
26787
+ capScope: "system",
26788
+ addonId: null,
26789
+ access: "view"
26790
+ },
26791
+ "recordingExport.getExport": {
26792
+ capName: "recordingExport",
26793
+ capScope: "system",
26794
+ addonId: null,
26795
+ access: "view"
26796
+ },
26797
+ "recordingExport.listExports": {
26798
+ capName: "recordingExport",
26799
+ capScope: "system",
26800
+ addonId: null,
26801
+ access: "view"
26802
+ },
26464
26803
  "sceneMonitor.captureReference": {
26465
26804
  capName: "scene-monitor",
26466
26805
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rtsp",
3
- "version": "1.1.25",
3
+ "version": "1.1.27",
4
4
  "description": "Generic RTSP camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",