@camstack/addon-provider-rademacher 0.1.10 → 0.1.12

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
@@ -984,7 +984,7 @@ var Rademacher = class {
984
984
  }
985
985
  };
986
986
  //#endregion
987
- //#region ../types/dist/event-category-H4AVePnn.mjs
987
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
988
988
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
989
989
  EventCategory["SystemBoot"] = "system.boot";
990
990
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1137,6 +1137,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1137
1137
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
1138
1138
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
1139
1139
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
1140
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
1141
+ * progress bar the client reconciles via `recordingExport.getExport`. */
1142
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
1143
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
1144
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
1140
1145
  EventCategory["DetectionEvent"] = "detection.event";
1141
1146
  EventCategory["SessionTrackNew"] = "session.track.new";
1142
1147
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -1426,6 +1431,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1426
1431
  */
1427
1432
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
1428
1433
  /**
1434
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
1435
+ * a package zone — a newly-appeared stationary object of a package class
1436
+ * that cleared the class / zone / dwell / size gates. Payload:
1437
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
1438
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
1439
+ * Telemetry (D8): the durable record is the `package-events` store row;
1440
+ * this bus topic drives notifier rules + live UI. See
1441
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
1442
+ */
1443
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
1444
+ /**
1445
+ * Fired by `addon-post-analysis` when a previously-delivered package
1446
+ * leaves its zone (the stationary entry departed — moved or swept).
1447
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
1448
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
1449
+ * Telemetry (D8). See package-zones-design §5.2.
1450
+ */
1451
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
1452
+ /**
1429
1453
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
1430
1454
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
1431
1455
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -6178,6 +6202,25 @@ function preprocess(fn, schema) {
6178
6202
  out: schema
6179
6203
  });
6180
6204
  }
6205
+ //#endregion
6206
+ //#region ../../node_modules/zod/v4/classic/compat.js
6207
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
6208
+ var ZodIssueCode = {
6209
+ invalid_type: "invalid_type",
6210
+ too_big: "too_big",
6211
+ too_small: "too_small",
6212
+ invalid_format: "invalid_format",
6213
+ not_multiple_of: "not_multiple_of",
6214
+ unrecognized_keys: "unrecognized_keys",
6215
+ invalid_union: "invalid_union",
6216
+ invalid_key: "invalid_key",
6217
+ invalid_element: "invalid_element",
6218
+ invalid_value: "invalid_value",
6219
+ custom: "custom"
6220
+ };
6221
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
6222
+ var ZodFirstPartyTypeKind;
6223
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6181
6224
  Object.fromEntries([
6182
6225
  {
6183
6226
  id: "overview",
@@ -8236,6 +8279,24 @@ var RecordingRetentionSchema = object({
8236
8279
  maxSizeGb: number().min(0).optional()
8237
8280
  });
8238
8281
  /**
8282
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
8283
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
8284
+ * previews at. Five graduated steps; absent on a config = `standard` (the
8285
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
8286
+ *
8287
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
8288
+ * Each window's index sidecar carries its own tile dims, so a camera whose
8289
+ * preset changed over time renders every historical window at the dims it was
8290
+ * written with.
8291
+ */
8292
+ var ScrubThumbnailPresetSchema = _enum([
8293
+ "minimal",
8294
+ "low",
8295
+ "standard",
8296
+ "high",
8297
+ "max"
8298
+ ]);
8299
+ /**
8239
8300
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
8240
8301
  *
8241
8302
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -8272,7 +8333,14 @@ var RecordingConfigSchema = object({
8272
8333
  * derived into bands once via `migrateConfigToBands`.
8273
8334
  */
8274
8335
  bands: array(RecordingBandSchema).optional(),
8275
- retention: RecordingRetentionSchema.optional()
8336
+ retention: RecordingRetentionSchema.optional(),
8337
+ /**
8338
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
8339
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
8340
+ * windows only — existing sheets are immutable, and each window's index
8341
+ * carries its own tile dims so mixed-preset history renders correctly.
8342
+ */
8343
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8276
8344
  });
