@camstack/addon-provider-reolink 1.1.27 → 1.1.29

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
@@ -26,7 +26,7 @@ let fs_promises = require("fs/promises");
26
26
  fs_promises = require_chunk.__toESM(fs_promises, 1);
27
27
  let node_os = require("node:os");
28
28
  node_os = require_chunk.__toESM(node_os);
29
- //#region ../types/dist/event-category-H4AVePnn.mjs
29
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
30
30
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
31
31
  EventCategory["SystemBoot"] = "system.boot";
32
32
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -179,6 +179,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
179
179
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
180
180
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
181
181
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
182
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
183
+ * progress bar the client reconciles via `recordingExport.getExport`. */
184
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
185
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
186
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
182
187
  EventCategory["DetectionEvent"] = "detection.event";
183
188
  EventCategory["SessionTrackNew"] = "session.track.new";
184
189
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -468,6 +473,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
468
473
  */
469
474
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
470
475
  /**
476
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
477
+ * a package zone — a newly-appeared stationary object of a package class
478
+ * that cleared the class / zone / dwell / size gates. Payload:
479
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
480
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
481
+ * Telemetry (D8): the durable record is the `package-events` store row;
482
+ * this bus topic drives notifier rules + live UI. See
483
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
484
+ */
485
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
486
+ /**
487
+ * Fired by `addon-post-analysis` when a previously-delivered package
488
+ * leaves its zone (the stationary entry departed — moved or swept).
489
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
490
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
491
+ * Telemetry (D8). See package-zones-design §5.2.
492
+ */
493
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
494
+ /**
471
495
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
472
496
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
473
497
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5206,6 +5230,25 @@ function _instanceof(cls, params = {}) {
5206
5230
  };
5207
5231
  return inst;
5208
5232
  }
5233
+ //#endregion
5234
+ //#region ../../node_modules/zod/v4/classic/compat.js
5235
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5236
+ var ZodIssueCode$16 = {
5237
+ invalid_type: "invalid_type",
5238
+ too_big: "too_big",
5239
+ too_small: "too_small",
5240
+ invalid_format: "invalid_format",
5241
+ not_multiple_of: "not_multiple_of",
5242
+ unrecognized_keys: "unrecognized_keys",
5243
+ invalid_union: "invalid_union",
5244
+ invalid_key: "invalid_key",
5245
+ invalid_element: "invalid_element",
5246
+ invalid_value: "invalid_value",
5247
+ custom: "custom"
5248
+ };
5249
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5250
+ var ZodFirstPartyTypeKind$16;
5251
+ ZodFirstPartyTypeKind$16 || (ZodFirstPartyTypeKind$16 = {});
5209
5252
  Object.fromEntries([
5210
5253
  {
5211
5254
  id: "overview",
@@ -7515,6 +7558,62 @@ var RecordingConfigSchema = object({
7515
7558
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7516
7559
  });
7517
7560
  /**
7561
+ * Ops-log — the durable, append-only operations audit shared by the
7562
+ * recordings and events management surfaces.
7563
+ *
7564
+ * ONE row shape is reused for both domains so a single "Activity" view can
7565
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7566
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7567
+ * management operation, WHY it ran (reason), and its measurable effect
7568
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7569
+ * never fail the operation it records.
7570
+ */
7571
+ /** Which management domain the operation belongs to. */
7572
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7573
+ /** The kind of management operation performed. */
7574
+ var OpsLogOpSchema = _enum([
7575
+ "prune",
7576
+ "manual-delete",
7577
+ "rescan",
7578
+ "retention-run"
7579
+ ]);
7580
+ /** Why the operation ran. */
7581
+ var OpsLogReasonSchema = _enum([
7582
+ "retention",
7583
+ "quota",
7584
+ "manual",
7585
+ "operator"
7586
+ ]);
7587
+ /** One audit row, shared verbatim by both domains. */
7588
+ var OpsLogEntrySchema = object({
7589
+ /** Unique row id. */
7590
+ id: string(),
7591
+ /** Epoch ms the operation completed. */
7592
+ at: number(),
7593
+ domain: OpsLogDomainSchema,
7594
+ op: OpsLogOpSchema,
7595
+ reason: OpsLogReasonSchema,
7596
+ /** The camera the op targeted; null for a cluster/global op. */
7597
+ deviceId: number().nullable(),
7598
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7599
+ nodeId: string(),
7600
+ /** Buckets / rows deleted (op-specific unit). */
7601
+ itemsAffected: number(),
7602
+ /** Bytes reclaimed by the op (0 when not measurable). */
7603
+ bytesReclaimed: number(),
7604
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7605
+ detail: string().nullable(),
7606
+ /** Who/what triggered the op. */
7607
+ actor: string()
7608
+ });
7609
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7610
+ var OpsLogQueryInputSchema = object({
7611
+ /** Restrict to a single camera; omit for every row. */
7612
+ deviceId: number().optional(),
7613
+ /** Max rows returned, newest-first. */
7614
+ limit: number().int().min(1).max(1e3).optional()
7615
+ });
7616
+ /**
7518
7617
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7519
7618
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7520
7619
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14909,7 +15008,11 @@ array(ZoneRuleSchema).readonly();
14909
15008
  * Extend the enum here when a new gating consumer comes online (audio
14910
15009
  * gating, alert filtering, …) — no other surface needs to change.
14911
15010
  */
