@camstack/addon-provider-wyze 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.js CHANGED
@@ -30,7 +30,7 @@ let events = require("events");
30
30
  let net = require("net");
31
31
  net = __toESM(net, 1);
32
32
  let node_child_process = require("node:child_process");
33
- //#region ../types/dist/event-category-H4AVePnn.mjs
33
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
34
34
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
35
35
  EventCategory["SystemBoot"] = "system.boot";
36
36
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -183,6 +183,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
183
183
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
184
184
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
185
185
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
186
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
187
+ * progress bar the client reconciles via `recordingExport.getExport`. */
188
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
189
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
190
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
186
191
  EventCategory["DetectionEvent"] = "detection.event";
187
192
  EventCategory["SessionTrackNew"] = "session.track.new";
188
193
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -472,6 +477,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
472
477
  */
473
478
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
474
479
  /**
480
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
481
+ * a package zone — a newly-appeared stationary object of a package class
482
+ * that cleared the class / zone / dwell / size gates. Payload:
483
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
484
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
485
+ * Telemetry (D8): the durable record is the `package-events` store row;
486
+ * this bus topic drives notifier rules + live UI. See
487
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
488
+ */
489
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
490
+ /**
491
+ * Fired by `addon-post-analysis` when a previously-delivered package
492
+ * leaves its zone (the stationary entry departed — moved or swept).
493
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
494
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
495
+ * Telemetry (D8). See package-zones-design §5.2.
496
+ */
497
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
498
+ /**
475
499
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
476
500
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
477
501
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5210,6 +5234,25 @@ function _instanceof(cls, params = {}) {
5210
5234
  };
5211
5235
  return inst;
5212
5236
  }
5237
+ //#endregion
5238
+ //#region ../../node_modules/zod/v4/classic/compat.js
5239
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5240
+ var ZodIssueCode = {
5241
+ invalid_type: "invalid_type",
5242
+ too_big: "too_big",
5243
+ too_small: "too_small",
5244
+ invalid_format: "invalid_format",
5245
+ not_multiple_of: "not_multiple_of",
5246
+ unrecognized_keys: "unrecognized_keys",
5247
+ invalid_union: "invalid_union",
5248
+ invalid_key: "invalid_key",
5249
+ invalid_element: "invalid_element",
5250
+ invalid_value: "invalid_value",
5251
+ custom: "custom"
5252
+ };
5253
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5254
+ var ZodFirstPartyTypeKind;
5255
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5213
5256
  Object.fromEntries([
5214
5257
  {
5215
5258
  id: "overview",
@@ -7332,6 +7375,62 @@ var RecordingConfigSchema = object({
7332
7375
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7333
7376
  });
7334
7377
  /**
7378
+ * Ops-log — the durable, append-only operations audit shared by the
7379
+ * recordings and events management surfaces.
7380
+ *
7381
+ * ONE row shape is reused for both domains so a single "Activity" view can
7382
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7383
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7384
+ * management operation, WHY it ran (reason), and its measurable effect
7385
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7386
+ * never fail the operation it records.
7387
+ */
7388
+ /** Which management domain the operation belongs to. */
7389
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7390
+ /** The kind of management operation performed. */
7391
+ var OpsLogOpSchema = _enum([
7392
+ "prune",
7393
+ "manual-delete",
7394
+ "rescan",
7395
+ "retention-run"
7396
+ ]);
7397
+ /** Why the operation ran. */
7398
+ var OpsLogReasonSchema = _enum([
7399
+ "retention",
7400
+ "quota",
7401
+ "manual",
7402
+ "operator"
7403
+ ]);
7404
+ /** One audit row, shared verbatim by both domains. */
7405
+ var OpsLogEntrySchema = object({
7406
+ /** Unique row id. */
7407
+ id: string(),
7408
+ /** Epoch ms the operation completed. */
7409
+ at: number(),
7410
+ domain: OpsLogDomainSchema,
7411
+ op: OpsLogOpSchema,
7412
+ reason: OpsLogReasonSchema,
7413
+ /** The camera the op targeted; null for a cluster/global op. */
7414
+ deviceId: number().nullable(),
7415
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7416
+ nodeId: string(),
7417
+ /** Buckets / rows deleted (op-specific unit). */
7418
+ itemsAffected: number(),
7419
+ /** Bytes reclaimed by the op (0 when not measurable). */
7420
+ bytesReclaimed: number(),
7421
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7422
+ detail: string().nullable(),
7423
+ /** Who/what triggered the op. */
7424
+ actor: string()
7425
+ });
7426
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7427
+ var OpsLogQueryInputSchema = object({
7428
+ /** Restrict to a single camera; omit for every row. */
7429
+ deviceId: number().optional(),
7430
+ /** Max rows returned, newest-first. */
7431
+ limit: number().int().min(1).max(1e3).optional()
7432
+ });
7433
+ /**
7335
7434
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7336
7435
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7337
7436
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14687,7 +14786,11 @@ array(ZoneRuleSchema).readonly();
14687
14786
  * Extend the enum here when a new gating consumer comes online (audio
14688
14787
  * gating, alert filtering, …) — no other surface needs to change.
14689
14788
  */