8277
8345
  /**
8278
8346
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -15630,7 +15698,11 @@ array(ZoneRuleSchema).readonly();
15630
15698
  * Extend the enum here when a new gating consumer comes online (audio
15631
15699
  * gating, alert filtering, …) — no other surface needs to change.
15632
15700
  */
15633
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
15701
+ var ZoneRuleStageEnum = _enum([
15702
+ "motion",
15703
+ "detection",
15704
+ "package"
15705
+ ]);
15634
15706
  /**
15635
15707
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
15636
15708
  * `state` getter looks up the cap definition here to construct a
@@ -15724,16 +15796,24 @@ var DEVICE_LOCAL_STATE_CAPS = {
15724
15796
  })
15725
15797
  },
15726
15798
  /**
15727
- * Runtime-state slice — both stages mirrored together so consumers
15799
+ * Runtime-state slice — every stage mirrored together so consumers
15728
15800
  * see one reactive handle (`device.state.zoneRules.value`) instead
15729
- * of two. Bulk-replace mutations on either stage write the full
15730
- * `{motion, detection}` shape, so subscribers always get the
15801
+ * of one per stage. Bulk-replace mutations on any stage write the full
15802
+ * `{motion, detection, package}` shape, so subscribers always get the
15731
15803
  * complete current set. Consumers that only care about one stage
15732
15804
  * just read the matching property.
15805
+ *
15806
+ * `package` backs the package-drop detector — a package zone is a
15807
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
15808
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
15809
+ * The orchestrator provider that writes this stage lands in a later
15810
+ * slice; until then the mirror carries only `{motion, detection}` and
15811
+ * consumers read `package` as absent (treat as `[]`).
15733
15812
  */
15734
15813
  runtimeState: object({
15735
15814
  motion: array(ZoneRuleSchema).readonly(),
15736
- detection: array(ZoneRuleSchema).readonly()
15815
+ detection: array(ZoneRuleSchema).readonly(),
15816
+ package: array(ZoneRuleSchema).readonly()
15737
15817
  })
15738
15818
  },
15739
15819
  zones: zonesCapability
@@ -19695,6 +19775,7 @@ var EventKindIconSchema = _enum([
19695
19775
  "smoke",
19696
19776
  "water",
19697
19777
  "button",
19778
+ "package",
19698
19779
  "generic"
19699
19780
  ]);
