@camstack/addon-provider-dreo 0.1.20 → 0.1.22

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
@@ -35,7 +35,7 @@ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["modu
35
35
  //#endregion
36
36
  let crypto$1 = require("crypto");
37
37
  let events = require("events");
38
- //#region ../types/dist/event-category-H4AVePnn.mjs
38
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
39
39
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
40
  EventCategory["SystemBoot"] = "system.boot";
41
41
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -188,6 +188,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
188
188
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
189
189
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
190
190
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
191
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
192
+ * progress bar the client reconciles via `recordingExport.getExport`. */
193
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
194
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
195
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
191
196
  EventCategory["DetectionEvent"] = "detection.event";
192
197
  EventCategory["SessionTrackNew"] = "session.track.new";
193
198
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -477,6 +482,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
477
482
  */
478
483
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
479
484
  /**
485
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
486
+ * a package zone — a newly-appeared stationary object of a package class
487
+ * that cleared the class / zone / dwell / size gates. Payload:
488
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
489
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
490
+ * Telemetry (D8): the durable record is the `package-events` store row;
491
+ * this bus topic drives notifier rules + live UI. See
492
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
493
+ */
494
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
495
+ /**
496
+ * Fired by `addon-post-analysis` when a previously-delivered package
497
+ * leaves its zone (the stationary entry departed — moved or swept).
498
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
499
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
500
+ * Telemetry (D8). See package-zones-design §5.2.
501
+ */
502
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
503
+ /**
480
504
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
481
505
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
482
506
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5215,6 +5239,25 @@ function _instanceof(cls, params = {}) {
5215
5239
  };
5216
5240
  return inst;
5217
5241
  }
5242
+ //#endregion
5243
+ //#region ../../node_modules/zod/v4/classic/compat.js
5244
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5245
+ var ZodIssueCode = {
5246
+ invalid_type: "invalid_type",
5247
+ too_big: "too_big",
5248
+ too_small: "too_small",
5249
+ invalid_format: "invalid_format",
5250
+ not_multiple_of: "not_multiple_of",
5251
+ unrecognized_keys: "unrecognized_keys",
5252
+ invalid_union: "invalid_union",
5253
+ invalid_key: "invalid_key",
5254
+ invalid_element: "invalid_element",
5255
+ invalid_value: "invalid_value",
5256
+ custom: "custom"
5257
+ };
5258
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5259
+ var ZodFirstPartyTypeKind;
5260
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5218
5261
  Object.fromEntries([
5219
5262
  {
5220
5263
  id: "overview",
@@ -7337,6 +7380,62 @@ var RecordingConfigSchema = object({
7337
7380
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7338
7381
  });
7339
7382
  /**
7383
+ * Ops-log — the durable, append-only operations audit shared by the
7384
+ * recordings and events management surfaces.
7385
+ *
7386
+ * ONE row shape is reused for both domains so a single "Activity" view can
7387
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7388
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7389
+ * management operation, WHY it ran (reason), and its measurable effect
7390
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7391
+ * never fail the operation it records.
7392
+ */
7393
+ /** Which management domain the operation belongs to. */
7394
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7395
+ /** The kind of management operation performed. */
7396
+ var OpsLogOpSchema = _enum([
7397
+ "prune",
7398
+ "manual-delete",
7399
+ "rescan",
7400
+ "retention-run"
7401
+ ]);
7402
+ /** Why the operation ran. */
7403
+ var OpsLogReasonSchema = _enum([
7404
+ "retention",
7405
+ "quota",
7406
+ "manual",
7407
+ "operator"
7408
+ ]);
7409
+ /** One audit row, shared verbatim by both domains. */
7410
+ var OpsLogEntrySchema = object({
7411
+ /** Unique row id. */
7412
+ id: string(),
7413
+ /** Epoch ms the operation completed. */
7414
+ at: number(),
7415
+ domain: OpsLogDomainSchema,
7416
+ op: OpsLogOpSchema,
7417
+ reason: OpsLogReasonSchema,
7418
+ /** The camera the op targeted; null for a cluster/global op. */
7419
+ deviceId: number().nullable(),
7420
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7421
+ nodeId: string(),
7422
+ /** Buckets / rows deleted (op-specific unit). */
7423
+ itemsAffected: number(),
7424
+ /** Bytes reclaimed by the op (0 when not measurable). */
7425
+ bytesReclaimed: number(),
7426
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7427
+ detail: string().nullable(),
7428
+ /** Who/what triggered the op. */
7429
+ actor: string()
7430
+ });
7431
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7432
+ var OpsLogQueryInputSchema = object({
7433
+ /** Restrict to a single camera; omit for every row. */
7434
+ deviceId: number().optional(),
7435
+ /** Max rows returned, newest-first. */
7436
+ limit: number().int().min(1).max(1e3).optional()
7437
+ });
7438
+ /**
7340
7439
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7341
7440
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7342
7441
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14692,7 +14791,11 @@ array(ZoneRuleSchema).readonly();
14692
14791
  * Extend the enum here when a new gating consumer comes online (audio
14693
14792
  * gating, alert filtering, …) — no other surface needs to change.
14694
14793
  */
14695
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14794
+ var ZoneRuleStageEnum = _enum([
14795
+ "motion",
14796
+ "detection",
14797
+ "package"
14798
+ ]);
14696
14799
  /**
14697
14800
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14698
14801
  * `state` getter looks up the cap definition here to construct a
@@ -14786,16 +14889,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14786
14889
  })
14787
14890
  },
14788
14891
  /**
14789
- * Runtime-state slice — both stages mirrored together so consumers
14892
+ * Runtime-state slice — every stage mirrored together so consumers
14790
14893
  * see one reactive handle (`device.state.zoneRules.value`) instead
14791
- * of two. Bulk-replace mutations on either stage write the full
14792
- * `{motion, detection}` shape, so subscribers always get the
14894
+ * of one per stage. Bulk-replace mutations on any stage write the full
14895
+ * `{motion, detection, package}` shape, so subscribers always get the
14793
14896
  * complete current set. Consumers that only care about one stage
14794
14897
  * just read the matching property.
14898
+ *
14899
+ * `package` backs the package-drop detector — a package zone is a
14900
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14901
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14902
+ * The orchestrator provider writes this stage as a first-class slice
14903
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14904
+ * package}` shape, so consumers read the current package rules directly
14905
+ * off `device.state.zoneRules.value.package`.
14795
14906
  */
14796
14907
  runtimeState: object({
14797
14908
  motion: array(ZoneRuleSchema).readonly(),
14798
- detection: array(ZoneRuleSchema).readonly()
14909
+ detection: array(ZoneRuleSchema).readonly(),
14910
+ package: array(ZoneRuleSchema).readonly()
14799
14911
  })
14800
14912
  },
