@camstack/addon-provider-wyze 0.1.21 → 0.1.23

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",
@@ -7268,6 +7311,24 @@ var RecordingRetentionSchema = object({
7268
7311
  maxSizeGb: number().min(0).optional()
7269
7312
  });
7270
7313
  /**
7314
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7315
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7316
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7317
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7318
+ *
7319
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7320
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7321
+ * preset changed over time renders every historical window at the dims it was
7322
+ * written with.
7323
+ */
7324
+ var ScrubThumbnailPresetSchema = _enum([
7325
+ "minimal",
7326
+ "low",
7327
+ "standard",
7328
+ "high",
7329
+ "max"
7330
+ ]);
7331
+ /**
7271
7332
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7272
7333
  *
7273
7334
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7304,7 +7365,14 @@ var RecordingConfigSchema = object({
7304
7365
  * derived into bands once via `migrateConfigToBands`.
7305
7366
  */
7306
7367
  bands: array(RecordingBandSchema).optional(),
7307
- retention: RecordingRetentionSchema.optional()
7368
+ retention: RecordingRetentionSchema.optional(),
7369
+ /**
7370
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7371
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7372
+ * windows only — existing sheets are immutable, and each window's index
7373
+ * carries its own tile dims so mixed-preset history renders correctly.
7374
+ */
7375
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7308
7376
  });
7309
7377
  /**
7310
7378
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -14662,7 +14730,11 @@ array(ZoneRuleSchema).readonly();
14662
14730
  * Extend the enum here when a new gating consumer comes online (audio
14663
14731
  * gating, alert filtering, …) — no other surface needs to change.
14664
14732
  */
14665
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14733
+ var ZoneRuleStageEnum = _enum([
14734
+ "motion",
14735
+ "detection",
14736
+ "package"
14737
+ ]);
14666
14738
  /**
14667
14739
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14668
14740
  * `state` getter looks up the cap definition here to construct a
@@ -14756,16 +14828,24 @@ var DEVICE_LOCAL_STATE_CAPS = {
14756
14828
  })
14757
14829
  },
14758
14830
  /**
14759
- * Runtime-state slice — both stages mirrored together so consumers
14831
+ * Runtime-state slice — every stage mirrored together so consumers
14760
14832
  * see one reactive handle (`device.state.zoneRules.value`) instead
14761
- * of two. Bulk-replace mutations on either stage write the full
14762
- * `{motion, detection}` shape, so subscribers always get the
14833
+ * of one per stage. Bulk-replace mutations on any stage write the full
14834
+ * `{motion, detection, package}` shape, so subscribers always get the
14763
14835
  * complete current set. Consumers that only care about one stage
14764
14836
  * just read the matching property.
14837
+ *
14838
+ * `package` backs the package-drop detector — a package zone is a
14839
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14840
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14841
+ * The orchestrator provider that writes this stage lands in a later
14842
+ * slice; until then the mirror carries only `{motion, detection}` and
14843
+ * consumers read `package` as absent (treat as `[]`).
14765
14844
  */
14766
14845
  runtimeState: object({
14767
14846
  motion: array(ZoneRuleSchema).readonly(),
14768
- detection: array(ZoneRuleSchema).readonly()
14847
+ detection: array(ZoneRuleSchema).readonly(),
14848
+ package: array(ZoneRuleSchema).readonly()
14769
14849
  })
14770
14850
  },
14771
14851
  zones: zonesCapability
@@ -18744,6 +18824,7 @@ var EventKindIconSchema = _enum([
18744
18824
  "smoke",
18745
18825
  "water",
18746
18826
  "button",
18827
+ "package",
18747
18828
  "generic"
18748
18829
  ]);