14690
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14789
+ var ZoneRuleStageEnum = _enum([
14790
+ "motion",
14791
+ "detection",
14792
+ "package"
14793
+ ]);
14691
14794
  /**
14692
14795
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14693
14796
  * `state` getter looks up the cap definition here to construct a
@@ -14781,16 +14884,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14781
14884
  })
14782
14885
  },
14783
14886
  /**
14784
- * Runtime-state slice — both stages mirrored together so consumers
14887
+ * Runtime-state slice — every stage mirrored together so consumers
14785
14888
  * see one reactive handle (`device.state.zoneRules.value`) instead
14786
- * of two. Bulk-replace mutations on either stage write the full
14787
- * `{motion, detection}` shape, so subscribers always get the
14889
+ * of one per stage. Bulk-replace mutations on any stage write the full
14890
+ * `{motion, detection, package}` shape, so subscribers always get the
14788
14891
  * complete current set. Consumers that only care about one stage
14789
14892
  * just read the matching property.
14893
+ *
14894
+ * `package` backs the package-drop detector — a package zone is a
14895
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14896
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14897
+ * The orchestrator provider writes this stage as a first-class slice
14898
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14899
+ * package}` shape, so consumers read the current package rules directly
14900
+ * off `device.state.zoneRules.value.package`.
14790
14901
  */
14791
14902
  runtimeState: object({
14792
14903
  motion: array(ZoneRuleSchema).readonly(),
14793
- detection: array(ZoneRuleSchema).readonly()
14904
+ detection: array(ZoneRuleSchema).readonly(),
14905
+ package: array(ZoneRuleSchema).readonly()
14794
14906
  })
14795
14907
  },
14796
14908
  zones: zonesCapability
@@ -18769,6 +18881,7 @@ var EventKindIconSchema = _enum([
18769
18881
  "smoke",
18770
18882
  "water",
18771
18883
  "button",
18884
+ "package",
18772
18885
  "generic"
18773
18886
  ]);