14912
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
15011
+ var ZoneRuleStageEnum = _enum([
15012
+ "motion",
15013
+ "detection",
15014
+ "package"
15015
+ ]);
14913
15016
  /**
14914
15017
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14915
15018
  * `state` getter looks up the cap definition here to construct a
@@ -15003,16 +15106,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
15003
15106
  })
15004
15107
  },
15005
15108
  /**
15006
- * Runtime-state slice — both stages mirrored together so consumers
15109
+ * Runtime-state slice — every stage mirrored together so consumers
15007
15110
  * see one reactive handle (`device.state.zoneRules.value`) instead
15008
- * of two. Bulk-replace mutations on either stage write the full
15009
- * `{motion, detection}` shape, so subscribers always get the
15111
+ * of one per stage. Bulk-replace mutations on any stage write the full
15112
+ * `{motion, detection, package}` shape, so subscribers always get the
15010
15113
  * complete current set. Consumers that only care about one stage
15011
15114
  * just read the matching property.
15115
+ *
15116
+ * `package` backs the package-drop detector — a package zone is a
15117
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
15118
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
15119
+ * The orchestrator provider writes this stage as a first-class slice
15120
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
15121
+ * package}` shape, so consumers read the current package rules directly
15122
+ * off `device.state.zoneRules.value.package`.
15012
15123
  */
15013
15124
  runtimeState: object({
15014
15125
  motion: array(ZoneRuleSchema).readonly(),
15015
- detection: array(ZoneRuleSchema).readonly()
15126
+ detection: array(ZoneRuleSchema).readonly(),
15127
+ package: array(ZoneRuleSchema).readonly()
15016
15128
  })
15017
15129
  },
15018
15130
  zones: zonesCapability
@@ -18986,6 +19098,7 @@ var EventKindIconSchema = _enum([
18986
19098
  "smoke",
18987
19099
  "water",
18988
19100
  "button",
19101
+ "package",
18989
19102
  "generic"
18990
19103
  ]);