18749
18830
  var EventKindCategorySchema = _enum([
@@ -18751,7 +18832,8 @@ var EventKindCategorySchema = _enum([
18751
18832
  "audio",
18752
18833
  "detection",
18753
18834
  "sensor",
18754
- "custom"
18835
+ "custom",
18836
+ "package"
18755
18837
  ]);
18756
18838
  var EventKindDescriptorSchema = object({
18757
18839
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -22499,6 +22581,108 @@ method(object({
22499
22581
  auth: "admin"
22500
22582
  });
22501
22583
  /**
22584
+ * `recordingExport` cap — render a footage time range into a single downloadable
22585
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22586
+ * bounded lifetime with a durable history, auto-expiry, and optional
22587
+ * delete-after-download.
22588
+ *
22589
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22590
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22591
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22592
+ * input and dispatch to that single provider; the render runs on the node that
22593
+ * owns the footage (no cross-node segment transfer). Download rides the
22594
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22595
+ * SQLite); history rows survive file deletion for audit.
22596
+ */
22597
+ /** Playback-speed multiplier for the render (1 = realtime). */
22598
+ var ExportSpeedSchema = number().min(.25).max(32);
22599
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22600
+ var ExportTimelapseSchema = object({
22601
+ everyMs: number().int().positive(),
22602
+ outputFps: number().int().min(1).max(60).optional()
22603
+ });
22604
+ /**
22605
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22606
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22607
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22608
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22609
+ */
22610
+ var ExportOptionsSchema = object({
22611
+ speed: ExportSpeedSchema.optional(),
22612
+ timelapse: ExportTimelapseSchema.optional(),
22613
+ includeAudio: boolean(),
22614
+ maxLifeMs: number().int().positive(),
22615
+ deleteAfterDownload: boolean(),
22616
+ title: string().max(200).optional()
22617
+ }).superRefine((v, ctx) => {
22618
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22619
+ code: ZodIssueCode.custom,
22620
+ message: "speed and timelapse are mutually exclusive",
22621
+ path: ["timelapse"]
22622
+ });
22623
+ });
22624
+ var ExportStateSchema = _enum([
22625
+ "queued",
22626
+ "rendering",
22627
+ "ready",
22628
+ "failed",
22629
+ "expired",
22630
+ "deleted"
22631
+ ]);
22632
+ /** One export job / history row. */
22633
+ var ExportRecordSchema = object({
22634
+ id: string(),
22635
+ deviceId: number(),
22636
+ profile: string(),
22637
+ fromMs: number(),
22638
+ toMs: number(),
22639
+ options: ExportOptionsSchema,
22640
+ state: ExportStateSchema,
22641
+ /** 0–100 while rendering; null otherwise. */
22642
+ progressPct: number().nullable(),
22643
+ /** File size once ready; null before. */
22644
+ fileBytes: number().nullable(),
22645
+ expiresAt: number(),
22646
+ deleteAfterDownload: boolean(),
22647
+ /** Epoch of the first complete download; null until then. */
22648
+ downloadedAt: number().nullable(),
22649
+ createdAt: number(),
22650
+ /** User id/name that requested the export. */
22651
+ createdBy: string(),
22652
+ /** Failure reason when state is 'failed'; null otherwise. */
22653
+ error: string().nullable()
22654
+ });
22655
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22656
+ var ExportDownloadSchema = object({
22657
+ url: string(),
22658
+ endpoints: array(string())
22659
+ });
22660
+ method(object({
22661
+ deviceId: number(),
22662
+ profile: string(),
22663
+ fromMs: number(),
22664
+ toMs: number(),
22665
+ options: ExportOptionsSchema
22666
+ }), ExportRecordSchema, {
22667
+ kind: "mutation",
22668
+ auth: "protected"
22669
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22670
+ kind: "query",
22671
+ auth: "protected"
22672
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22673
+ kind: "query",
22674
+ auth: "protected"
22675
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22676
+ kind: "mutation",
22677
+ auth: "protected"
22678
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22679
+ kind: "mutation",
22680
+ auth: "protected"
22681
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22682
+ kind: "query",
22683
+ auth: "protected"
22684
+ });
22685
+ /**
22502
22686
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
22503
22687
  * payload the (legacy) push `streamBroker.publishCameraStream` carried, minus
22504
22688
  * `deviceId`.
@@ -26435,6 +26619,42 @@ Object.freeze({
26435
26619
  addonId: null,
26436
26620
  access: "create"
26437
26621
  },
26622
+ "recordingExport.cancelExport": {
26623
+ capName: "recordingExport",
26624
+ capScope: "system",
26625
+ addonId: null,
26626
+ access: "create"
26627
+ },
26628
+ "recordingExport.createExport": {
26629
+ capName: "recordingExport",
26630
+ capScope: "system",
26631
+ addonId: null,
26632
+ access: "create"
26633
+ },
26634
+ "recordingExport.deleteExport": {
26635
+ capName: "recordingExport",
26636
+ capScope: "system",
26637
+ addonId: null,
26638
+ access: "delete"
26639
+ },
26640
+ "recordingExport.getDownloadUrl": {
26641
+ capName: "recordingExport",
26642
+ capScope: "system",
26643
+ addonId: null,
26644
+ access: "view"
26645
+ },
26646
+ "recordingExport.getExport": {
26647
+ capName: "recordingExport",
26648
+ capScope: "system",
26649
+ addonId: null,
26650
+ access: "view"
26651
+ },
26652
+ "recordingExport.listExports": {
26653
+ capName: "recordingExport",
26654
+ capScope: "system",
26655
+ addonId: null,
26656
+ access: "view"
26657
+ },
26438
26658
  "sceneMonitor.captureReference": {
26439
26659
  capName: "scene-monitor",
26440
26660
  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",
@@ -7247,6 +7290,24 @@ var RecordingRetentionSchema = object({
7247
7290
  maxSizeGb: number().min(0).optional()
7248
7291
  });
7249
7292
  /**
7293
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7294
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7295
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7296
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7297
+ *
7298
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7299
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7300
+ * preset changed over time renders every historical window at the dims it was
7301
+ * written with.
7302
+ */
7303
+ var ScrubThumbnailPresetSchema = _enum([
7304
+ "minimal",
7305
+ "low",
7306
+ "standard",
7307
+ "high",
7308
+ "max"
7309
+ ]);
7310
+ /**
7250
7311
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7251
7312
  *
7252
7313
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7283,7 +7344,14 @@ var RecordingConfigSchema = object({
7283
7344
  * derived into bands once via `migrateConfigToBands`.
7284
7345
  */
7285
7346
  bands: array(RecordingBandSchema).optional(),
7286
- retention: RecordingRetentionSchema.optional()
7347
+ retention: RecordingRetentionSchema.optional(),
7348
+ /**
7349
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7350
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7351
+ * windows only — existing sheets are immutable, and each window's index
7352
+ * carries its own tile dims so mixed-preset history renders correctly.
7353
+ */
7354
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7287
7355
  });
7288
7356
  /**
7289
7357
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -14641,7 +14709,11 @@ array(ZoneRuleSchema).readonly();
14641
14709
  * Extend the enum here when a new gating consumer comes online (audio
14642
14710
  * gating, alert filtering, …) — no other surface needs to change.
14643
14711
  */
14644
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
14712
+ var ZoneRuleStageEnum = _enum([
14713
+ "motion",
14714
+ "detection",
14715
+ "package"
14716
+ ]);
14645
14717
  /**
14646
14718
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
14647
14719
  * `state` getter looks up the cap definition here to construct a
@@ -14735,16 +14807,24 @@ var DEVICE_LOCAL_STATE_CAPS = {
14735
14807
  })
14736
14808
  },
14737
14809
  /**
14738
- * Runtime-state slice — both stages mirrored together so consumers
14810
+ * Runtime-state slice — every stage mirrored together so consumers
14739
14811
  * see one reactive handle (`device.state.zoneRules.value`) instead
14740
- * of two. Bulk-replace mutations on either stage write the full
14741
- * `{motion, detection}` shape, so subscribers always get the
14812
+ * of one per stage. Bulk-replace mutations on any stage write the full
14813
+ * `{motion, detection, package}` shape, so subscribers always get the
14742
14814
  * complete current set. Consumers that only care about one stage
14743
14815
  * just read the matching property.
14816
+ *
14817
+ * `package` backs the package-drop detector — a package zone is a
14818
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
14819
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
14820
+ * The orchestrator provider that writes this stage lands in a later
14821
+ * slice; until then the mirror carries only `{motion, detection}` and
14822
+ * consumers read `package` as absent (treat as `[]`).
14744
14823
  */
14745
14824
  runtimeState: object({
14746
14825
  motion: array(ZoneRuleSchema).readonly(),
14747
- detection: array(ZoneRuleSchema).readonly()
14826
+ detection: array(ZoneRuleSchema).readonly(),
14827
+ package: array(ZoneRuleSchema).readonly()
14748
14828
  })
14749
14829
  },