19700
19781
  var EventKindCategorySchema = _enum([
@@ -19702,7 +19783,8 @@ var EventKindCategorySchema = _enum([
19702
19783
  "audio",
19703
19784
  "detection",
19704
19785
  "sensor",
19705
- "custom"
19786
+ "custom",
19787
+ "package"
19706
19788
  ]);
19707
19789
  var EventKindDescriptorSchema = object({
19708
19790
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -23348,6 +23430,108 @@ method(object({
23348
23430
  auth: "admin"
23349
23431
  });
23350
23432
  /**
23433
+ * `recordingExport` cap — render a footage time range into a single downloadable
23434
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
23435
+ * bounded lifetime with a durable history, auto-expiry, and optional
23436
+ * delete-after-download.
23437
+ *
23438
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
23439
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
23440
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
23441
+ * input and dispatch to that single provider; the render runs on the node that
23442
+ * owns the footage (no cross-node segment transfer). Download rides the
23443
+ * framework addon data-plane. State persists in a DurableState blob (NOT
23444
+ * SQLite); history rows survive file deletion for audit.
23445
+ */
23446
+ /** Playback-speed multiplier for the render (1 = realtime). */
23447
+ var ExportSpeedSchema = number().min(.25).max(32);
23448
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
23449
+ var ExportTimelapseSchema = object({
23450
+ everyMs: number().int().positive(),
23451
+ outputFps: number().int().min(1).max(60).optional()
23452
+ });
23453
+ /**
23454
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
23455
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
23456
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
23457
+ * `deleteAfterDownload` removes it shortly after the first complete download.
23458
+ */
23459
+ var ExportOptionsSchema = object({
23460
+ speed: ExportSpeedSchema.optional(),
23461
+ timelapse: ExportTimelapseSchema.optional(),
23462
+ includeAudio: boolean(),
23463
+ maxLifeMs: number().int().positive(),
23464
+ deleteAfterDownload: boolean(),
23465
+ title: string().max(200).optional()
23466
+ }).superRefine((v, ctx) => {
23467
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
23468
+ code: ZodIssueCode.custom,
23469
+ message: "speed and timelapse are mutually exclusive",
23470
+ path: ["timelapse"]
23471
+ });
23472
+ });
23473
+ var ExportStateSchema = _enum([
23474
+ "queued",
23475
+ "rendering",
23476
+ "ready",
23477
+ "failed",
23478
+ "expired",
23479
+ "deleted"
23480
+ ]);
23481
+ /** One export job / history row. */
23482
+ var ExportRecordSchema = object({
23483
+ id: string(),
23484
+ deviceId: number(),
23485
+ profile: string(),
23486
+ fromMs: number(),
23487
+ toMs: number(),
23488
+ options: ExportOptionsSchema,
23489
+ state: ExportStateSchema,
23490
+ /** 0–100 while rendering; null otherwise. */
23491
+ progressPct: number().nullable(),
23492
+ /** File size once ready; null before. */
23493
+ fileBytes: number().nullable(),
23494
+ expiresAt: number(),
23495
+ deleteAfterDownload: boolean(),
23496
+ /** Epoch of the first complete download; null until then. */
23497
+ downloadedAt: number().nullable(),
23498
+ createdAt: number(),
23499
+ /** User id/name that requested the export. */
23500
+ createdBy: string(),
23501
+ /** Failure reason when state is 'failed'; null otherwise. */
23502
+ error: string().nullable()
23503
+ });
23504
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
23505
+ var ExportDownloadSchema = object({
23506
+ url: string(),
23507
+ endpoints: array(string())
23508
+ });
23509
+ method(object({
23510
+ deviceId: number(),
23511
+ profile: string(),
23512
+ fromMs: number(),
23513
+ toMs: number(),
23514
+ options: ExportOptionsSchema
23515
+ }), ExportRecordSchema, {
23516
+ kind: "mutation",
23517
+ auth: "protected"
23518
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
23519
+ kind: "query",
23520
+ auth: "protected"
23521
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23522
+ kind: "query",
23523
+ auth: "protected"
23524
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23525
+ kind: "mutation",
23526
+ auth: "protected"
23527
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23528
+ kind: "mutation",
23529
+ auth: "protected"
23530
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
23531
+ kind: "query",
23532
+ auth: "protected"
23533
+ });
23534
+ /**
23351
23535
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
23352
23536
  * payload the (legacy) push `streamBroker.publishCameraStream` carried, minus
23353
23537
  * `deviceId`.
@@ -27271,6 +27455,42 @@ Object.freeze({
27271
27455
  addonId: null,
27272
27456
  access: "create"
27273
27457
  },
27458
+ "recordingExport.cancelExport": {
27459
+ capName: "recordingExport",
27460
+ capScope: "system",
27461
+ addonId: null,
27462
+ access: "create"
27463
+ },
27464
+ "recordingExport.createExport": {
27465
+ capName: "recordingExport",
27466
+ capScope: "system",
27467
+ addonId: null,
27468
+ access: "create"
27469
+ },
27470
+ "recordingExport.deleteExport": {
27471
+ capName: "recordingExport",
27472
+ capScope: "system",
27473
+ addonId: null,
27474
+ access: "delete"
27475
+ },
27476
+ "recordingExport.getDownloadUrl": {
27477
+ capName: "recordingExport",
27478
+ capScope: "system",
27479
+ addonId: null,
27480
+ access: "view"
27481
+ },
27482
+ "recordingExport.getExport": {
27483
+ capName: "recordingExport",
27484
+ capScope: "system",
27485
+ addonId: null,
27486
+ access: "view"
27487
+ },
27488
+ "recordingExport.listExports": {
27489
+ capName: "recordingExport",
27490
+ capScope: "system",
27491
+ addonId: null,
27492
+ access: "view"
27493
+ },
27274
27494
  "sceneMonitor.captureReference": {
27275
27495
  capName: "scene-monitor",
27276
27496
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -983,7 +983,7 @@ var Rademacher = class {
983
983
  }
984
984
  };
985
985
  //#endregion
986
- //#region ../types/dist/event-category-H4AVePnn.mjs
986
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
987
987
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
988
988
  EventCategory["SystemBoot"] = "system.boot";
989
989
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1136,6 +1136,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1136
1136
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
1137
1137
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
1138
1138
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
1139
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
1140
+ * progress bar the client reconciles via `recordingExport.getExport`. */
1141
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
1142
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
1143
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
1139
1144
  EventCategory["DetectionEvent"] = "detection.event";
1140
1145
  EventCategory["SessionTrackNew"] = "session.track.new";
1141
1146
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -1425,6 +1430,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1425
1430
  */
1426
1431
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
1427
1432
  /**
1433
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
1434
+ * a package zone — a newly-appeared stationary object of a package class
1435
+ * that cleared the class / zone / dwell / size gates. Payload:
1436
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
1437
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
1438
+ * Telemetry (D8): the durable record is the `package-events` store row;
1439
+ * this bus topic drives notifier rules + live UI. See
1440
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
1441
+ */
1442
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
1443
+ /**
1444
+ * Fired by `addon-post-analysis` when a previously-delivered package
1445
+ * leaves its zone (the stationary entry departed — moved or swept).
1446
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
1447
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
1448
+ * Telemetry (D8). See package-zones-design §5.2.
1449
+ */
1450
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
1451
+ /**
1428
1452
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
1429
1453
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
1430
1454
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -6177,6 +6201,25 @@ function preprocess(fn, schema) {
6177
6201
  out: schema
6178
6202
  });
6179
6203
  }
6204
+ //#endregion
6205
+ //#region ../../node_modules/zod/v4/classic/compat.js
6206
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
6207
+ var ZodIssueCode = {
6208
+ invalid_type: "invalid_type",
6209
+ too_big: "too_big",
6210
+ too_small: "too_small",
6211
+ invalid_format: "invalid_format",
6212
+ not_multiple_of: "not_multiple_of",
6213
+ unrecognized_keys: "unrecognized_keys",
6214
+ invalid_union: "invalid_union",
6215
+ invalid_key: "invalid_key",
6216
+ invalid_element: "invalid_element",
6217
+ invalid_value: "invalid_value",
6218
+ custom: "custom"
6219
+ };
6220
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
6221
+ var ZodFirstPartyTypeKind;
6222
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6180
6223
  Object.fromEntries([
6181
6224
  {
6182
6225
  id: "overview",
@@ -8235,6 +8278,24 @@ var RecordingRetentionSchema = object({
8235
8278
  maxSizeGb: number().min(0).optional()
8236
8279
  });
8237
8280
  /**
8281
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
8282
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
8283
+ * previews at. Five graduated steps; absent on a config = `standard` (the
8284
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
8285
+ *
8286
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
8287
+ * Each window's index sidecar carries its own tile dims, so a camera whose
8288
+ * preset changed over time renders every historical window at the dims it was
8289
+ * written with.
8290
+ */
8291
+ var ScrubThumbnailPresetSchema = _enum([
8292
+ "minimal",
8293
+ "low",
8294
+ "standard",
8295
+ "high",
8296
+ "max"
8297
+ ]);
8298
+ /**
8238
8299
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
8239
8300
  *
8240
8301
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -8271,7 +8332,14 @@ var RecordingConfigSchema = object({
8271
8332
  * derived into bands once via `migrateConfigToBands`.
8272
8333
  */
8273
8334
  bands: array(RecordingBandSchema).optional(),
8274
- retention: RecordingRetentionSchema.optional()
8335
+ retention: RecordingRetentionSchema.optional(),
8336
+ /**
8337
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
8338
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
8339
+ * windows only — existing sheets are immutable, and each window's index
8340
+ * carries its own tile dims so mixed-preset history renders correctly.
8341
+ */
8342
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8275
8343
  });
8276
8344
  /**
8277
8345
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -15629,7 +15697,11 @@ array(ZoneRuleSchema).readonly();
15629
15697
  * Extend the enum here when a new gating consumer comes online (audio
15630
15698
  * gating, alert filtering, …) — no other surface needs to change.
15631
15699
  */
15632
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
15700
+ var ZoneRuleStageEnum = _enum([
15701
+ "motion",
15702
+ "detection",
15703
+ "package"
15704
+ ]);
15633
15705
  /**
15634
15706
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
15635
15707
  * `state` getter looks up the cap definition here to construct a
@@ -15723,16 +15795,24 @@ var DEVICE_LOCAL_STATE_CAPS = {
15723
15795
  })
15724
15796
  },
15725
15797
  /**
15726
- * Runtime-state slice — both stages mirrored together so consumers
15798
+ * Runtime-state slice — every stage mirrored together so consumers
15727
15799
  * see one reactive handle (`device.state.zoneRules.value`) instead
15728
- * of two. Bulk-replace mutations on either stage write the full
15729
- * `{motion, detection}` shape, so subscribers always get the
15800
+ * of one per stage. Bulk-replace mutations on any stage write the full
15801
+ * `{motion, detection, package}` shape, so subscribers always get the
15730
15802
  * complete current set. Consumers that only care about one stage
15731
15803
  * just read the matching property.
15804
+ *
15805
+ * `package` backs the package-drop detector — a package zone is a
15806
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
15807
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
15808
+ * The orchestrator provider that writes this stage lands in a later
15809
+ * slice; until then the mirror carries only `{motion, detection}` and
15810
+ * consumers read `package` as absent (treat as `[]`).
15732
15811
  */
15733
15812
  runtimeState: object({
15734
15813
  motion: array(ZoneRuleSchema).readonly(),
15735
- detection: array(ZoneRuleSchema).readonly()
15814
+ detection: array(ZoneRuleSchema).readonly(),
15815
+ package: array(ZoneRuleSchema).readonly()
15736
15816
  })
15737
15817
  },