18991
19104
  var EventKindCategorySchema = _enum([
@@ -18993,7 +19106,8 @@ var EventKindCategorySchema = _enum([
18993
19106
  "audio",
18994
19107
  "detection",
18995
19108
  "sensor",
18996
- "custom"
19109
+ "custom",
19110
+ "package"
18997
19111
  ]);
18998
19112
  var EventKindDescriptorSchema = object({
18999
19113
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19334,6 +19448,26 @@ var TrackCascadeCountsSchema = object({
19334
19448
  /** Per-track CLIP search vectors removed (best-effort). */
19335
19449
  embeddings: number().int()
19336
19450
  });
19451
+ /** Event-store footprint for one camera. */
19452
+ var EventStoreDeviceFootprintSchema = object({
19453
+ deviceId: number(),
19454
+ /** Persisted event rows (motion + object + audio) for the camera. */
19455
+ rows: number().int(),
19456
+ /** Event-owned media bytes on disk for the camera. */
19457
+ bytes: number().int()
19458
+ });
19459
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19460
+ var EventStoreFootprintSchema = object({
19461
+ totalRows: number().int(),
19462
+ totalBytes: number().int(),
19463
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19464
+ });
19465
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19466
+ var EventPruneCountsSchema = object({
19467
+ motion: number().int(),
19468
+ object: number().int(),
19469
+ audio: number().int()
19470
+ });
19337
19471
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19338
19472
  deviceId: number(),
19339
19473
  trackId: string()
@@ -19397,6 +19531,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19397
19531
  }), {
19398
19532
  kind: "mutation",
19399
19533
  auth: "admin"
19534
+ }), method(object({}), EventStoreFootprintSchema, {
19535
+ kind: "query",
19536
+ auth: "admin"
19537
+ }), method(object({
19538
+ olderThanMs: number(),
19539
+ reason: OpsLogReasonSchema.optional()
19540
+ }), EventPruneCountsSchema, {
19541
+ kind: "mutation",
19542
+ auth: "admin"
19543
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19544
+ kind: "mutation",
19545
+ auth: "admin"
19546
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19547
+ kind: "query",
19548
+ auth: "admin"
19400
19549
  }), method(object({
19401
19550
  eventId: string(),
19402
19551
  kind: MediaFileKindEnum.optional()
@@ -22799,13 +22948,131 @@ method(object({
22799
22948
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22800
22949
  kind: "mutation",
22801
22950
  auth: "admin"
22802
- }), method(object({ deviceId: number() }), object({
22951
+ }), method(object({
22952
+ deviceId: number(),
22953
+ reason: OpsLogReasonSchema.optional()
22954
+ }), object({
22803
22955
  floorMs: number().nullable(),
22804
22956
  deletedBuckets: number().int(),
22805
22957
  reclaimedBytes: number().int()
22806
22958
  }), {
22807
22959
  kind: "mutation",
22808
22960
  auth: "admin"
22961
+ }), method(object({
22962
+ deviceId: number(),
22963
+ fromMs: number().optional(),
22964
+ toMs: number().optional()
22965
+ }), object({
22966
+ deletedBuckets: number().int(),
22967
+ reclaimedBytes: number().int()
22968
+ }), {
22969
+ kind: "mutation",
22970
+ auth: "admin"
22971
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22972
+ kind: "query",
22973
+ auth: "admin"
22974
+ });
22975
+ /**
22976
+ * `recordingExport` cap — render a footage time range into a single downloadable
22977
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22978
+ * bounded lifetime with a durable history, auto-expiry, and optional
22979
+ * delete-after-download.
22980
+ *
22981
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22982
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22983
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22984
+ * input and dispatch to that single provider; the render runs on the node that
22985
+ * owns the footage (no cross-node segment transfer). Download rides the
22986
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22987
+ * SQLite); history rows survive file deletion for audit.
22988
+ */
22989
+ /** Playback-speed multiplier for the render (1 = realtime). */
22990
+ var ExportSpeedSchema = number().min(.25).max(32);
22991
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22992
+ var ExportTimelapseSchema = object({
22993
+ everyMs: number().int().positive(),
22994
+ outputFps: number().int().min(1).max(60).optional()
22995
+ });
22996
+ /**
22997
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22998
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22999
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
23000
+ * `deleteAfterDownload` removes it shortly after the first complete download.
23001
+ */
23002
+ var ExportOptionsSchema = object({
23003
+ speed: ExportSpeedSchema.optional(),
23004
+ timelapse: ExportTimelapseSchema.optional(),
23005
+ includeAudio: boolean(),
23006
+ maxLifeMs: number().int().positive(),
23007
+ deleteAfterDownload: boolean(),
23008
+ title: string().max(200).optional()
23009
+ }).superRefine((v, ctx) => {
23010
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
23011
+ code: ZodIssueCode$16.custom,
23012
+ message: "speed and timelapse are mutually exclusive",
23013
+ path: ["timelapse"]
23014
+ });
23015
+ });
23016
+ var ExportStateSchema = _enum([
23017
+ "queued",
23018
+ "rendering",
23019
+ "ready",
23020
+ "failed",
23021
+ "expired",
23022
+ "deleted"
23023
+ ]);
23024
+ /** One export job / history row. */
23025
+ var ExportRecordSchema = object({
23026
+ id: string(),
23027
+ deviceId: number(),
23028
+ profile: string(),
23029
+ fromMs: number(),
23030
+ toMs: number(),
23031
+ options: ExportOptionsSchema,
23032
+ state: ExportStateSchema,
23033
+ /** 0–100 while rendering; null otherwise. */
23034
+ progressPct: number().nullable(),
23035
+ /** File size once ready; null before. */
23036
+ fileBytes: number().nullable(),
23037
+ expiresAt: number(),
23038
+ deleteAfterDownload: boolean(),
23039
+ /** Epoch of the first complete download; null until then. */
23040
+ downloadedAt: number().nullable(),
23041
+ createdAt: number(),
23042
+ /** User id/name that requested the export. */
23043
+ createdBy: string(),
23044
+ /** Failure reason when state is 'failed'; null otherwise. */
23045
+ error: string().nullable()
23046
+ });
23047
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
23048
+ var ExportDownloadSchema = object({
23049
+ url: string(),
23050
+ endpoints: array(string())
23051
+ });
23052
+ method(object({
23053
+ deviceId: number(),
23054
+ profile: string(),
23055
+ fromMs: number(),
23056
+ toMs: number(),
23057
+ options: ExportOptionsSchema
23058
+ }), ExportRecordSchema, {
23059
+ kind: "mutation",
23060
+ auth: "protected"
23061
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
23062
+ kind: "query",
23063
+ auth: "protected"
23064
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23065
+ kind: "query",
23066
+ auth: "protected"
23067
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23068
+ kind: "mutation",
23069
+ auth: "protected"
23070
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23071
+ kind: "mutation",
23072
+ auth: "protected"
23073
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
23074
+ kind: "query",
23075
+ auth: "protected"
22809
23076
  });
22810
23077
  /**
22811
23078
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -26036,6 +26303,12 @@ Object.freeze({
26036
26303
  addonId: null,
26037
26304
  access: "delete"
26038
26305
  },
26306
+ "pipelineAnalytics.deleteDeviceEvents": {
26307
+ capName: "pipeline-analytics",
26308
+ capScope: "device",
26309
+ addonId: null,
26310
+ access: "delete"
26311
+ },
26039
26312
  "pipelineAnalytics.deleteTracks": {
26040
26313
  capName: "pipeline-analytics",
26041
26314
  capScope: "device",
@@ -26066,6 +26339,12 @@ Object.freeze({
26066
26339
  addonId: null,
26067
26340
  access: "view"
26068
26341
  },
26342
+ "pipelineAnalytics.getEventStoreFootprint": {
26343
+ capName: "pipeline-analytics",
26344
+ capScope: "device",
26345
+ addonId: null,
26346
+ access: "view"
26347
+ },
26069
26348
  "pipelineAnalytics.getKeyEvents": {
26070
26349
  capName: "pipeline-analytics",
26071
26350
  capScope: "device",
@@ -26108,6 +26387,12 @@ Object.freeze({
26108
26387
  addonId: null,
26109
26388
  access: "view"
26110
26389
  },
26390
+ "pipelineAnalytics.listOpsLog": {
26391
+ capName: "pipeline-analytics",
26392
+ capScope: "device",
26393
+ addonId: null,
26394
+ access: "view"
26395
+ },
26111
26396
  "pipelineAnalytics.listRecentTracks": {
26112
26397
  capName: "pipeline-analytics",
26113
26398
  capScope: "device",
@@ -26120,6 +26405,12 @@ Object.freeze({
26120
26405
  addonId: null,
26121
26406
  access: "view"
26122
26407
  },
26408
+ "pipelineAnalytics.pruneEvents": {
26409
+ capName: "pipeline-analytics",
26410
+ capScope: "device",
26411
+ addonId: null,
26412
+ access: "create"
26413
+ },
26123
26414
  "pipelineAnalytics.pruneEventsBefore": {
26124
26415
  capName: "pipeline-analytics",
26125
26416
  capScope: "device",
@@ -26882,6 +27173,12 @@ Object.freeze({
26882
27173
  addonId: null,
26883
27174
  access: "create"
26884
27175
  },
27176
+ "recording.deleteFootprint": {
27177
+ capName: "recording",
27178
+ capScope: "system",
27179
+ addonId: null,
27180
+ access: "delete"
27181
+ },
26885
27182
  "recording.getAvailability": {
26886
27183
  capName: "recording",
26887
27184
  capScope: "system",
@@ -26912,6 +27209,12 @@ Object.freeze({
26912
27209
  addonId: null,
26913
27210
  access: "view"
26914
27211
  },
27212
+ "recording.listOpsLog": {
27213
+ capName: "recording",
27214
+ capScope: "system",
27215
+ addonId: null,
27216
+ access: "view"
27217
+ },
26915
27218
  "recording.locateSegment": {
26916
27219
  capName: "recording",
26917
27220
  capScope: "system",
@@ -26942,6 +27245,42 @@ Object.freeze({
26942
27245
  addonId: null,
26943
27246
  access: "create"
26944
27247
  },
27248
+ "recordingExport.cancelExport": {
27249
+ capName: "recordingExport",
27250
+ capScope: "system",
27251
+ addonId: null,
27252
+ access: "create"
27253
+ },
27254
+ "recordingExport.createExport": {
27255
+ capName: "recordingExport",
27256
+ capScope: "system",
27257
+ addonId: null,
27258
+ access: "create"
27259
+ },
27260
+ "recordingExport.deleteExport": {
27261
+ capName: "recordingExport",
27262
+ capScope: "system",
27263
+ addonId: null,
27264
+ access: "delete"
27265
+ },
27266
+ "recordingExport.getDownloadUrl": {
27267
+ capName: "recordingExport",
27268
+ capScope: "system",
27269
+ addonId: null,
27270
+ access: "view"
27271
+ },
27272
+ "recordingExport.getExport": {
27273
+ capName: "recordingExport",
27274
+ capScope: "system",
27275
+ addonId: null,
27276
+ access: "view"
27277
+ },
27278
+ "recordingExport.listExports": {
27279
+ capName: "recordingExport",
27280
+ capScope: "system",
27281
+ addonId: null,
27282
+ access: "view"
27283
+ },
26945
27284
  "sceneMonitor.captureReference": {
26946
27285
  capName: "scene-monitor",
26947
27286
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -21,7 +21,7 @@ import netImpl from "net";
21
21
  import { fileURLToPath } from "url";
22
22
  import { mkdir } from "fs/promises";
23
23
  import os from "node:os";
24
- //#region ../types/dist/event-category-H4AVePnn.mjs
24
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
25
25
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
26
26
  EventCategory["SystemBoot"] = "system.boot";
27
27
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -174,6 +174,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
174
174
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
175
175
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
176
176
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
177
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
178
+ * progress bar the client reconciles via `recordingExport.getExport`. */
179
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
180
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
181
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
177
182
  EventCategory["DetectionEvent"] = "detection.event";
178
183
  EventCategory["SessionTrackNew"] = "session.track.new";
179
184
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -463,6 +468,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
463
468
  */
464
469
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
465
470
  /**
471
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
472
+ * a package zone — a newly-appeared stationary object of a package class
473
+ * that cleared the class / zone / dwell / size gates. Payload:
474
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
475
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
476
+ * Telemetry (D8): the durable record is the `package-events` store row;
477
+ * this bus topic drives notifier rules + live UI. See
478
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
479
+ */
480
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
481
+ /**
482
+ * Fired by `addon-post-analysis` when a previously-delivered package
483
+ * leaves its zone (the stationary entry departed — moved or swept).
484
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
485
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
486
+ * Telemetry (D8). See package-zones-design §5.2.
487
+ */
488
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
489
+ /**
466
490
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
467
491
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
468
492
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5201,6 +5225,25 @@ function _instanceof(cls, params = {}) {
5201
5225
  };
5202
5226
  return inst;
5203
5227
  }
5228
+ //#endregion
5229
+ //#region ../../node_modules/zod/v4/classic/compat.js
5230
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5231
+ var ZodIssueCode$16 = {
5232
+ invalid_type: "invalid_type",
5233
+ too_big: "too_big",
5234
+ too_small: "too_small",
5235
+ invalid_format: "invalid_format",
5236
+ not_multiple_of: "not_multiple_of",
5237
+ unrecognized_keys: "unrecognized_keys",
5238
+ invalid_union: "invalid_union",
5239
+ invalid_key: "invalid_key",
5240
+ invalid_element: "invalid_element",
5241
+ invalid_value: "invalid_value",
5242
+ custom: "custom"
5243
+ };
5244
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5245
+ var ZodFirstPartyTypeKind$16;
5246
+ ZodFirstPartyTypeKind$16 || (ZodFirstPartyTypeKind$16 = {});
5204
5247
  Object.fromEntries([
5205
5248
  {
5206
5249
  id: "overview",
@@ -7510,6 +7553,62 @@ var RecordingConfigSchema = object({
7510
7553
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7511
7554
  });
7512
7555
  /**
7556
+ * Ops-log — the durable, append-only operations audit shared by the
7557
+ * recordings and events management surfaces.
7558
+ *
7559
+ * ONE row shape is reused for both domains so a single "Activity" view can
7560
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7561
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7562
+ * management operation, WHY it ran (reason), and its measurable effect
7563
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7564
+ * never fail the operation it records.
7565
+ */
7566
+ /** Which management domain the operation belongs to. */
7567
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7568
+ /** The kind of management operation performed. */
7569
+ var OpsLogOpSchema = _enum([
7570
+ "prune",
7571
+ "manual-delete",
7572
+ "rescan",
7573
+ "retention-run"
7574
+ ]);
7575
+ /** Why the operation ran. */
7576
+ var OpsLogReasonSchema = _enum([
7577
+ "retention",
7578
+ "quota",
7579
+ "manual",
7580
+ "operator"
7581
+ ]);
7582
+ /** One audit row, shared verbatim by both domains. */
7583
+ var OpsLogEntrySchema = object({
7584
+ /** Unique row id. */
7585
+ id: string(),
7586
+ /** Epoch ms the operation completed. */
7587
+ at: number(),
7588
+ domain: OpsLogDomainSchema,
7589
+ op: OpsLogOpSchema,
7590
+ reason: OpsLogReasonSchema,
7591
+ /** The camera the op targeted; null for a cluster/global op. */
7592
+ deviceId: number().nullable(),
7593
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7594
+ nodeId: string(),
7595
+ /** Buckets / rows deleted (op-specific unit). */
7596
+ itemsAffected: number(),
7597
+ /** Bytes reclaimed by the op (0 when not measurable). */
7598
+ bytesReclaimed: number(),
7599
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7600
+ detail: string().nullable(),
7601
+ /** Who/what triggered the op. */
7602
+ actor: string()
7603
+ });
7604
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7605
+ var OpsLogQueryInputSchema = object({
7606
+ /** Restrict to a single camera; omit for every row. */
7607
+ deviceId: number().optional(),
7608
+ /** Max rows returned, newest-first. */
7609
+ limit: number().int().min(1).max(1e3).optional()
7610
+ });
7611
+ /**
7513
7612
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7514
7613
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7515
7614
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14904,7 +15003,11 @@ array(ZoneRuleSchema).readonly();
14904
15003
  * Extend the enum here when a new gating consumer comes online (audio
14905
15004
  * gating, alert filtering, …) — no other surface needs to change.
14906
15005
  */
14907
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
15006
+ var ZoneRuleStageEnum = _enum([
15007
+ "motion",
15008
+ "detection",
15009
+ "package"
15010
+ ]);
14908
15011
  /**
14909
15012
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14910
15013
  * `state` getter looks up the cap definition here to construct a
@@ -14998,16 +15101,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14998
15101
  })
14999
15102
  },
15000
15103
  /**
15001
- * Runtime-state slice — both stages mirrored together so consumers
15104
+ * Runtime-state slice — every stage mirrored together so consumers
15002
15105
  * see one reactive handle (`device.state.zoneRules.value`) instead
15003
- * of two. Bulk-replace mutations on either stage write the full
15004
- * `{motion, detection}` shape, so subscribers always get the
15106
+ * of one per stage. Bulk-replace mutations on any stage write the full
15107
+ * `{motion, detection, package}` shape, so subscribers always get the
15005
15108
  * complete current set. Consumers that only care about one stage
15006
15109
  * just read the matching property.
15110
+ *
15111
+ * `package` backs the package-drop detector — a package zone is a
15112
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
15113
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
15114
+ * The orchestrator provider writes this stage as a first-class slice
15115
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
15116
+ * package}` shape, so consumers read the current package rules directly
15117
+ * off `device.state.zoneRules.value.package`.
15007
15118
  */
15008
15119
  runtimeState: object({
15009
15120
  motion: array(ZoneRuleSchema).readonly(),
15010
- detection: array(ZoneRuleSchema).readonly()
15121
+ detection: array(ZoneRuleSchema).readonly(),
15122
+ package: array(ZoneRuleSchema).readonly()
15011
15123
  })
15012
15124
  },
15013
15125
  zones: zonesCapability
@@ -18981,6 +19093,7 @@ var EventKindIconSchema = _enum([
18981
19093
  "smoke",
18982
19094
  "water",
18983
19095
  "button",
19096
+ "package",
18984
19097
  "generic"
18985
19098
  ]);
18986
19099
  var EventKindCategorySchema = _enum([
@@ -18988,7 +19101,8 @@ var EventKindCategorySchema = _enum([
18988
19101
  "audio",
18989
19102
  "detection",
18990
19103
  "sensor",
18991
- "custom"
19104
+ "custom",
19105
+ "package"
18992
19106
  ]);
18993
19107
  var EventKindDescriptorSchema = object({
18994
19108
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19329,6 +19443,26 @@ var TrackCascadeCountsSchema = object({
19329
19443
  /** Per-track CLIP search vectors removed (best-effort). */
19330
19444
  embeddings: number().int()
19331
19445
  });
19446
+ /** Event-store footprint for one camera. */
19447
+ var EventStoreDeviceFootprintSchema = object({
19448
+ deviceId: number(),
19449
+ /** Persisted event rows (motion + object + audio) for the camera. */
19450
+ rows: number().int(),
19451
+ /** Event-owned media bytes on disk for the camera. */
19452
+ bytes: number().int()
19453
+ });
19454
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19455
+ var EventStoreFootprintSchema = object({
19456
+ totalRows: number().int(),
19457
+ totalBytes: number().int(),
19458
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19459
+ });
19460
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19461
+ var EventPruneCountsSchema = object({
19462
+ motion: number().int(),
19463
+ object: number().int(),
19464
+ audio: number().int()
19465
+ });
19332
19466
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19333
19467
  deviceId: number(),
19334
19468
  trackId: string()
@@ -19392,6 +19526,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19392
19526
  }), {
19393
19527
  kind: "mutation",
19394
19528
  auth: "admin"
19529
+ }), method(object({}), EventStoreFootprintSchema, {
19530
+ kind: "query",
19531
+ auth: "admin"
19532
+ }), method(object({
19533
+ olderThanMs: number(),
19534
+ reason: OpsLogReasonSchema.optional()
19535
+ }), EventPruneCountsSchema, {
19536
+ kind: "mutation",
19537
+ auth: "admin"
19538
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19539
+ kind: "mutation",
19540
+ auth: "admin"
19541
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19542
+ kind: "query",
19543
+ auth: "admin"
19395
19544
  }), method(object({
19396
19545
  eventId: string(),
19397
19546
  kind: MediaFileKindEnum.optional()
@@ -22794,13 +22943,131 @@ method(object({
22794
22943
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22795
22944
  kind: "mutation",
22796
22945
  auth: "admin"
22797
- }), method(object({ deviceId: number() }), object({
22946
+ }), method(object({
22947
+ deviceId: number(),
22948
+ reason: OpsLogReasonSchema.optional()
22949
+ }), object({
22798
22950
  floorMs: number().nullable(),
22799
22951
  deletedBuckets: number().int(),
22800
22952
  reclaimedBytes: number().int()
22801
22953
  }), {
22802
22954
  kind: "mutation",
22803
22955
  auth: "admin"
22956
+ }), method(object({
22957
+ deviceId: number(),
22958
+ fromMs: number().optional(),
22959
+ toMs: number().optional()
22960
+ }), object({
22961
+ deletedBuckets: number().int(),
22962
+ reclaimedBytes: number().int()
22963
+ }), {
22964
+ kind: "mutation",
22965
+ auth: "admin"
22966
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22967
+ kind: "query",
22968
+ auth: "admin"
22969
+ });
22970
+ /**
22971
+ * `recordingExport` cap — render a footage time range into a single downloadable
22972
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22973
+ * bounded lifetime with a durable history, auto-expiry, and optional
22974
+ * delete-after-download.
22975
+ *
22976
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22977
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22978
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22979
+ * input and dispatch to that single provider; the render runs on the node that
22980
+ * owns the footage (no cross-node segment transfer). Download rides the
22981
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22982
+ * SQLite); history rows survive file deletion for audit.
22983
+ */
22984
+ /** Playback-speed multiplier for the render (1 = realtime). */
22985
+ var ExportSpeedSchema = number().min(.25).max(32);
22986
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22987
+ var ExportTimelapseSchema = object({
22988
+ everyMs: number().int().positive(),
22989
+ outputFps: number().int().min(1).max(60).optional()
22990
+ });
22991
+ /**
22992
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22993
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22994
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22995
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22996
+ */
22997
+ var ExportOptionsSchema = object({
22998
+ speed: ExportSpeedSchema.optional(),
22999
+ timelapse: ExportTimelapseSchema.optional(),
23000
+ includeAudio: boolean(),
23001
+ maxLifeMs: number().int().positive(),
23002
+ deleteAfterDownload: boolean(),
23003
+ title: string().max(200).optional()
23004
+ }).superRefine((v, ctx) => {
23005
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
23006
+ code: ZodIssueCode$16.custom,
23007
+ message: "speed and timelapse are mutually exclusive",
23008
+ path: ["timelapse"]
23009
+ });
23010
+ });
23011
+ var ExportStateSchema = _enum([
23012
+ "queued",
23013
+ "rendering",
23014
+ "ready",
23015
+ "failed",
23016
+ "expired",
23017
+ "deleted"
23018
+ ]);
23019
+ /** One export job / history row. */
23020
+ var ExportRecordSchema = object({
23021
+ id: string(),
23022
+ deviceId: number(),
23023
+ profile: string(),
23024
+ fromMs: number(),
23025
+ toMs: number(),
23026
+ options: ExportOptionsSchema,
23027
+ state: ExportStateSchema,
23028
+ /** 0–100 while rendering; null otherwise. */
23029
+ progressPct: number().nullable(),
23030
+ /** File size once ready; null before. */
23031
+ fileBytes: number().nullable(),
23032
+ expiresAt: number(),
23033
+ deleteAfterDownload: boolean(),
23034
+ /** Epoch of the first complete download; null until then. */
23035
+ downloadedAt: number().nullable(),
23036
+ createdAt: number(),
23037
+ /** User id/name that requested the export. */
23038
+ createdBy: string(),
23039
+ /** Failure reason when state is 'failed'; null otherwise. */
23040
+ error: string().nullable()
23041
+ });
23042
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
23043
+ var ExportDownloadSchema = object({
23044
+ url: string(),
23045
+ endpoints: array(string())
23046
+ });
23047
+ method(object({
23048
+ deviceId: number(),
23049
+ profile: string(),
23050
+ fromMs: number(),
23051
+ toMs: number(),
23052
+ options: ExportOptionsSchema
23053
+ }), ExportRecordSchema, {
23054
+ kind: "mutation",
23055
+ auth: "protected"
23056
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
23057
+ kind: "query",
23058
+ auth: "protected"
23059
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23060
+ kind: "query",
23061
+ auth: "protected"
23062
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23063
+ kind: "mutation",
23064
+ auth: "protected"
23065
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23066
+ kind: "mutation",
23067
+ auth: "protected"
23068
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
23069
+ kind: "query",
23070
+ auth: "protected"
22804
23071
  });
22805
23072
  /**
22806
23073
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -26031,6 +26298,12 @@ Object.freeze({
26031
26298
  addonId: null,
26032
26299
  access: "delete"
26033
26300
  },
26301
+ "pipelineAnalytics.deleteDeviceEvents": {
26302
+ capName: "pipeline-analytics",
26303
+ capScope: "device",
26304
+ addonId: null,
26305
+ access: "delete"
26306
+ },
26034
26307
  "pipelineAnalytics.deleteTracks": {
26035
26308
  capName: "pipeline-analytics",
26036
26309
  capScope: "device",
@@ -26061,6 +26334,12 @@ Object.freeze({
26061
26334
  addonId: null,
26062
26335
  access: "view"
26063
26336
  },
26337
+ "pipelineAnalytics.getEventStoreFootprint": {
26338
+ capName: "pipeline-analytics",
26339
+ capScope: "device",
26340
+ addonId: null,
26341
+ access: "view"
26342
+ },
26064
26343
  "pipelineAnalytics.getKeyEvents": {
26065
26344
  capName: "pipeline-analytics",
26066
26345
  capScope: "device",
@@ -26103,6 +26382,12 @@ Object.freeze({
26103
26382
  addonId: null,
26104
26383
  access: "view"
26105
26384
  },
26385
+ "pipelineAnalytics.listOpsLog": {
26386
+ capName: "pipeline-analytics",
26387
+ capScope: "device",
26388
+ addonId: null,
26389
+ access: "view"
26390
+ },
26106
26391
  "pipelineAnalytics.listRecentTracks": {
26107
26392
  capName: "pipeline-analytics",
26108
26393
  capScope: "device",
@@ -26115,6 +26400,12 @@ Object.freeze({
26115
26400
  addonId: null,
26116
26401
  access: "view"
26117
26402
  },
26403
+ "pipelineAnalytics.pruneEvents": {
26404
+ capName: "pipeline-analytics",
26405
+ capScope: "device",
26406
+ addonId: null,
26407
+ access: "create"
26408
+ },
26118
26409
  "pipelineAnalytics.pruneEventsBefore": {
26119
26410
  capName: "pipeline-analytics",
26120
26411
  capScope: "device",
@@ -26877,6 +27168,12 @@ Object.freeze({
26877
27168
  addonId: null,
26878
27169
  access: "create"
26879
27170
  },
27171
+ "recording.deleteFootprint": {
27172
+ capName: "recording",
27173
+ capScope: "system",
27174
+ addonId: null,
27175
+ access: "delete"
27176
+ },
26880
27177
  "recording.getAvailability": {
26881
27178
  capName: "recording",
26882
27179
  capScope: "system",
@@ -26907,6 +27204,12 @@ Object.freeze({
26907
27204
  addonId: null,
26908
27205
  access: "view"
26909
27206
  },
27207
+ "recording.listOpsLog": {
27208
+ capName: "recording",
27209
+ capScope: "system",
27210
+ addonId: null,
27211
+ access: "view"
27212
+ },
26910
27213
  "recording.locateSegment": {
26911
27214
  capName: "recording",
26912
27215
  capScope: "system",
@@ -26937,6 +27240,42 @@ Object.freeze({
26937
27240
  addonId: null,
26938
27241
  access: "create"
26939
27242
  },
27243
+ "recordingExport.cancelExport": {
27244
+ capName: "recordingExport",
27245
+ capScope: "system",
27246
+ addonId: null,
27247
+ access: "create"
27248
+ },
27249
+ "recordingExport.createExport": {
27250
+ capName: "recordingExport",
27251
+ capScope: "system",
27252
+ addonId: null,
27253
+ access: "create"
27254
+ },
27255
+ "recordingExport.deleteExport": {
27256
+ capName: "recordingExport",
27257
+ capScope: "system",
27258
+ addonId: null,
27259
+ access: "delete"
27260
+ },
27261
+ "recordingExport.getDownloadUrl": {
27262
+ capName: "recordingExport",
27263
+ capScope: "system",
27264
+ addonId: null,
27265
+ access: "view"
27266
+ },
27267
+ "recordingExport.getExport": {
27268
+ capName: "recordingExport",
27269
+ capScope: "system",
27270
+ addonId: null,
27271
+ access: "view"
27272
+ },
27273
+ "recordingExport.listExports": {
27274
+ capName: "recordingExport",
27275
+ capScope: "system",
27276
+ addonId: null,
27277
+ access: "view"
27278
+ },
26940
27279
  "sceneMonitor.captureReference": {
26941
27280
  capName: "scene-monitor",
26942
27281
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.1.27",
3
+ "version": "1.1.29",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",