14801
14913
  zones: zonesCapability
@@ -18774,6 +18886,7 @@ var EventKindIconSchema = _enum([
18774
18886
  "smoke",
18775
18887
  "water",
18776
18888
  "button",
18889
+ "package",
18777
18890
  "generic"
18778
18891
  ]);
18779
18892
  var EventKindCategorySchema = _enum([
@@ -18781,7 +18894,8 @@ var EventKindCategorySchema = _enum([
18781
18894
  "audio",
18782
18895
  "detection",
18783
18896
  "sensor",
18784
- "custom"
18897
+ "custom",
18898
+ "package"
18785
18899
  ]);
18786
18900
  var EventKindDescriptorSchema = object({
18787
18901
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19122,6 +19236,26 @@ var TrackCascadeCountsSchema = object({
19122
19236
  /** Per-track CLIP search vectors removed (best-effort). */
19123
19237
  embeddings: number().int()
19124
19238
  });
19239
+ /** Event-store footprint for one camera. */
19240
+ var EventStoreDeviceFootprintSchema = object({
19241
+ deviceId: number(),
19242
+ /** Persisted event rows (motion + object + audio) for the camera. */
19243
+ rows: number().int(),
19244
+ /** Event-owned media bytes on disk for the camera. */
19245
+ bytes: number().int()
19246
+ });
19247
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19248
+ var EventStoreFootprintSchema = object({
19249
+ totalRows: number().int(),
19250
+ totalBytes: number().int(),
19251
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19252
+ });
19253
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19254
+ var EventPruneCountsSchema = object({
19255
+ motion: number().int(),
19256
+ object: number().int(),
19257
+ audio: number().int()
19258
+ });
19125
19259
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19126
19260
  deviceId: number(),
19127
19261
  trackId: string()
@@ -19185,6 +19319,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19185
19319
  }), {
19186
19320
  kind: "mutation",
19187
19321
  auth: "admin"
19322
+ }), method(object({}), EventStoreFootprintSchema, {
19323
+ kind: "query",
19324
+ auth: "admin"
19325
+ }), method(object({
19326
+ olderThanMs: number(),
19327
+ reason: OpsLogReasonSchema.optional()
19328
+ }), EventPruneCountsSchema, {
19329
+ kind: "mutation",
19330
+ auth: "admin"
19331
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19332
+ kind: "mutation",
19333
+ auth: "admin"
19334
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19335
+ kind: "query",
19336
+ auth: "admin"
19188
19337
  }), method(object({
19189
19338
  eventId: string(),
19190
19339
  kind: MediaFileKindEnum.optional()
@@ -22418,13 +22567,131 @@ method(object({
22418
22567
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22419
22568
  kind: "mutation",
22420
22569
  auth: "admin"
22421
- }), method(object({ deviceId: number() }), object({
22570
+ }), method(object({
22571
+ deviceId: number(),
22572
+ reason: OpsLogReasonSchema.optional()
22573
+ }), object({
22422
22574
  floorMs: number().nullable(),
22423
22575
  deletedBuckets: number().int(),
22424
22576
  reclaimedBytes: number().int()
22425
22577
  }), {
22426
22578
  kind: "mutation",
22427
22579
  auth: "admin"
22580
+ }), method(object({
22581
+ deviceId: number(),
22582
+ fromMs: number().optional(),
22583
+ toMs: number().optional()
22584
+ }), object({
22585
+ deletedBuckets: number().int(),
22586
+ reclaimedBytes: number().int()
22587
+ }), {
22588
+ kind: "mutation",
22589
+ auth: "admin"
22590
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22591
+ kind: "query",
22592
+ auth: "admin"
22593
+ });
22594
+ /**
22595
+ * `recordingExport` cap — render a footage time range into a single downloadable
22596
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22597
+ * bounded lifetime with a durable history, auto-expiry, and optional
22598
+ * delete-after-download.
22599
+ *
22600
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22601
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22602
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22603
+ * input and dispatch to that single provider; the render runs on the node that
22604
+ * owns the footage (no cross-node segment transfer). Download rides the
22605
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22606
+ * SQLite); history rows survive file deletion for audit.
22607
+ */
22608
+ /** Playback-speed multiplier for the render (1 = realtime). */
22609
+ var ExportSpeedSchema = number().min(.25).max(32);
22610
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22611
+ var ExportTimelapseSchema = object({
22612
+ everyMs: number().int().positive(),
22613
+ outputFps: number().int().min(1).max(60).optional()
22614
+ });
22615
+ /**
22616
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22617
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22618
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22619
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22620
+ */
22621
+ var ExportOptionsSchema = object({
22622
+ speed: ExportSpeedSchema.optional(),
22623
+ timelapse: ExportTimelapseSchema.optional(),
22624
+ includeAudio: boolean(),
22625
+ maxLifeMs: number().int().positive(),
22626
+ deleteAfterDownload: boolean(),
22627
+ title: string().max(200).optional()
22628
+ }).superRefine((v, ctx) => {
22629
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22630
+ code: ZodIssueCode.custom,
22631
+ message: "speed and timelapse are mutually exclusive",
22632
+ path: ["timelapse"]
22633
+ });
22634
+ });
22635
+ var ExportStateSchema = _enum([
22636
+ "queued",
22637
+ "rendering",
22638
+ "ready",
22639
+ "failed",
22640
+ "expired",
22641
+ "deleted"
22642
+ ]);
22643
+ /** One export job / history row. */
22644
+ var ExportRecordSchema = object({
22645
+ id: string(),
22646
+ deviceId: number(),
22647
+ profile: string(),
22648
+ fromMs: number(),
22649
+ toMs: number(),
22650
+ options: ExportOptionsSchema,
22651
+ state: ExportStateSchema,
22652
+ /** 0–100 while rendering; null otherwise. */
22653
+ progressPct: number().nullable(),
22654
+ /** File size once ready; null before. */
22655
+ fileBytes: number().nullable(),
22656
+ expiresAt: number(),
22657
+ deleteAfterDownload: boolean(),
22658
+ /** Epoch of the first complete download; null until then. */
22659
+ downloadedAt: number().nullable(),
22660
+ createdAt: number(),
22661
+ /** User id/name that requested the export. */
22662
+ createdBy: string(),
22663
+ /** Failure reason when state is 'failed'; null otherwise. */
22664
+ error: string().nullable()
22665
+ });
22666
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22667
+ var ExportDownloadSchema = object({
22668
+ url: string(),
22669
+ endpoints: array(string())
22670
+ });
22671
+ method(object({
22672
+ deviceId: number(),
22673
+ profile: string(),
22674
+ fromMs: number(),
22675
+ toMs: number(),
22676
+ options: ExportOptionsSchema
22677
+ }), ExportRecordSchema, {
22678
+ kind: "mutation",
22679
+ auth: "protected"
22680
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22681
+ kind: "query",
22682
+ auth: "protected"
22683
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22684
+ kind: "query",
22685
+ auth: "protected"
22686
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22687
+ kind: "mutation",
22688
+ auth: "protected"
22689
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22690
+ kind: "mutation",
22691
+ auth: "protected"
22692
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22693
+ kind: "query",
22694
+ auth: "protected"
22428
22695
  });
22429
22696
  /**
22430
22697
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25444,6 +25711,12 @@ Object.freeze({
25444
25711
  addonId: null,
25445
25712
  access: "delete"
25446
25713
  },
25714
+ "pipelineAnalytics.deleteDeviceEvents": {
25715
+ capName: "pipeline-analytics",
25716
+ capScope: "device",
25717
+ addonId: null,
25718
+ access: "delete"
25719
+ },
25447
25720
  "pipelineAnalytics.deleteTracks": {
25448
25721
  capName: "pipeline-analytics",
25449
25722
  capScope: "device",
@@ -25474,6 +25747,12 @@ Object.freeze({
25474
25747
  addonId: null,
25475
25748
  access: "view"
25476
25749
  },
25750
+ "pipelineAnalytics.getEventStoreFootprint": {
25751
+ capName: "pipeline-analytics",
25752
+ capScope: "device",
25753
+ addonId: null,
25754
+ access: "view"
25755
+ },
25477
25756
  "pipelineAnalytics.getKeyEvents": {
25478
25757
  capName: "pipeline-analytics",
25479
25758
  capScope: "device",
@@ -25516,6 +25795,12 @@ Object.freeze({
25516
25795
  addonId: null,
25517
25796
  access: "view"
25518
25797
  },
25798
+ "pipelineAnalytics.listOpsLog": {
25799
+ capName: "pipeline-analytics",
25800
+ capScope: "device",
25801
+ addonId: null,
25802
+ access: "view"
25803
+ },
25519
25804
  "pipelineAnalytics.listRecentTracks": {
25520
25805
  capName: "pipeline-analytics",
25521
25806
  capScope: "device",
@@ -25528,6 +25813,12 @@ Object.freeze({
25528
25813
  addonId: null,
25529
25814
  access: "view"
25530
25815
  },
25816
+ "pipelineAnalytics.pruneEvents": {
25817
+ capName: "pipeline-analytics",
25818
+ capScope: "device",
25819
+ addonId: null,
25820
+ access: "create"
25821
+ },
25531
25822
  "pipelineAnalytics.pruneEventsBefore": {
25532
25823
  capName: "pipeline-analytics",
25533
25824
  capScope: "device",
@@ -26290,6 +26581,12 @@ Object.freeze({
26290
26581
  addonId: null,
26291
26582
  access: "create"
26292
26583
  },
26584
+ "recording.deleteFootprint": {
26585
+ capName: "recording",
26586
+ capScope: "system",
26587
+ addonId: null,
26588
+ access: "delete"
26589
+ },
26293
26590
  "recording.getAvailability": {
26294
26591
  capName: "recording",
26295
26592
  capScope: "system",
@@ -26320,6 +26617,12 @@ Object.freeze({
26320
26617
  addonId: null,
26321
26618
  access: "view"
26322
26619
  },
26620
+ "recording.listOpsLog": {
26621
+ capName: "recording",
26622
+ capScope: "system",
26623
+ addonId: null,
26624
+ access: "view"
26625
+ },
26323
26626
  "recording.locateSegment": {
26324
26627
  capName: "recording",
26325
26628
  capScope: "system",
@@ -26350,6 +26653,42 @@ Object.freeze({
26350
26653
  addonId: null,
26351
26654
  access: "create"
26352
26655
  },
26656
+ "recordingExport.cancelExport": {
26657
+ capName: "recordingExport",
26658
+ capScope: "system",
26659
+ addonId: null,
26660
+ access: "create"
26661
+ },
26662
+ "recordingExport.createExport": {
26663
+ capName: "recordingExport",
26664
+ capScope: "system",
26665
+ addonId: null,
26666
+ access: "create"
26667
+ },
26668
+ "recordingExport.deleteExport": {
26669
+ capName: "recordingExport",
26670
+ capScope: "system",
26671
+ addonId: null,
26672
+ access: "delete"
26673
+ },
26674
+ "recordingExport.getDownloadUrl": {
26675
+ capName: "recordingExport",
26676
+ capScope: "system",
26677
+ addonId: null,
26678
+ access: "view"
26679
+ },
26680
+ "recordingExport.getExport": {
26681
+ capName: "recordingExport",
26682
+ capScope: "system",
26683
+ addonId: null,
26684
+ access: "view"
26685
+ },
26686
+ "recordingExport.listExports": {
26687
+ capName: "recordingExport",
26688
+ capScope: "system",
26689
+ addonId: null,
26690
+ access: "view"
26691
+ },
26353
26692
  "sceneMonitor.captureReference": {
26354
26693
  capName: "scene-monitor",
26355
26694
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -36,7 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
38
38
  //#endregion
39
- //#region ../types/dist/event-category-H4AVePnn.mjs
39
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
40
40
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
41
41
  EventCategory["SystemBoot"] = "system.boot";
42
42
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -189,6 +189,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
189
189
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
190
190
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
191
191
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
192
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
193
+ * progress bar the client reconciles via `recordingExport.getExport`. */
194
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
195
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
196
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
192
197
  EventCategory["DetectionEvent"] = "detection.event";
193
198
  EventCategory["SessionTrackNew"] = "session.track.new";
194
199
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -478,6 +483,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
478
483
  */
479
484
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
480
485
  /**
486
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
487
+ * a package zone — a newly-appeared stationary object of a package class
488
+ * that cleared the class / zone / dwell / size gates. Payload:
489
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
490
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
491
+ * Telemetry (D8): the durable record is the `package-events` store row;
492
+ * this bus topic drives notifier rules + live UI. See
493
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
494
+ */
495
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
496
+ /**
497
+ * Fired by `addon-post-analysis` when a previously-delivered package
498
+ * leaves its zone (the stationary entry departed — moved or swept).
499
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
500
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
501
+ * Telemetry (D8). See package-zones-design §5.2.
502
+ */
503
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
504
+ /**
481
505
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
482
506
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
483
507
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5216,6 +5240,25 @@ function _instanceof(cls, params = {}) {
5216
5240
  };
5217
5241
  return inst;
5218
5242
  }
5243
+ //#endregion
5244
+ //#region ../../node_modules/zod/v4/classic/compat.js
5245
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5246
+ var ZodIssueCode = {
5247
+ invalid_type: "invalid_type",
5248
+ too_big: "too_big",
5249
+ too_small: "too_small",
5250
+ invalid_format: "invalid_format",
5251
+ not_multiple_of: "not_multiple_of",
5252
+ unrecognized_keys: "unrecognized_keys",
5253
+ invalid_union: "invalid_union",
5254
+ invalid_key: "invalid_key",
5255
+ invalid_element: "invalid_element",
5256
+ invalid_value: "invalid_value",
5257
+ custom: "custom"
5258
+ };
5259
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5260
+ var ZodFirstPartyTypeKind;
5261
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5219
5262
  Object.fromEntries([
5220
5263
  {
5221
5264
  id: "overview",
@@ -7338,6 +7381,62 @@ var RecordingConfigSchema = object({
7338
7381
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7339
7382
  });
7340
7383
  /**
7384
+ * Ops-log — the durable, append-only operations audit shared by the
7385
+ * recordings and events management surfaces.
7386
+ *
7387
+ * ONE row shape is reused for both domains so a single "Activity" view can
7388
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7389
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7390
+ * management operation, WHY it ran (reason), and its measurable effect
7391
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7392
+ * never fail the operation it records.
7393
+ */
7394
+ /** Which management domain the operation belongs to. */
7395
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7396
+ /** The kind of management operation performed. */
7397
+ var OpsLogOpSchema = _enum([
7398
+ "prune",
7399
+ "manual-delete",
7400
+ "rescan",
7401
+ "retention-run"
7402
+ ]);
7403
+ /** Why the operation ran. */
7404
+ var OpsLogReasonSchema = _enum([
7405
+ "retention",
7406
+ "quota",
7407
+ "manual",
7408
+ "operator"
7409
+ ]);
7410
+ /** One audit row, shared verbatim by both domains. */
7411
+ var OpsLogEntrySchema = object({
7412
+ /** Unique row id. */
7413
+ id: string(),
7414
+ /** Epoch ms the operation completed. */
7415
+ at: number(),
7416
+ domain: OpsLogDomainSchema,
7417
+ op: OpsLogOpSchema,
7418
+ reason: OpsLogReasonSchema,
7419
+ /** The camera the op targeted; null for a cluster/global op. */
7420
+ deviceId: number().nullable(),
7421
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7422
+ nodeId: string(),
7423
+ /** Buckets / rows deleted (op-specific unit). */
7424
+ itemsAffected: number(),
7425
+ /** Bytes reclaimed by the op (0 when not measurable). */
7426
+ bytesReclaimed: number(),
7427
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7428
+ detail: string().nullable(),
7429
+ /** Who/what triggered the op. */
7430
+ actor: string()
7431
+ });
7432
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7433
+ var OpsLogQueryInputSchema = object({
7434
+ /** Restrict to a single camera; omit for every row. */
7435
+ deviceId: number().optional(),
7436
+ /** Max rows returned, newest-first. */
7437
+ limit: number().int().min(1).max(1e3).optional()
7438
+ });
7439
+ /**
7341
7440
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7342
7441
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7343
7442
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14693,7 +14792,11 @@ array(ZoneRuleSchema).readonly();
14693
14792
  * Extend the enum here when a new gating consumer comes online (audio
14694
14793
  * gating, alert filtering, …) — no other surface needs to change.
14695
14794
  */
14696
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14795
+ var ZoneRuleStageEnum = _enum([
14796
+ "motion",
14797
+ "detection",
14798
+ "package"
14799
+ ]);
14697
14800
  /**
14698
14801
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14699
14802
  * `state` getter looks up the cap definition here to construct a
@@ -14787,16 +14890,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14787
14890
  })
14788
14891
  },
14789
14892
  /**
14790
- * Runtime-state slice — both stages mirrored together so consumers
14893
+ * Runtime-state slice — every stage mirrored together so consumers
14791
14894
  * see one reactive handle (`device.state.zoneRules.value`) instead
14792
- * of two. Bulk-replace mutations on either stage write the full
14793
- * `{motion, detection}` shape, so subscribers always get the
14895
+ * of one per stage. Bulk-replace mutations on any stage write the full
14896
+ * `{motion, detection, package}` shape, so subscribers always get the
14794
14897
  * complete current set. Consumers that only care about one stage
14795
14898
  * just read the matching property.
14899
+ *
14900
+ * `package` backs the package-drop detector — a package zone is a
14901
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14902
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14903
+ * The orchestrator provider writes this stage as a first-class slice
14904
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14905
+ * package}` shape, so consumers read the current package rules directly
14906
+ * off `device.state.zoneRules.value.package`.
14796
14907
  */
14797
14908
  runtimeState: object({
14798
14909
  motion: array(ZoneRuleSchema).readonly(),
14799
- detection: array(ZoneRuleSchema).readonly()
14910
+ detection: array(ZoneRuleSchema).readonly(),
14911
+ package: array(ZoneRuleSchema).readonly()
14800
14912
  })
14801
14913
  },
14802
14914
  zones: zonesCapability
@@ -18775,6 +18887,7 @@ var EventKindIconSchema = _enum([
18775
18887
  "smoke",
18776
18888
  "water",
18777
18889
  "button",
18890
+ "package",
18778
18891
  "generic"
18779
18892
  ]);
18780
18893
  var EventKindCategorySchema = _enum([
@@ -18782,7 +18895,8 @@ var EventKindCategorySchema = _enum([
18782
18895
  "audio",
18783
18896
  "detection",
18784
18897
  "sensor",
18785
- "custom"
18898
+ "custom",
18899
+ "package"
18786
18900
  ]);
18787
18901
  var EventKindDescriptorSchema = object({
18788
18902
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19123,6 +19237,26 @@ var TrackCascadeCountsSchema = object({
19123
19237
  /** Per-track CLIP search vectors removed (best-effort). */
19124
19238
  embeddings: number().int()
19125
19239
  });
19240
+ /** Event-store footprint for one camera. */
19241
+ var EventStoreDeviceFootprintSchema = object({
19242
+ deviceId: number(),
19243
+ /** Persisted event rows (motion + object + audio) for the camera. */
19244
+ rows: number().int(),
19245
+ /** Event-owned media bytes on disk for the camera. */
19246
+ bytes: number().int()
19247
+ });
19248
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19249
+ var EventStoreFootprintSchema = object({
19250
+ totalRows: number().int(),
19251
+ totalBytes: number().int(),
19252
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19253
+ });
19254
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19255
+ var EventPruneCountsSchema = object({
19256
+ motion: number().int(),
19257
+ object: number().int(),
19258
+ audio: number().int()
19259
+ });
19126
19260
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19127
19261
  deviceId: number(),
19128
19262
  trackId: string()
@@ -19186,6 +19320,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19186
19320
  }), {
19187
19321
  kind: "mutation",
19188
19322
  auth: "admin"
19323
+ }), method(object({}), EventStoreFootprintSchema, {
19324
+ kind: "query",
19325
+ auth: "admin"
19326
+ }), method(object({
19327
+ olderThanMs: number(),
19328
+ reason: OpsLogReasonSchema.optional()
19329
+ }), EventPruneCountsSchema, {
19330
+ kind: "mutation",
19331
+ auth: "admin"
19332
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19333
+ kind: "mutation",
19334
+ auth: "admin"
19335
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19336
+ kind: "query",
19337
+ auth: "admin"
19189
19338
  }), method(object({
19190
19339
  eventId: string(),
19191
19340
  kind: MediaFileKindEnum.optional()
@@ -22419,13 +22568,131 @@ method(object({
22419
22568
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22420
22569
  kind: "mutation",
22421
22570
  auth: "admin"
22422
- }), method(object({ deviceId: number() }), object({
22571
+ }), method(object({
22572
+ deviceId: number(),
22573
+ reason: OpsLogReasonSchema.optional()
22574
+ }), object({
22423
22575
  floorMs: number().nullable(),
22424
22576
  deletedBuckets: number().int(),
22425
22577
  reclaimedBytes: number().int()
22426
22578
  }), {
22427
22579
  kind: "mutation",
22428
22580
  auth: "admin"
22581
+ }), method(object({
22582
+ deviceId: number(),
22583
+ fromMs: number().optional(),
22584
+ toMs: number().optional()
22585
+ }), object({
22586
+ deletedBuckets: number().int(),
22587
+ reclaimedBytes: number().int()
22588
+ }), {
22589
+ kind: "mutation",
22590
+ auth: "admin"
22591
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22592
+ kind: "query",
22593
+ auth: "admin"
22594
+ });
22595
+ /**
22596
+ * `recordingExport` cap — render a footage time range into a single downloadable
22597
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22598
+ * bounded lifetime with a durable history, auto-expiry, and optional
22599
+ * delete-after-download.
22600
+ *
22601
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22602
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22603
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22604
+ * input and dispatch to that single provider; the render runs on the node that
22605
+ * owns the footage (no cross-node segment transfer). Download rides the
22606
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22607
+ * SQLite); history rows survive file deletion for audit.
22608
+ */
22609
+ /** Playback-speed multiplier for the render (1 = realtime). */
22610
+ var ExportSpeedSchema = number().min(.25).max(32);
22611
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22612
+ var ExportTimelapseSchema = object({
22613
+ everyMs: number().int().positive(),
22614
+ outputFps: number().int().min(1).max(60).optional()
22615
+ });
22616
+ /**
22617
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22618
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22619
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22620
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22621
+ */
22622
+ var ExportOptionsSchema = object({
22623
+ speed: ExportSpeedSchema.optional(),
22624
+ timelapse: ExportTimelapseSchema.optional(),
22625
+ includeAudio: boolean(),
22626
+ maxLifeMs: number().int().positive(),
22627
+ deleteAfterDownload: boolean(),
22628
+ title: string().max(200).optional()
22629
+ }).superRefine((v, ctx) => {
22630
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22631
+ code: ZodIssueCode.custom,
22632
+ message: "speed and timelapse are mutually exclusive",
22633
+ path: ["timelapse"]
22634
+ });
22635
+ });
22636
+ var ExportStateSchema = _enum([
22637
+ "queued",
22638
+ "rendering",
22639
+ "ready",
22640
+ "failed",
22641
+ "expired",
22642
+ "deleted"
22643
+ ]);
22644
+ /** One export job / history row. */
22645
+ var ExportRecordSchema = object({
22646
+ id: string(),
22647
+ deviceId: number(),
22648
+ profile: string(),
22649
+ fromMs: number(),
22650
+ toMs: number(),
22651
+ options: ExportOptionsSchema,
22652
+ state: ExportStateSchema,
22653
+ /** 0–100 while rendering; null otherwise. */
22654
+ progressPct: number().nullable(),
22655
+ /** File size once ready; null before. */
22656
+ fileBytes: number().nullable(),
22657
+ expiresAt: number(),
22658
+ deleteAfterDownload: boolean(),
22659
+ /** Epoch of the first complete download; null until then. */
22660
+ downloadedAt: number().nullable(),
22661
+ createdAt: number(),
22662
+ /** User id/name that requested the export. */
22663
+ createdBy: string(),
22664
+ /** Failure reason when state is 'failed'; null otherwise. */
22665
+ error: string().nullable()
22666
+ });
22667
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22668
+ var ExportDownloadSchema = object({
22669
+ url: string(),
22670
+ endpoints: array(string())
22671
+ });
22672
+ method(object({
22673
+ deviceId: number(),
22674
+ profile: string(),
22675
+ fromMs: number(),
22676
+ toMs: number(),
22677
+ options: ExportOptionsSchema
22678
+ }), ExportRecordSchema, {
22679
+ kind: "mutation",
22680
+ auth: "protected"
22681
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22682
+ kind: "query",
22683
+ auth: "protected"
22684
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22685
+ kind: "query",
22686
+ auth: "protected"
22687
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22688
+ kind: "mutation",
22689
+ auth: "protected"
22690
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22691
+ kind: "mutation",
22692
+ auth: "protected"
22693
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22694
+ kind: "query",
22695
+ auth: "protected"
22429
22696
  });
22430
22697
  /**
22431
22698
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25445,6 +25712,12 @@ Object.freeze({
25445
25712
  addonId: null,
25446
25713
  access: "delete"
25447
25714
  },
25715
+ "pipelineAnalytics.deleteDeviceEvents": {
25716
+ capName: "pipeline-analytics",
25717
+ capScope: "device",
25718
+ addonId: null,
25719
+ access: "delete"
25720
+ },
25448
25721
  "pipelineAnalytics.deleteTracks": {
25449
25722
  capName: "pipeline-analytics",
25450
25723
  capScope: "device",
@@ -25475,6 +25748,12 @@ Object.freeze({
25475
25748
  addonId: null,
25476
25749
  access: "view"
25477
25750
  },
25751
+ "pipelineAnalytics.getEventStoreFootprint": {
25752
+ capName: "pipeline-analytics",
25753
+ capScope: "device",
25754
+ addonId: null,
25755
+ access: "view"
25756
+ },
25478
25757
  "pipelineAnalytics.getKeyEvents": {
25479
25758
  capName: "pipeline-analytics",
25480
25759
  capScope: "device",
@@ -25517,6 +25796,12 @@ Object.freeze({
25517
25796
  addonId: null,
25518
25797
  access: "view"
25519
25798
  },
25799
+ "pipelineAnalytics.listOpsLog": {
25800
+ capName: "pipeline-analytics",
25801
+ capScope: "device",
25802
+ addonId: null,
25803
+ access: "view"
25804
+ },
25520
25805
  "pipelineAnalytics.listRecentTracks": {
25521
25806
  capName: "pipeline-analytics",
25522
25807
  capScope: "device",
@@ -25529,6 +25814,12 @@ Object.freeze({
25529
25814
  addonId: null,
25530
25815
  access: "view"
25531
25816
  },
25817
+ "pipelineAnalytics.pruneEvents": {
25818
+ capName: "pipeline-analytics",
25819
+ capScope: "device",
25820
+ addonId: null,
25821
+ access: "create"
25822
+ },
25532
25823
  "pipelineAnalytics.pruneEventsBefore": {
25533
25824
  capName: "pipeline-analytics",
25534
25825
  capScope: "device",
@@ -26291,6 +26582,12 @@ Object.freeze({
26291
26582
  addonId: null,
26292
26583
  access: "create"
26293
26584
  },
26585
+ "recording.deleteFootprint": {
26586
+ capName: "recording",
26587
+ capScope: "system",
26588
+ addonId: null,
26589
+ access: "delete"
26590
+ },
26294
26591
  "recording.getAvailability": {
26295
26592
  capName: "recording",
26296
26593
  capScope: "system",
@@ -26321,6 +26618,12 @@ Object.freeze({
26321
26618
  addonId: null,
26322
26619
  access: "view"
26323
26620
  },
26621
+ "recording.listOpsLog": {
26622
+ capName: "recording",
26623
+ capScope: "system",
26624
+ addonId: null,
26625
+ access: "view"
26626
+ },
26324
26627
  "recording.locateSegment": {
26325
26628
  capName: "recording",
26326
26629
  capScope: "system",
@@ -26351,6 +26654,42 @@ Object.freeze({
26351
26654
  addonId: null,
26352
26655
  access: "create"
26353
26656
  },
26657
+ "recordingExport.cancelExport": {
26658
+ capName: "recordingExport",
26659
+ capScope: "system",
26660
+ addonId: null,
26661
+ access: "create"
26662
+ },
26663
+ "recordingExport.createExport": {
26664
+ capName: "recordingExport",
26665
+ capScope: "system",
26666
+ addonId: null,
26667
+ access: "create"
26668
+ },
26669
+ "recordingExport.deleteExport": {
26670
+ capName: "recordingExport",
26671
+ capScope: "system",
26672
+ addonId: null,
26673
+ access: "delete"
26674
+ },
26675
+ "recordingExport.getDownloadUrl": {
26676
+ capName: "recordingExport",
26677
+ capScope: "system",
26678
+ addonId: null,
26679
+ access: "view"
26680
+ },
26681
+ "recordingExport.getExport": {
26682
+ capName: "recordingExport",
26683
+ capScope: "system",
26684
+ addonId: null,
26685
+ access: "view"
26686
+ },
26687
+ "recordingExport.listExports": {
26688
+ capName: "recordingExport",
26689
+ capScope: "system",
26690
+ addonId: null,
26691
+ access: "view"
26692
+ },
26354
26693
  "sceneMonitor.captureReference": {
26355
26694
  capName: "scene-monitor",
26356
26695
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreo",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Dreo smart-device (fan / air-circulator / purifier / heater / humidifier) device-provider addon for CamStack — wraps the @apocaliss92/nodedreo Dreo cloud client (REST + WebSocket)",
5
5
  "keywords": [
6
6
  "camstack",