15738
15818
  zones: zonesCapability
@@ -19694,6 +19774,7 @@ var EventKindIconSchema = _enum([
19694
19774
  "smoke",
19695
19775
  "water",
19696
19776
  "button",
19777
+ "package",
19697
19778
  "generic"
19698
19779
  ]);
19699
19780
  var EventKindCategorySchema = _enum([
@@ -19701,7 +19782,8 @@ var EventKindCategorySchema = _enum([
19701
19782
  "audio",
19702
19783
  "detection",
19703
19784
  "sensor",
19704
- "custom"
19785
+ "custom",
19786
+ "package"
19705
19787
  ]);
19706
19788
  var EventKindDescriptorSchema = object({
19707
19789
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -23347,6 +23429,108 @@ method(object({
23347
23429
  auth: "admin"
23348
23430
  });
23349
23431
  /**
23432
+ * `recordingExport` cap — render a footage time range into a single downloadable
23433
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
23434
+ * bounded lifetime with a durable history, auto-expiry, and optional
23435
+ * delete-after-download.
23436
+ *
23437
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
23438
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
23439
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
23440
+ * input and dispatch to that single provider; the render runs on the node that
23441
+ * owns the footage (no cross-node segment transfer). Download rides the
23442
+ * framework addon data-plane. State persists in a DurableState blob (NOT
23443
+ * SQLite); history rows survive file deletion for audit.
23444
+ */
23445
+ /** Playback-speed multiplier for the render (1 = realtime). */
23446
+ var ExportSpeedSchema = number().min(.25).max(32);
23447
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
23448
+ var ExportTimelapseSchema = object({
23449
+ everyMs: number().int().positive(),
23450
+ outputFps: number().int().min(1).max(60).optional()
23451
+ });
23452
+ /**
23453
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
23454
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
23455
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
23456
+ * `deleteAfterDownload` removes it shortly after the first complete download.
23457
+ */
23458
+ var ExportOptionsSchema = object({
23459
+ speed: ExportSpeedSchema.optional(),
23460
+ timelapse: ExportTimelapseSchema.optional(),
23461
+ includeAudio: boolean(),
23462
+ maxLifeMs: number().int().positive(),
23463
+ deleteAfterDownload: boolean(),
23464
+ title: string().max(200).optional()
23465
+ }).superRefine((v, ctx) => {
23466
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
23467
+ code: ZodIssueCode.custom,
23468
+ message: "speed and timelapse are mutually exclusive",
23469
+ path: ["timelapse"]
23470
+ });
23471
+ });
23472
+ var ExportStateSchema = _enum([
23473
+ "queued",
23474
+ "rendering",
23475
+ "ready",
23476
+ "failed",
23477
+ "expired",
23478
+ "deleted"
23479
+ ]);
23480
+ /** One export job / history row. */
23481
+ var ExportRecordSchema = object({
23482
+ id: string(),
23483
+ deviceId: number(),
23484
+ profile: string(),
23485
+ fromMs: number(),
23486
+ toMs: number(),
23487
+ options: ExportOptionsSchema,
23488
+ state: ExportStateSchema,
23489
+ /** 0–100 while rendering; null otherwise. */
23490
+ progressPct: number().nullable(),
23491
+ /** File size once ready; null before. */
23492
+ fileBytes: number().nullable(),
23493
+ expiresAt: number(),
23494
+ deleteAfterDownload: boolean(),
23495
+ /** Epoch of the first complete download; null until then. */
23496
+ downloadedAt: number().nullable(),
23497
+ createdAt: number(),
23498
+ /** User id/name that requested the export. */
23499
+ createdBy: string(),
23500
+ /** Failure reason when state is 'failed'; null otherwise. */
23501
+ error: string().nullable()
23502
+ });
23503
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
23504
+ var ExportDownloadSchema = object({
23505
+ url: string(),
23506
+ endpoints: array(string())
23507
+ });
23508
+ method(object({
23509
+ deviceId: number(),
23510
+ profile: string(),
23511
+ fromMs: number(),
23512
+ toMs: number(),
23513
+ options: ExportOptionsSchema
23514
+ }), ExportRecordSchema, {
23515
+ kind: "mutation",
23516
+ auth: "protected"
23517
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
23518
+ kind: "query",
23519
+ auth: "protected"
23520
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23521
+ kind: "query",
23522
+ auth: "protected"
23523
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23524
+ kind: "mutation",
23525
+ auth: "protected"
23526
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23527
+ kind: "mutation",
23528
+ auth: "protected"
23529
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
23530
+ kind: "query",
23531
+ auth: "protected"
23532
+ });
23533
+ /**
23350
23534
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
23351
23535
  * payload the (legacy) push `streamBroker.publishCameraStream` carried, minus
23352
23536
  * `deviceId`.
@@ -27270,6 +27454,42 @@ Object.freeze({
27270
27454
  addonId: null,
27271
27455
  access: "create"
27272
27456
  },
27457
+ "recordingExport.cancelExport": {
27458
+ capName: "recordingExport",
27459
+ capScope: "system",
27460
+ addonId: null,
27461
+ access: "create"
27462
+ },
27463
+ "recordingExport.createExport": {
27464
+ capName: "recordingExport",
27465
+ capScope: "system",
27466
+ addonId: null,
27467
+ access: "create"
27468
+ },
27469
+ "recordingExport.deleteExport": {
27470
+ capName: "recordingExport",
27471
+ capScope: "system",
27472
+ addonId: null,
27473
+ access: "delete"
27474
+ },
27475
+ "recordingExport.getDownloadUrl": {
27476
+ capName: "recordingExport",
27477
+ capScope: "system",
27478
+ addonId: null,
27479
+ access: "view"
27480
+ },
27481
+ "recordingExport.getExport": {
27482
+ capName: "recordingExport",
27483
+ capScope: "system",
27484
+ addonId: null,
27485
+ access: "view"
27486
+ },
27487
+ "recordingExport.listExports": {
27488
+ capName: "recordingExport",
27489
+ capScope: "system",
27490
+ addonId: null,
27491
+ access: "view"
27492
+ },
27273
27493
  "sceneMonitor.captureReference": {
27274
27494
  capName: "scene-monitor",
27275
27495
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",