14750
14830
  zones: zonesCapability
@@ -18723,6 +18803,7 @@ var EventKindIconSchema = _enum([
18723
18803
  "smoke",
18724
18804
  "water",
18725
18805
  "button",
18806
+ "package",
18726
18807
  "generic"
18727
18808
  ]);
18728
18809
  var EventKindCategorySchema = _enum([
@@ -18730,7 +18811,8 @@ var EventKindCategorySchema = _enum([
18730
18811
  "audio",
18731
18812
  "detection",
18732
18813
  "sensor",
18733
- "custom"
18814
+ "custom",
18815
+ "package"
18734
18816
  ]);
18735
18817
  var EventKindDescriptorSchema = object({
18736
18818
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -22478,6 +22560,108 @@ method(object({
22478
22560
  auth: "admin"
22479
22561
  });
22480
22562
  /**
22563
+ * `recordingExport` cap — render a footage time range into a single downloadable
22564
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
22565
+ * bounded lifetime with a durable history, auto-expiry, and optional
22566
+ * delete-after-download.
22567
+ *
22568
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
22569
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
22570
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
22571
+ * input and dispatch to that single provider; the render runs on the node that
22572
+ * owns the footage (no cross-node segment transfer). Download rides the
22573
+ * framework addon data-plane. State persists in a DurableState blob (NOT
22574
+ * SQLite); history rows survive file deletion for audit.
22575
+ */
22576
+ /** Playback-speed multiplier for the render (1 = realtime). */
22577
+ var ExportSpeedSchema = number().min(.25).max(32);
22578
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
22579
+ var ExportTimelapseSchema = object({
22580
+ everyMs: number().int().positive(),
22581
+ outputFps: number().int().min(1).max(60).optional()
22582
+ });
22583
+ /**
22584
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
22585
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
22586
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
22587
+ * `deleteAfterDownload` removes it shortly after the first complete download.
22588
+ */
22589
+ var ExportOptionsSchema = object({
22590
+ speed: ExportSpeedSchema.optional(),
22591
+ timelapse: ExportTimelapseSchema.optional(),
22592
+ includeAudio: boolean(),
22593
+ maxLifeMs: number().int().positive(),
22594
+ deleteAfterDownload: boolean(),
22595
+ title: string().max(200).optional()
22596
+ }).superRefine((v, ctx) => {
22597
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
22598
+ code: ZodIssueCode.custom,
22599
+ message: "speed and timelapse are mutually exclusive",
22600
+ path: ["timelapse"]
22601
+ });
22602
+ });
22603
+ var ExportStateSchema = _enum([
22604
+ "queued",
22605
+ "rendering",
22606
+ "ready",
22607
+ "failed",
22608
+ "expired",
22609
+ "deleted"
22610
+ ]);
22611
+ /** One export job / history row. */
22612
+ var ExportRecordSchema = object({
22613
+ id: string(),
22614
+ deviceId: number(),
22615
+ profile: string(),
22616
+ fromMs: number(),
22617
+ toMs: number(),
22618
+ options: ExportOptionsSchema,
22619
+ state: ExportStateSchema,
22620
+ /** 0–100 while rendering; null otherwise. */
22621
+ progressPct: number().nullable(),
22622
+ /** File size once ready; null before. */
22623
+ fileBytes: number().nullable(),
22624
+ expiresAt: number(),
22625
+ deleteAfterDownload: boolean(),
22626
+ /** Epoch of the first complete download; null until then. */
22627
+ downloadedAt: number().nullable(),
22628
+ createdAt: number(),
22629
+ /** User id/name that requested the export. */
22630
+ createdBy: string(),
22631
+ /** Failure reason when state is 'failed'; null otherwise. */
22632
+ error: string().nullable()
22633
+ });
22634
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
22635
+ var ExportDownloadSchema = object({
22636
+ url: string(),
22637
+ endpoints: array(string())
22638
+ });
22639
+ method(object({
22640
+ deviceId: number(),
22641
+ profile: string(),
22642
+ fromMs: number(),
22643
+ toMs: number(),
22644
+ options: ExportOptionsSchema
22645
+ }), ExportRecordSchema, {
22646
+ kind: "mutation",
22647
+ auth: "protected"
22648
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
22649
+ kind: "query",
22650
+ auth: "protected"
22651
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22652
+ kind: "query",
22653
+ auth: "protected"
22654
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22655
+ kind: "mutation",
22656
+ auth: "protected"
22657
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
22658
+ kind: "mutation",
22659
+ auth: "protected"
22660
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
22661
+ kind: "query",
22662
+ auth: "protected"
22663
+ });
22664
+ /**
22481
22665
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
22482
22666
  * payload the (legacy) push `streamBroker.publishCameraStream` carried, minus
22483
22667
  * `deviceId`.
@@ -26414,6 +26598,42 @@ Object.freeze({
26414
26598
  addonId: null,
26415
26599
  access: "create"
26416
26600
  },
26601
+ "recordingExport.cancelExport": {
26602
+ capName: "recordingExport",
26603
+ capScope: "system",
26604
+ addonId: null,
26605
+ access: "create"
26606
+ },
26607
+ "recordingExport.createExport": {
26608
+ capName: "recordingExport",
26609
+ capScope: "system",
26610
+ addonId: null,
26611
+ access: "create"
26612
+ },
26613
+ "recordingExport.deleteExport": {
26614
+ capName: "recordingExport",
26615
+ capScope: "system",
26616
+ addonId: null,
26617
+ access: "delete"
26618
+ },
26619
+ "recordingExport.getDownloadUrl": {
26620
+ capName: "recordingExport",
26621
+ capScope: "system",
26622
+ addonId: null,
26623
+ access: "view"
26624
+ },
26625
+ "recordingExport.getExport": {
26626
+ capName: "recordingExport",
26627
+ capScope: "system",
26628
+ addonId: null,
26629
+ access: "view"
26630
+ },
26631
+ "recordingExport.listExports": {
26632
+ capName: "recordingExport",
26633
+ capScope: "system",
26634
+ addonId: null,
26635
+ access: "view"
26636
+ },
26417
26637
  "sceneMonitor.captureReference": {
26418
26638
  capName: "scene-monitor",
26419
26639
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
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",