18774
18887
  var EventKindCategorySchema = _enum([
@@ -18776,7 +18889,8 @@ var EventKindCategorySchema = _enum([
18776
18889
  "audio",
18777
18890
  "detection",
18778
18891
  "sensor",
18779
- "custom"
18892
+ "custom",
18893
+ "package"
18780
18894
  ]);
18781
18895
  var EventKindDescriptorSchema = object({
18782
18896
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19117,6 +19231,26 @@ var TrackCascadeCountsSchema = object({
19117
19231
  /** Per-track CLIP search vectors removed (best-effort). */
19118
19232
  embeddings: number().int()
19119
19233
  });
19234
+ /** Event-store footprint for one camera. */
19235
+ var EventStoreDeviceFootprintSchema = object({
19236
+ deviceId: number(),
19237
+ /** Persisted event rows (motion + object + audio) for the camera. */
19238
+ rows: number().int(),
19239
+ /** Event-owned media bytes on disk for the camera. */
19240
+ bytes: number().int()
19241
+ });
19242
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19243
+ var EventStoreFootprintSchema = object({
19244
+ totalRows: number().int(),
19245
+ totalBytes: number().int(),
19246
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19247
+ });
19248
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19249
+ var EventPruneCountsSchema = object({
19250
+ motion: number().int(),
19251
+ object: number().int(),
19252
+ audio: number().int()
19253
+ });
19120
19254
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19121
19255
  deviceId: number(),
19122
19256
  trackId: string()
@@ -19180,6 +19314,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19180
19314
  }), {
19181
19315
  kind: "mutation",
19182
19316
  auth: "admin"
19317
+ }), method(object({}), EventStoreFootprintSchema, {
19318
+ kind: "query",
19319
+ auth: "admin"
19320
+ }), method(object({
19321
+ olderThanMs: number(),
19322
+ reason: OpsLogReasonSchema.optional()
19323
+ }), EventPruneCountsSchema, {
19324
+ kind: "mutation",
19325
+ auth: "admin"
19326
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19327
+ kind: "mutation",
19328
+ auth: "admin"
19329
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19330
+ kind: "query",
19331
+ auth: "admin"
19183
19332
  }), method(object({
19184
19333
  eventId: string(),
19185
19334
  kind: MediaFileKindEnum.optional()
@@ -22515,13 +22664,131 @@ method(object({
22515
22664
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22516
22665
  kind: "mutation",
22517
22666
  auth: "admin"
22518
- }), method(object({ deviceId: number() }), object({
22667
+ }), method(object({
22668
+ deviceId: number(),
22669
+ reason: OpsLogReasonSchema.optional()
22670
+ }), object({
22519
22671
  floorMs: number().nullable(),
22520
22672
  deletedBuckets: number().int(),
22521
22673
  reclaimedBytes: number().int()
22522
22674
  }), {
22523
22675
  kind: "mutation",
22524
22676
  auth: "admin"
22677
+ }), method(object({
22678
+ deviceId: number(),
22679
+ fromMs: number().optional(),
22680
+ toMs: number().optional()
22681
+ }), object({
22682
+ deletedBuckets: number().int(),
22683
+ reclaimedBytes: number().int()
22684
+ }), {
22685
+ kind: "mutation",
22686
+ auth: "admin"
22687
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22688
+ kind: "query",
22689
+ auth: "admin"
22690
+ });
22691
+ /**
22692
+ * `recordingExport` cap — render a footage time range into a single downloadable
22693
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22694
+ * bounded lifetime with a durable history, auto-expiry, and optional
22695
+ * delete-after-download.
22696
+ *
22697
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22698
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22699
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22700
+ * input and dispatch to that single provider; the render runs on the node that
22701
+ * owns the footage (no cross-node segment transfer). Download rides the
22702
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22703
+ * SQLite); history rows survive file deletion for audit.
22704
+ */
22705
+ /** Playback-speed multiplier for the render (1 = realtime). */
22706
+ var ExportSpeedSchema = number().min(.25).max(32);
22707
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22708
+ var ExportTimelapseSchema = object({
22709
+ everyMs: number().int().positive(),
22710
+ outputFps: number().int().min(1).max(60).optional()
22711
+ });
22712
+ /**
22713
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22714
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22715
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22716
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22717
+ */
22718
+ var ExportOptionsSchema = object({
22719
+ speed: ExportSpeedSchema.optional(),
22720
+ timelapse: ExportTimelapseSchema.optional(),
22721
+ includeAudio: boolean(),
22722
+ maxLifeMs: number().int().positive(),
22723
+ deleteAfterDownload: boolean(),
22724
+ title: string().max(200).optional()
22725
+ }).superRefine((v, ctx) => {
22726
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22727
+ code: ZodIssueCode.custom,
22728
+ message: "speed and timelapse are mutually exclusive",
22729
+ path: ["timelapse"]
22730
+ });
22731
+ });
22732
+ var ExportStateSchema = _enum([
22733
+ "queued",
22734
+ "rendering",
22735
+ "ready",
22736
+ "failed",
22737
+ "expired",
22738
+ "deleted"
22739
+ ]);
22740
+ /** One export job / history row. */
22741
+ var ExportRecordSchema = object({
22742
+ id: string(),
22743
+ deviceId: number(),
22744
+ profile: string(),
22745
+ fromMs: number(),
22746
+ toMs: number(),
22747
+ options: ExportOptionsSchema,
22748
+ state: ExportStateSchema,
22749
+ /** 0–100 while rendering; null otherwise. */
22750
+ progressPct: number().nullable(),
22751
+ /** File size once ready; null before. */
22752
+ fileBytes: number().nullable(),
22753
+ expiresAt: number(),
22754
+ deleteAfterDownload: boolean(),
22755
+ /** Epoch of the first complete download; null until then. */
22756
+ downloadedAt: number().nullable(),
22757
+ createdAt: number(),
22758
+ /** User id/name that requested the export. */
22759
+ createdBy: string(),
22760
+ /** Failure reason when state is 'failed'; null otherwise. */
22761
+ error: string().nullable()
22762
+ });
22763
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22764
+ var ExportDownloadSchema = object({
22765
+ url: string(),
22766
+ endpoints: array(string())
22767
+ });
22768
+ method(object({
22769
+ deviceId: number(),
22770
+ profile: string(),
22771
+ fromMs: number(),
22772
+ toMs: number(),
22773
+ options: ExportOptionsSchema
22774
+ }), ExportRecordSchema, {
22775
+ kind: "mutation",
22776
+ auth: "protected"
22777
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22778
+ kind: "query",
22779
+ auth: "protected"
22780
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22781
+ kind: "query",
22782
+ auth: "protected"
22783
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22784
+ kind: "mutation",
22785
+ auth: "protected"
22786
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22787
+ kind: "mutation",
22788
+ auth: "protected"
22789
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22790
+ kind: "query",
22791
+ auth: "protected"
22525
22792
  });
22526
22793
  /**
22527
22794
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25554,6 +25821,12 @@ Object.freeze({
25554
25821
  addonId: null,
25555
25822
  access: "delete"
25556
25823
  },
25824
+ "pipelineAnalytics.deleteDeviceEvents": {
25825
+ capName: "pipeline-analytics",
25826
+ capScope: "device",
25827
+ addonId: null,
25828
+ access: "delete"
25829
+ },
25557
25830
  "pipelineAnalytics.deleteTracks": {
25558
25831
  capName: "pipeline-analytics",
25559
25832
  capScope: "device",
@@ -25584,6 +25857,12 @@ Object.freeze({
25584
25857
  addonId: null,
25585
25858
  access: "view"
25586
25859
  },
25860
+ "pipelineAnalytics.getEventStoreFootprint": {
25861
+ capName: "pipeline-analytics",
25862
+ capScope: "device",
25863
+ addonId: null,
25864
+ access: "view"
25865
+ },
25587
25866
  "pipelineAnalytics.getKeyEvents": {
25588
25867
  capName: "pipeline-analytics",
25589
25868
  capScope: "device",
@@ -25626,6 +25905,12 @@ Object.freeze({
25626
25905
  addonId: null,
25627
25906
  access: "view"
25628
25907
  },
25908
+ "pipelineAnalytics.listOpsLog": {
25909
+ capName: "pipeline-analytics",
25910
+ capScope: "device",
25911
+ addonId: null,
25912
+ access: "view"
25913
+ },
25629
25914
  "pipelineAnalytics.listRecentTracks": {
25630
25915
  capName: "pipeline-analytics",
25631
25916
  capScope: "device",
@@ -25638,6 +25923,12 @@ Object.freeze({
25638
25923
  addonId: null,
25639
25924
  access: "view"
25640
25925
  },
25926
+ "pipelineAnalytics.pruneEvents": {
25927
+ capName: "pipeline-analytics",
25928
+ capScope: "device",
25929
+ addonId: null,
25930
+ access: "create"
25931
+ },
25641
25932
  "pipelineAnalytics.pruneEventsBefore": {
25642
25933
  capName: "pipeline-analytics",
25643
25934
  capScope: "device",
@@ -26400,6 +26691,12 @@ Object.freeze({
26400
26691
  addonId: null,
26401
26692
  access: "create"
26402
26693
  },
26694
+ "recording.deleteFootprint": {
26695
+ capName: "recording",
26696
+ capScope: "system",
26697
+ addonId: null,
26698
+ access: "delete"
26699
+ },
26403
26700
  "recording.getAvailability": {
26404
26701
  capName: "recording",
26405
26702
  capScope: "system",
@@ -26430,6 +26727,12 @@ Object.freeze({
26430
26727
  addonId: null,
26431
26728
  access: "view"
26432
26729
  },
26730
+ "recording.listOpsLog": {
26731
+ capName: "recording",
26732
+ capScope: "system",
26733
+ addonId: null,
26734
+ access: "view"
26735
+ },
26433
26736
  "recording.locateSegment": {
26434
26737
  capName: "recording",
26435
26738
  capScope: "system",
@@ -26460,6 +26763,42 @@ Object.freeze({
26460
26763
  addonId: null,
26461
26764
  access: "create"
26462
26765
  },
26766
+ "recordingExport.cancelExport": {
26767
+ capName: "recordingExport",
26768
+ capScope: "system",
26769
+ addonId: null,
26770
+ access: "create"
26771
+ },
26772
+ "recordingExport.createExport": {
26773
+ capName: "recordingExport",
26774
+ capScope: "system",
26775
+ addonId: null,
26776
+ access: "create"
26777
+ },
26778
+ "recordingExport.deleteExport": {
26779
+ capName: "recordingExport",
26780
+ capScope: "system",
26781
+ addonId: null,
26782
+ access: "delete"
26783
+ },
26784
+ "recordingExport.getDownloadUrl": {
26785
+ capName: "recordingExport",
26786
+ capScope: "system",
26787
+ addonId: null,
26788
+ access: "view"
26789
+ },
26790
+ "recordingExport.getExport": {
26791
+ capName: "recordingExport",
26792
+ capScope: "system",
26793
+ addonId: null,
26794
+ access: "view"
26795
+ },
26796
+ "recordingExport.listExports": {
26797
+ capName: "recordingExport",
26798
+ capScope: "system",
26799
+ addonId: null,
26800
+ access: "view"
26801
+ },
26463
26802
  "sceneMonitor.captureReference": {
26464
26803
  capName: "scene-monitor",
26465
26804
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -9,7 +9,7 @@ import { spawn } from "node:child_process";
9
9
  //#region \0rolldown/runtime.js
10
10
  var __require$1 = /* @__PURE__ */ createRequire(import.meta.url);
11
11
  //#endregion
12
- //#region ../types/dist/event-category-H4AVePnn.mjs
12
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
13
13
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
14
14
  EventCategory["SystemBoot"] = "system.boot";
15
15
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -162,6 +162,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
162
162
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
163
163
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
164
164
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
165
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
166
+ * progress bar the client reconciles via `recordingExport.getExport`. */
167
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
168
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
169
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
165
170
  EventCategory["DetectionEvent"] = "detection.event";
166
171
  EventCategory["SessionTrackNew"] = "session.track.new";
167
172
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -451,6 +456,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
451
456
  */
452
457
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
453
458
  /**
459
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
460
+ * a package zone — a newly-appeared stationary object of a package class
461
+ * that cleared the class / zone / dwell / size gates. Payload:
462
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
463
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
464
+ * Telemetry (D8): the durable record is the `package-events` store row;
465
+ * this bus topic drives notifier rules + live UI. See
466
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
467
+ */
468
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
469
+ /**
470
+ * Fired by `addon-post-analysis` when a previously-delivered package
471
+ * leaves its zone (the stationary entry departed — moved or swept).
472
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
473
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
474
+ * Telemetry (D8). See package-zones-design §5.2.
475
+ */
476
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
477
+ /**
454
478
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
455
479
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
456
480
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5189,6 +5213,25 @@ function _instanceof(cls, params = {}) {
5189
5213
  };
5190
5214
  return inst;
5191
5215
  }
5216
+ //#endregion
5217
+ //#region ../../node_modules/zod/v4/classic/compat.js
5218
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5219
+ var ZodIssueCode = {
5220
+ invalid_type: "invalid_type",
5221
+ too_big: "too_big",
5222
+ too_small: "too_small",
5223
+ invalid_format: "invalid_format",
5224
+ not_multiple_of: "not_multiple_of",
5225
+ unrecognized_keys: "unrecognized_keys",
5226
+ invalid_union: "invalid_union",
5227
+ invalid_key: "invalid_key",
5228
+ invalid_element: "invalid_element",
5229
+ invalid_value: "invalid_value",
5230
+ custom: "custom"
5231
+ };
5232
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5233
+ var ZodFirstPartyTypeKind;
5234
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5192
5235
  Object.fromEntries([
5193
5236
  {
5194
5237
  id: "overview",
@@ -7311,6 +7354,62 @@ var RecordingConfigSchema = object({
7311
7354
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7312
7355
  });
7313
7356
  /**
7357
+ * Ops-log — the durable, append-only operations audit shared by the
7358
+ * recordings and events management surfaces.
7359
+ *
7360
+ * ONE row shape is reused for both domains so a single "Activity" view can
7361
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7362
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7363
+ * management operation, WHY it ran (reason), and its measurable effect
7364
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7365
+ * never fail the operation it records.
7366
+ */
7367
+ /** Which management domain the operation belongs to. */
7368
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7369
+ /** The kind of management operation performed. */
7370
+ var OpsLogOpSchema = _enum([
7371
+ "prune",
7372
+ "manual-delete",
7373
+ "rescan",
7374
+ "retention-run"
7375
+ ]);
7376
+ /** Why the operation ran. */
7377
+ var OpsLogReasonSchema = _enum([
7378
+ "retention",
7379
+ "quota",
7380
+ "manual",
7381
+ "operator"
7382
+ ]);
7383
+ /** One audit row, shared verbatim by both domains. */
7384
+ var OpsLogEntrySchema = object({
7385
+ /** Unique row id. */
7386
+ id: string(),
7387
+ /** Epoch ms the operation completed. */
7388
+ at: number(),
7389
+ domain: OpsLogDomainSchema,
7390
+ op: OpsLogOpSchema,
7391
+ reason: OpsLogReasonSchema,
7392
+ /** The camera the op targeted; null for a cluster/global op. */
7393
+ deviceId: number().nullable(),
7394
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7395
+ nodeId: string(),
7396
+ /** Buckets / rows deleted (op-specific unit). */
7397
+ itemsAffected: number(),
7398
+ /** Bytes reclaimed by the op (0 when not measurable). */
7399
+ bytesReclaimed: number(),
7400
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7401
+ detail: string().nullable(),
7402
+ /** Who/what triggered the op. */
7403
+ actor: string()
7404
+ });
7405
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7406
+ var OpsLogQueryInputSchema = object({
7407
+ /** Restrict to a single camera; omit for every row. */
7408
+ deviceId: number().optional(),
7409
+ /** Max rows returned, newest-first. */
7410
+ limit: number().int().min(1).max(1e3).optional()
7411
+ });
7412
+ /**
7314
7413
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7315
7414
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7316
7415
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -14666,7 +14765,11 @@ array(ZoneRuleSchema).readonly();
14666
14765
  * Extend the enum here when a new gating consumer comes online (audio
14667
14766
  * gating, alert filtering, …) — no other surface needs to change.
14668
14767
  */
14669
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14768
+ var ZoneRuleStageEnum = _enum([
14769
+ "motion",
14770
+ "detection",
14771
+ "package"
14772
+ ]);
14670
14773
  /**
14671
14774
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14672
14775
  * `state` getter looks up the cap definition here to construct a
@@ -14760,16 +14863,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
14760
14863
  })
14761
14864
  },
14762
14865
  /**
14763
- * Runtime-state slice — both stages mirrored together so consumers
14866
+ * Runtime-state slice — every stage mirrored together so consumers
14764
14867
  * see one reactive handle (`device.state.zoneRules.value`) instead
14765
- * of two. Bulk-replace mutations on either stage write the full
14766
- * `{motion, detection}` shape, so subscribers always get the
14868
+ * of one per stage. Bulk-replace mutations on any stage write the full
14869
+ * `{motion, detection, package}` shape, so subscribers always get the
14767
14870
  * complete current set. Consumers that only care about one stage
14768
14871
  * just read the matching property.
14872
+ *
14873
+ * `package` backs the package-drop detector — a package zone is a
14874
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14875
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14876
+ * The orchestrator provider writes this stage as a first-class slice
14877
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
14878
+ * package}` shape, so consumers read the current package rules directly
14879
+ * off `device.state.zoneRules.value.package`.
14769
14880
  */
14770
14881
  runtimeState: object({
14771
14882
  motion: array(ZoneRuleSchema).readonly(),
14772
- detection: array(ZoneRuleSchema).readonly()
14883
+ detection: array(ZoneRuleSchema).readonly(),
14884
+ package: array(ZoneRuleSchema).readonly()
14773
14885
  })
14774
14886
  },
14775
14887
  zones: zonesCapability
@@ -18748,6 +18860,7 @@ var EventKindIconSchema = _enum([
18748
18860
  "smoke",
18749
18861
  "water",
18750
18862
  "button",
18863
+ "package",
18751
18864
  "generic"
18752
18865
  ]);
18753
18866
  var EventKindCategorySchema = _enum([
@@ -18755,7 +18868,8 @@ var EventKindCategorySchema = _enum([
18755
18868
  "audio",
18756
18869
  "detection",
18757
18870
  "sensor",
18758
- "custom"
18871
+ "custom",
18872
+ "package"
18759
18873
  ]);
18760
18874
  var EventKindDescriptorSchema = object({
18761
18875
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -19096,6 +19210,26 @@ var TrackCascadeCountsSchema = object({
19096
19210
  /** Per-track CLIP search vectors removed (best-effort). */
19097
19211
  embeddings: number().int()
19098
19212
  });
19213
+ /** Event-store footprint for one camera. */
19214
+ var EventStoreDeviceFootprintSchema = object({
19215
+ deviceId: number(),
19216
+ /** Persisted event rows (motion + object + audio) for the camera. */
19217
+ rows: number().int(),
19218
+ /** Event-owned media bytes on disk for the camera. */
19219
+ bytes: number().int()
19220
+ });
19221
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
19222
+ var EventStoreFootprintSchema = object({
19223
+ totalRows: number().int(),
19224
+ totalBytes: number().int(),
19225
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
19226
+ });
19227
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
19228
+ var EventPruneCountsSchema = object({
19229
+ motion: number().int(),
19230
+ object: number().int(),
19231
+ audio: number().int()
19232
+ });
19099
19233
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
19100
19234
  deviceId: number(),
19101
19235
  trackId: string()
@@ -19159,6 +19293,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19159
19293
  }), {
19160
19294
  kind: "mutation",
19161
19295
  auth: "admin"
19296
+ }), method(object({}), EventStoreFootprintSchema, {
19297
+ kind: "query",
19298
+ auth: "admin"
19299
+ }), method(object({
19300
+ olderThanMs: number(),
19301
+ reason: OpsLogReasonSchema.optional()
19302
+ }), EventPruneCountsSchema, {
19303
+ kind: "mutation",
19304
+ auth: "admin"
19305
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
19306
+ kind: "mutation",
19307
+ auth: "admin"
19308
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19309
+ kind: "query",
19310
+ auth: "admin"
19162
19311
  }), method(object({
19163
19312
  eventId: string(),
19164
19313
  kind: MediaFileKindEnum.optional()
@@ -22494,13 +22643,131 @@ method(object({
22494
22643
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
22495
22644
  kind: "mutation",
22496
22645
  auth: "admin"
22497
- }), method(object({ deviceId: number() }), object({
22646
+ }), method(object({
22647
+ deviceId: number(),
22648
+ reason: OpsLogReasonSchema.optional()
22649
+ }), object({
22498
22650
  floorMs: number().nullable(),
22499
22651
  deletedBuckets: number().int(),
22500
22652
  reclaimedBytes: number().int()
22501
22653
  }), {
22502
22654
  kind: "mutation",
22503
22655
  auth: "admin"
22656
+ }), method(object({
22657
+ deviceId: number(),
22658
+ fromMs: number().optional(),
22659
+ toMs: number().optional()
22660
+ }), object({
22661
+ deletedBuckets: number().int(),
22662
+ reclaimedBytes: number().int()
22663
+ }), {
22664
+ kind: "mutation",
22665
+ auth: "admin"
22666
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
22667
+ kind: "query",
22668
+ auth: "admin"
22669
+ });
22670
+ /**
22671
+ * `recordingExport` cap — render a footage time range into a single downloadable
22672
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22673
+ * bounded lifetime with a durable history, auto-expiry, and optional
22674
+ * delete-after-download.
22675
+ *
22676
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22677
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22678
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22679
+ * input and dispatch to that single provider; the render runs on the node that
22680
+ * owns the footage (no cross-node segment transfer). Download rides the
22681
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22682
+ * SQLite); history rows survive file deletion for audit.
22683
+ */
22684
+ /** Playback-speed multiplier for the render (1 = realtime). */
22685
+ var ExportSpeedSchema = number().min(.25).max(32);
22686
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22687
+ var ExportTimelapseSchema = object({
22688
+ everyMs: number().int().positive(),
22689
+ outputFps: number().int().min(1).max(60).optional()
22690
+ });
22691
+ /**
22692
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22693
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22694
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22695
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22696
+ */
22697
+ var ExportOptionsSchema = object({
22698
+ speed: ExportSpeedSchema.optional(),
22699
+ timelapse: ExportTimelapseSchema.optional(),
22700
+ includeAudio: boolean(),
22701
+ maxLifeMs: number().int().positive(),
22702
+ deleteAfterDownload: boolean(),
22703
+ title: string().max(200).optional()
22704
+ }).superRefine((v, ctx) => {
22705
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22706
+ code: ZodIssueCode.custom,
22707
+ message: "speed and timelapse are mutually exclusive",
22708
+ path: ["timelapse"]
22709
+ });
22710
+ });
22711
+ var ExportStateSchema = _enum([
22712
+ "queued",
22713
+ "rendering",
22714
+ "ready",
22715
+ "failed",
22716
+ "expired",
22717
+ "deleted"
22718
+ ]);
22719
+ /** One export job / history row. */
22720
+ var ExportRecordSchema = object({
22721
+ id: string(),
22722
+ deviceId: number(),
22723
+ profile: string(),
22724
+ fromMs: number(),
22725
+ toMs: number(),
22726
+ options: ExportOptionsSchema,
22727
+ state: ExportStateSchema,
22728
+ /** 0–100 while rendering; null otherwise. */
22729
+ progressPct: number().nullable(),
22730
+ /** File size once ready; null before. */
22731
+ fileBytes: number().nullable(),
22732
+ expiresAt: number(),
22733
+ deleteAfterDownload: boolean(),
22734
+ /** Epoch of the first complete download; null until then. */
22735
+ downloadedAt: number().nullable(),
22736
+ createdAt: number(),
22737
+ /** User id/name that requested the export. */
22738
+ createdBy: string(),
22739
+ /** Failure reason when state is 'failed'; null otherwise. */
22740
+ error: string().nullable()
22741
+ });
22742
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22743
+ var ExportDownloadSchema = object({
22744
+ url: string(),
22745
+ endpoints: array(string())
22746
+ });
22747
+ method(object({
22748
+ deviceId: number(),
22749
+ profile: string(),
22750
+ fromMs: number(),
22751
+ toMs: number(),
22752
+ options: ExportOptionsSchema
22753
+ }), ExportRecordSchema, {
22754
+ kind: "mutation",
22755
+ auth: "protected"
22756
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22757
+ kind: "query",
22758
+ auth: "protected"
22759
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22760
+ kind: "query",
22761
+ auth: "protected"
22762
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22763
+ kind: "mutation",
22764
+ auth: "protected"
22765
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22766
+ kind: "mutation",
22767
+ auth: "protected"
22768
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22769
+ kind: "query",
22770
+ auth: "protected"
22504
22771
  });
22505
22772
  /**
22506
22773
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -25533,6 +25800,12 @@ Object.freeze({
25533
25800
  addonId: null,
25534
25801
  access: "delete"
25535
25802
  },
25803
+ "pipelineAnalytics.deleteDeviceEvents": {
25804
+ capName: "pipeline-analytics",
25805
+ capScope: "device",
25806
+ addonId: null,
25807
+ access: "delete"
25808
+ },
25536
25809
  "pipelineAnalytics.deleteTracks": {
25537
25810
  capName: "pipeline-analytics",
25538
25811
  capScope: "device",
@@ -25563,6 +25836,12 @@ Object.freeze({
25563
25836
  addonId: null,
25564
25837
  access: "view"
25565
25838
  },
25839
+ "pipelineAnalytics.getEventStoreFootprint": {
25840
+ capName: "pipeline-analytics",
25841
+ capScope: "device",
25842
+ addonId: null,
25843
+ access: "view"
25844
+ },
25566
25845
  "pipelineAnalytics.getKeyEvents": {
25567
25846
  capName: "pipeline-analytics",
25568
25847
  capScope: "device",
@@ -25605,6 +25884,12 @@ Object.freeze({
25605
25884
  addonId: null,
25606
25885
  access: "view"
25607
25886
  },
25887
+ "pipelineAnalytics.listOpsLog": {
25888
+ capName: "pipeline-analytics",
25889
+ capScope: "device",
25890
+ addonId: null,
25891
+ access: "view"
25892
+ },
25608
25893
  "pipelineAnalytics.listRecentTracks": {
25609
25894
  capName: "pipeline-analytics",
25610
25895
  capScope: "device",
@@ -25617,6 +25902,12 @@ Object.freeze({
25617
25902
  addonId: null,
25618
25903
  access: "view"
25619
25904
  },
25905
+ "pipelineAnalytics.pruneEvents": {
25906
+ capName: "pipeline-analytics",
25907
+ capScope: "device",
25908
+ addonId: null,
25909
+ access: "create"
25910
+ },
25620
25911
  "pipelineAnalytics.pruneEventsBefore": {
25621
25912
  capName: "pipeline-analytics",
25622
25913
  capScope: "device",
@@ -26379,6 +26670,12 @@ Object.freeze({
26379
26670
  addonId: null,
26380
26671
  access: "create"
26381
26672
  },
26673
+ "recording.deleteFootprint": {
26674
+ capName: "recording",
26675
+ capScope: "system",
26676
+ addonId: null,
26677
+ access: "delete"
26678
+ },
26382
26679
  "recording.getAvailability": {
26383
26680
  capName: "recording",
26384
26681
  capScope: "system",
@@ -26409,6 +26706,12 @@ Object.freeze({
26409
26706
  addonId: null,
26410
26707
  access: "view"
26411
26708
  },
26709
+ "recording.listOpsLog": {
26710
+ capName: "recording",
26711
+ capScope: "system",
26712
+ addonId: null,
26713
+ access: "view"
26714
+ },
26412
26715
  "recording.locateSegment": {
26413
26716
  capName: "recording",
26414
26717
  capScope: "system",
@@ -26439,6 +26742,42 @@ Object.freeze({
26439
26742
  addonId: null,
26440
26743
  access: "create"
26441
26744
  },
26745
+ "recordingExport.cancelExport": {
26746
+ capName: "recordingExport",
26747
+ capScope: "system",
26748
+ addonId: null,
26749
+ access: "create"
26750
+ },
26751
+ "recordingExport.createExport": {
26752
+ capName: "recordingExport",
26753
+ capScope: "system",
26754
+ addonId: null,
26755
+ access: "create"
26756
+ },
26757
+ "recordingExport.deleteExport": {
26758
+ capName: "recordingExport",
26759
+ capScope: "system",
26760
+ addonId: null,
26761
+ access: "delete"
26762
+ },
26763
+ "recordingExport.getDownloadUrl": {
26764
+ capName: "recordingExport",
26765
+ capScope: "system",
26766
+ addonId: null,
26767
+ access: "view"
26768
+ },
26769
+ "recordingExport.getExport": {
26770
+ capName: "recordingExport",
26771
+ capScope: "system",
26772
+ addonId: null,
26773
+ access: "view"
26774
+ },
26775
+ "recordingExport.listExports": {
26776
+ capName: "recordingExport",
26777
+ capScope: "system",
26778
+ addonId: null,
26779
+ access: "view"
26780
+ },
26442
26781
  "sceneMonitor.captureReference": {
26443
26782
  capName: "scene-monitor",
26444
26783
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "Wyze camera device-provider addon for CamStack — wraps the @apocaliss92/wyze-bridge-js P2P/DTLS client, feeding the stream-broker via the pull-rfc4571 lazy-publish path (a structural twin of addon-provider-reolink)",
5
5
  "keywords": [
6
6
  "camstack",