@camstack/addon-provider-rademacher 0.1.11 → 0.1.13

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",
@@ -8300,6 +8343,62 @@ var RecordingConfigSchema = object({
8300
8343
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8301
8344
  });
8302
8345
  /**
8346
+ * Ops-log — the durable, append-only operations audit shared by the
8347
+ * recordings and events management surfaces.
8348
+ *
8349
+ * ONE row shape is reused for both domains so a single "Activity" view can
8350
+ * merge the recorder's DurableState ring (recordings ops-log) and the
8351
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
8352
+ * management operation, WHY it ran (reason), and its measurable effect
8353
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
8354
+ * never fail the operation it records.
8355
+ */
8356
+ /** Which management domain the operation belongs to. */
8357
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
8358
+ /** The kind of management operation performed. */
8359
+ var OpsLogOpSchema = _enum([
8360
+ "prune",
8361
+ "manual-delete",
8362
+ "rescan",
8363
+ "retention-run"
8364
+ ]);
8365
+ /** Why the operation ran. */
8366
+ var OpsLogReasonSchema = _enum([
8367
+ "retention",
8368
+ "quota",
8369
+ "manual",
8370
+ "operator"
8371
+ ]);
8372
+ /** One audit row, shared verbatim by both domains. */
8373
+ var OpsLogEntrySchema = object({
8374
+ /** Unique row id. */
8375
+ id: string(),
8376
+ /** Epoch ms the operation completed. */
8377
+ at: number(),
8378
+ domain: OpsLogDomainSchema,
8379
+ op: OpsLogOpSchema,
8380
+ reason: OpsLogReasonSchema,
8381
+ /** The camera the op targeted; null for a cluster/global op. */
8382
+ deviceId: number().nullable(),
8383
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
8384
+ nodeId: string(),
8385
+ /** Buckets / rows deleted (op-specific unit). */
8386
+ itemsAffected: number(),
8387
+ /** Bytes reclaimed by the op (0 when not measurable). */
8388
+ bytesReclaimed: number(),
8389
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
8390
+ detail: string().nullable(),
8391
+ /** Who/what triggered the op. */
8392
+ actor: string()
8393
+ });
8394
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
8395
+ var OpsLogQueryInputSchema = object({
8396
+ /** Restrict to a single camera; omit for every row. */
8397
+ deviceId: number().optional(),
8398
+ /** Max rows returned, newest-first. */
8399
+ limit: number().int().min(1).max(1e3).optional()
8400
+ });
8401
+ /**
8303
8402
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8304
8403
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8305
8404
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -15655,7 +15754,11 @@ array(ZoneRuleSchema).readonly();
15655
15754
  * Extend the enum here when a new gating consumer comes online (audio
15656
15755
  * gating, alert filtering, …) — no other surface needs to change.
15657
15756
  */
15658
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
15757
+ var ZoneRuleStageEnum = _enum([
15758
+ "motion",
15759
+ "detection",
15760
+ "package"
15761
+ ]);
15659
15762
  /**
15660
15763
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
15661
15764
  * `state` getter looks up the cap definition here to construct a
@@ -15749,16 +15852,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
15749
15852
  })
15750
15853
  },
15751
15854
  /**
15752
- * Runtime-state slice — both stages mirrored together so consumers
15855
+ * Runtime-state slice — every stage mirrored together so consumers
15753
15856
  * see one reactive handle (`device.state.zoneRules.value`) instead
15754
- * of two. Bulk-replace mutations on either stage write the full
15755
- * `{motion, detection}` shape, so subscribers always get the
15857
+ * of one per stage. Bulk-replace mutations on any stage write the full
15858
+ * `{motion, detection, package}` shape, so subscribers always get the
15756
15859
  * complete current set. Consumers that only care about one stage
15757
15860
  * just read the matching property.
15861
+ *
15862
+ * `package` backs the package-drop detector — a package zone is a
15863
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
15864
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
15865
+ * The orchestrator provider writes this stage as a first-class slice
15866
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
15867
+ * package}` shape, so consumers read the current package rules directly
15868
+ * off `device.state.zoneRules.value.package`.
15758
15869
  */
15759
15870
  runtimeState: object({
15760
15871
  motion: array(ZoneRuleSchema).readonly(),
15761
- detection: array(ZoneRuleSchema).readonly()
15872
+ detection: array(ZoneRuleSchema).readonly(),
15873
+ package: array(ZoneRuleSchema).readonly()
15762
15874
  })
15763
15875
  },
15764
15876
  zones: zonesCapability
@@ -19720,6 +19832,7 @@ var EventKindIconSchema = _enum([
19720
19832
  "smoke",
19721
19833
  "water",
19722
19834
  "button",
19835
+ "package",
19723
19836
  "generic"
19724
19837
  ]);
19725
19838
  var EventKindCategorySchema = _enum([
@@ -19727,7 +19840,8 @@ var EventKindCategorySchema = _enum([
19727
19840
  "audio",
19728
19841
  "detection",
19729
19842
  "sensor",
19730
- "custom"
19843
+ "custom",
19844
+ "package"
19731
19845
  ]);
19732
19846
  var EventKindDescriptorSchema = object({
19733
19847
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -20068,6 +20182,26 @@ var TrackCascadeCountsSchema = object({
20068
20182
  /** Per-track CLIP search vectors removed (best-effort). */
20069
20183
  embeddings: number().int()
20070
20184
  });
20185
+ /** Event-store footprint for one camera. */
20186
+ var EventStoreDeviceFootprintSchema = object({
20187
+ deviceId: number(),
20188
+ /** Persisted event rows (motion + object + audio) for the camera. */
20189
+ rows: number().int(),
20190
+ /** Event-owned media bytes on disk for the camera. */
20191
+ bytes: number().int()
20192
+ });
20193
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
20194
+ var EventStoreFootprintSchema = object({
20195
+ totalRows: number().int(),
20196
+ totalBytes: number().int(),
20197
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
20198
+ });
20199
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
20200
+ var EventPruneCountsSchema = object({
20201
+ motion: number().int(),
20202
+ object: number().int(),
20203
+ audio: number().int()
20204
+ });
20071
20205
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20072
20206
  deviceId: number(),
20073
20207
  trackId: string()
@@ -20131,6 +20265,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20131
20265
  }), {
20132
20266
  kind: "mutation",
20133
20267
  auth: "admin"
20268
+ }), method(object({}), EventStoreFootprintSchema, {
20269
+ kind: "query",
20270
+ auth: "admin"
20271
+ }), method(object({
20272
+ olderThanMs: number(),
20273
+ reason: OpsLogReasonSchema.optional()
20274
+ }), EventPruneCountsSchema, {
20275
+ kind: "mutation",
20276
+ auth: "admin"
20277
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
20278
+ kind: "mutation",
20279
+ auth: "admin"
20280
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20281
+ kind: "query",
20282
+ auth: "admin"
20134
20283
  }), method(object({
20135
20284
  eventId: string(),
20136
20285
  kind: MediaFileKindEnum.optional()
@@ -23364,13 +23513,131 @@ method(object({
23364
23513
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
23365
23514
  kind: "mutation",
23366
23515
  auth: "admin"
23367
- }), method(object({ deviceId: number() }), object({
23516
+ }), method(object({
23517
+ deviceId: number(),
23518
+ reason: OpsLogReasonSchema.optional()
23519
+ }), object({
23368
23520
  floorMs: number().nullable(),
23369
23521
  deletedBuckets: number().int(),
23370
23522
  reclaimedBytes: number().int()
23371
23523
  }), {
23372
23524
  kind: "mutation",
23373
23525
  auth: "admin"
23526
+ }), method(object({
23527
+ deviceId: number(),
23528
+ fromMs: number().optional(),
23529
+ toMs: number().optional()
23530
+ }), object({
23531
+ deletedBuckets: number().int(),
23532
+ reclaimedBytes: number().int()
23533
+ }), {
23534
+ kind: "mutation",
23535
+ auth: "admin"
23536
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
23537
+ kind: "query",
23538
+ auth: "admin"
23539
+ });
23540
+ /**
23541
+ * `recordingExport` cap — render a footage time range into a single downloadable
23542
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
23543
+ * bounded lifetime with a durable history, auto-expiry, and optional
23544
+ * delete-after-download.
23545
+ *
23546
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
23547
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
23548
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
23549
+ * input and dispatch to that single provider; the render runs on the node that
23550
+ * owns the footage (no cross-node segment transfer). Download rides the
23551
+ * framework addon data-plane. State persists in a DurableState blob (NOT
23552
+ * SQLite); history rows survive file deletion for audit.
23553
+ */
23554
+ /** Playback-speed multiplier for the render (1 = realtime). */
23555
+ var ExportSpeedSchema = number().min(.25).max(32);
23556
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
23557
+ var ExportTimelapseSchema = object({
23558
+ everyMs: number().int().positive(),
23559
+ outputFps: number().int().min(1).max(60).optional()
23560
+ });
23561
+ /**
23562
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
23563
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
23564
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
23565
+ * `deleteAfterDownload` removes it shortly after the first complete download.
23566
+ */
23567
+ var ExportOptionsSchema = object({
23568
+ speed: ExportSpeedSchema.optional(),
23569
+ timelapse: ExportTimelapseSchema.optional(),
23570
+ includeAudio: boolean(),
23571
+ maxLifeMs: number().int().positive(),
23572
+ deleteAfterDownload: boolean(),
23573
+ title: string().max(200).optional()
23574
+ }).superRefine((v, ctx) => {
23575
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
23576
+ code: ZodIssueCode.custom,
23577
+ message: "speed and timelapse are mutually exclusive",
23578
+ path: ["timelapse"]
23579
+ });
23580
+ });
23581
+ var ExportStateSchema = _enum([
23582
+ "queued",
23583
+ "rendering",
23584
+ "ready",
23585
+ "failed",
23586
+ "expired",
23587
+ "deleted"
23588
+ ]);
23589
+ /** One export job / history row. */
23590
+ var ExportRecordSchema = object({
23591
+ id: string(),
23592
+ deviceId: number(),
23593
+ profile: string(),
23594
+ fromMs: number(),
23595
+ toMs: number(),
23596
+ options: ExportOptionsSchema,
23597
+ state: ExportStateSchema,
23598
+ /** 0–100 while rendering; null otherwise. */
23599
+ progressPct: number().nullable(),
23600
+ /** File size once ready; null before. */
23601
+ fileBytes: number().nullable(),
23602
+ expiresAt: number(),
23603
+ deleteAfterDownload: boolean(),
23604
+ /** Epoch of the first complete download; null until then. */
23605
+ downloadedAt: number().nullable(),
23606
+ createdAt: number(),
23607
+ /** User id/name that requested the export. */
23608
+ createdBy: string(),
23609
+ /** Failure reason when state is 'failed'; null otherwise. */
23610
+ error: string().nullable()
23611
+ });
23612
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
23613
+ var ExportDownloadSchema = object({
23614
+ url: string(),
23615
+ endpoints: array(string())
23616
+ });
23617
+ method(object({
23618
+ deviceId: number(),
23619
+ profile: string(),
23620
+ fromMs: number(),
23621
+ toMs: number(),
23622
+ options: ExportOptionsSchema
23623
+ }), ExportRecordSchema, {
23624
+ kind: "mutation",
23625
+ auth: "protected"
23626
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
23627
+ kind: "query",
23628
+ auth: "protected"
23629
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23630
+ kind: "query",
23631
+ auth: "protected"
23632
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23633
+ kind: "mutation",
23634
+ auth: "protected"
23635
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23636
+ kind: "mutation",
23637
+ auth: "protected"
23638
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
23639
+ kind: "query",
23640
+ auth: "protected"
23374
23641
  });
23375
23642
  /**
23376
23643
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -26390,6 +26657,12 @@ Object.freeze({
26390
26657
  addonId: null,
26391
26658
  access: "delete"
26392
26659
  },
26660
+ "pipelineAnalytics.deleteDeviceEvents": {
26661
+ capName: "pipeline-analytics",
26662
+ capScope: "device",
26663
+ addonId: null,
26664
+ access: "delete"
26665
+ },
26393
26666
  "pipelineAnalytics.deleteTracks": {
26394
26667
  capName: "pipeline-analytics",
26395
26668
  capScope: "device",
@@ -26420,6 +26693,12 @@ Object.freeze({
26420
26693
  addonId: null,
26421
26694
  access: "view"
26422
26695
  },
26696
+ "pipelineAnalytics.getEventStoreFootprint": {
26697
+ capName: "pipeline-analytics",
26698
+ capScope: "device",
26699
+ addonId: null,
26700
+ access: "view"
26701
+ },
26423
26702
  "pipelineAnalytics.getKeyEvents": {
26424
26703
  capName: "pipeline-analytics",
26425
26704
  capScope: "device",
@@ -26462,6 +26741,12 @@ Object.freeze({
26462
26741
  addonId: null,
26463
26742
  access: "view"
26464
26743
  },
26744
+ "pipelineAnalytics.listOpsLog": {
26745
+ capName: "pipeline-analytics",
26746
+ capScope: "device",
26747
+ addonId: null,
26748
+ access: "view"
26749
+ },
26465
26750
  "pipelineAnalytics.listRecentTracks": {
26466
26751
  capName: "pipeline-analytics",
26467
26752
  capScope: "device",
@@ -26474,6 +26759,12 @@ Object.freeze({
26474
26759
  addonId: null,
26475
26760
  access: "view"
26476
26761
  },
26762
+ "pipelineAnalytics.pruneEvents": {
26763
+ capName: "pipeline-analytics",
26764
+ capScope: "device",
26765
+ addonId: null,
26766
+ access: "create"
26767
+ },
26477
26768
  "pipelineAnalytics.pruneEventsBefore": {
26478
26769
  capName: "pipeline-analytics",
26479
26770
  capScope: "device",
@@ -27236,6 +27527,12 @@ Object.freeze({
27236
27527
  addonId: null,
27237
27528
  access: "create"
27238
27529
  },
27530
+ "recording.deleteFootprint": {
27531
+ capName: "recording",
27532
+ capScope: "system",
27533
+ addonId: null,
27534
+ access: "delete"
27535
+ },
27239
27536
  "recording.getAvailability": {
27240
27537
  capName: "recording",
27241
27538
  capScope: "system",
@@ -27266,6 +27563,12 @@ Object.freeze({
27266
27563
  addonId: null,
27267
27564
  access: "view"
27268
27565
  },
27566
+ "recording.listOpsLog": {
27567
+ capName: "recording",
27568
+ capScope: "system",
27569
+ addonId: null,
27570
+ access: "view"
27571
+ },
27269
27572
  "recording.locateSegment": {
27270
27573
  capName: "recording",
27271
27574
  capScope: "system",
@@ -27296,6 +27599,42 @@ Object.freeze({
27296
27599
  addonId: null,
27297
27600
  access: "create"
27298
27601
  },
27602
+ "recordingExport.cancelExport": {
27603
+ capName: "recordingExport",
27604
+ capScope: "system",
27605
+ addonId: null,
27606
+ access: "create"
27607
+ },
27608
+ "recordingExport.createExport": {
27609
+ capName: "recordingExport",
27610
+ capScope: "system",
27611
+ addonId: null,
27612
+ access: "create"
27613
+ },
27614
+ "recordingExport.deleteExport": {
27615
+ capName: "recordingExport",
27616
+ capScope: "system",
27617
+ addonId: null,
27618
+ access: "delete"
27619
+ },
27620
+ "recordingExport.getDownloadUrl": {
27621
+ capName: "recordingExport",
27622
+ capScope: "system",
27623
+ addonId: null,
27624
+ access: "view"
27625
+ },
27626
+ "recordingExport.getExport": {
27627
+ capName: "recordingExport",
27628
+ capScope: "system",
27629
+ addonId: null,
27630
+ access: "view"
27631
+ },
27632
+ "recordingExport.listExports": {
27633
+ capName: "recordingExport",
27634
+ capScope: "system",
27635
+ addonId: null,
27636
+ access: "view"
27637
+ },
27299
27638
  "sceneMonitor.captureReference": {
27300
27639
  capName: "scene-monitor",
27301
27640
  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",
@@ -8299,6 +8342,62 @@ var RecordingConfigSchema = object({
8299
8342
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
8300
8343
  });
8301
8344
  /**
8345
+ * Ops-log — the durable, append-only operations audit shared by the
8346
+ * recordings and events management surfaces.
8347
+ *
8348
+ * ONE row shape is reused for both domains so a single "Activity" view can
8349
+ * merge the recorder's DurableState ring (recordings ops-log) and the
8350
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
8351
+ * management operation, WHY it ran (reason), and its measurable effect
8352
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
8353
+ * never fail the operation it records.
8354
+ */
8355
+ /** Which management domain the operation belongs to. */
8356
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
8357
+ /** The kind of management operation performed. */
8358
+ var OpsLogOpSchema = _enum([
8359
+ "prune",
8360
+ "manual-delete",
8361
+ "rescan",
8362
+ "retention-run"
8363
+ ]);
8364
+ /** Why the operation ran. */
8365
+ var OpsLogReasonSchema = _enum([
8366
+ "retention",
8367
+ "quota",
8368
+ "manual",
8369
+ "operator"
8370
+ ]);
8371
+ /** One audit row, shared verbatim by both domains. */
8372
+ var OpsLogEntrySchema = object({
8373
+ /** Unique row id. */
8374
+ id: string(),
8375
+ /** Epoch ms the operation completed. */
8376
+ at: number(),
8377
+ domain: OpsLogDomainSchema,
8378
+ op: OpsLogOpSchema,
8379
+ reason: OpsLogReasonSchema,
8380
+ /** The camera the op targeted; null for a cluster/global op. */
8381
+ deviceId: number().nullable(),
8382
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
8383
+ nodeId: string(),
8384
+ /** Buckets / rows deleted (op-specific unit). */
8385
+ itemsAffected: number(),
8386
+ /** Bytes reclaimed by the op (0 when not measurable). */
8387
+ bytesReclaimed: number(),
8388
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
8389
+ detail: string().nullable(),
8390
+ /** Who/what triggered the op. */
8391
+ actor: string()
8392
+ });
8393
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
8394
+ var OpsLogQueryInputSchema = object({
8395
+ /** Restrict to a single camera; omit for every row. */
8396
+ deviceId: number().optional(),
8397
+ /** Max rows returned, newest-first. */
8398
+ limit: number().int().min(1).max(1e3).optional()
8399
+ });
8400
+ /**
8302
8401
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8303
8402
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8304
8403
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -15654,7 +15753,11 @@ array(ZoneRuleSchema).readonly();
15654
15753
  * Extend the enum here when a new gating consumer comes online (audio
15655
15754
  * gating, alert filtering, …) — no other surface needs to change.
15656
15755
  */
15657
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
15756
+ var ZoneRuleStageEnum = _enum([
15757
+ "motion",
15758
+ "detection",
15759
+ "package"
15760
+ ]);
15658
15761
  /**
15659
15762
  * Runtime registry: cap-property-name → cap definition. `BaseDevice`'s
15660
15763
  * `state` getter looks up the cap definition here to construct a
@@ -15748,16 +15851,25 @@ var DEVICE_LOCAL_STATE_CAPS = {
15748
15851
  })
15749
15852
  },
15750
15853
  /**
15751
- * Runtime-state slice — both stages mirrored together so consumers
15854
+ * Runtime-state slice — every stage mirrored together so consumers
15752
15855
  * see one reactive handle (`device.state.zoneRules.value`) instead
15753
- * of two. Bulk-replace mutations on either stage write the full
15754
- * `{motion, detection}` shape, so subscribers always get the
15856
+ * of one per stage. Bulk-replace mutations on any stage write the full
15857
+ * `{motion, detection, package}` shape, so subscribers always get the
15755
15858
  * complete current set. Consumers that only care about one stage
15756
15859
  * just read the matching property.
15860
+ *
15861
+ * `package` backs the package-drop detector — a package zone is a
15862
+ * `ZoneRule` on the `'package'` stage referencing drawn polygons
15863
+ * (see docs/superpowers/specs/2026-07-17-package-zones-design.md §3.1).
15864
+ * The orchestrator provider writes this stage as a first-class slice
15865
+ * (Phase 4): every mutation mirrors the full `{motion, detection,
15866
+ * package}` shape, so consumers read the current package rules directly
15867
+ * off `device.state.zoneRules.value.package`.
15757
15868
  */
15758
15869
  runtimeState: object({
15759
15870
  motion: array(ZoneRuleSchema).readonly(),
15760
- detection: array(ZoneRuleSchema).readonly()
15871
+ detection: array(ZoneRuleSchema).readonly(),
15872
+ package: array(ZoneRuleSchema).readonly()
15761
15873
  })
15762
15874
  },
15763
15875
  zones: zonesCapability
@@ -19719,6 +19831,7 @@ var EventKindIconSchema = _enum([
19719
19831
  "smoke",
19720
19832
  "water",
19721
19833
  "button",
19834
+ "package",
19722
19835
  "generic"
19723
19836
  ]);
19724
19837
  var EventKindCategorySchema = _enum([
@@ -19726,7 +19839,8 @@ var EventKindCategorySchema = _enum([
19726
19839
  "audio",
19727
19840
  "detection",
19728
19841
  "sensor",
19729
- "custom"
19842
+ "custom",
19843
+ "package"
19730
19844
  ]);
19731
19845
  var EventKindDescriptorSchema = object({
19732
19846
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -20067,6 +20181,26 @@ var TrackCascadeCountsSchema = object({
20067
20181
  /** Per-track CLIP search vectors removed (best-effort). */
20068
20182
  embeddings: number().int()
20069
20183
  });
20184
+ /** Event-store footprint for one camera. */
20185
+ var EventStoreDeviceFootprintSchema = object({
20186
+ deviceId: number(),
20187
+ /** Persisted event rows (motion + object + audio) for the camera. */
20188
+ rows: number().int(),
20189
+ /** Event-owned media bytes on disk for the camera. */
20190
+ bytes: number().int()
20191
+ });
20192
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
20193
+ var EventStoreFootprintSchema = object({
20194
+ totalRows: number().int(),
20195
+ totalBytes: number().int(),
20196
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
20197
+ });
20198
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
20199
+ var EventPruneCountsSchema = object({
20200
+ motion: number().int(),
20201
+ object: number().int(),
20202
+ audio: number().int()
20203
+ });
20070
20204
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20071
20205
  deviceId: number(),
20072
20206
  trackId: string()
@@ -20130,6 +20264,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20130
20264
  }), {
20131
20265
  kind: "mutation",
20132
20266
  auth: "admin"
20267
+ }), method(object({}), EventStoreFootprintSchema, {
20268
+ kind: "query",
20269
+ auth: "admin"
20270
+ }), method(object({
20271
+ olderThanMs: number(),
20272
+ reason: OpsLogReasonSchema.optional()
20273
+ }), EventPruneCountsSchema, {
20274
+ kind: "mutation",
20275
+ auth: "admin"
20276
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
20277
+ kind: "mutation",
20278
+ auth: "admin"
20279
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20280
+ kind: "query",
20281
+ auth: "admin"
20133
20282
  }), method(object({
20134
20283
  eventId: string(),
20135
20284
  kind: MediaFileKindEnum.optional()
@@ -23363,13 +23512,131 @@ method(object({
23363
23512
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
23364
23513
  kind: "mutation",
23365
23514
  auth: "admin"
23366
- }), method(object({ deviceId: number() }), object({
23515
+ }), method(object({
23516
+ deviceId: number(),
23517
+ reason: OpsLogReasonSchema.optional()
23518
+ }), object({
23367
23519
  floorMs: number().nullable(),
23368
23520
  deletedBuckets: number().int(),
23369
23521
  reclaimedBytes: number().int()
23370
23522
  }), {
23371
23523
  kind: "mutation",
23372
23524
  auth: "admin"
23525
+ }), method(object({
23526
+ deviceId: number(),
23527
+ fromMs: number().optional(),
23528
+ toMs: number().optional()
23529
+ }), object({
23530
+ deletedBuckets: number().int(),
23531
+ reclaimedBytes: number().int()
23532
+ }), {
23533
+ kind: "mutation",
23534
+ auth: "admin"
23535
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
23536
+ kind: "query",
23537
+ auth: "admin"
23538
+ });
23539
+ /**
23540
+ * `recordingExport` cap — render a footage time range into a single downloadable
23541
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
23542
+ * bounded lifetime with a durable history, auto-expiry, and optional
23543
+ * delete-after-download.
23544
+ *
23545
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
23546
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
23547
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
23548
+ * input and dispatch to that single provider; the render runs on the node that
23549
+ * owns the footage (no cross-node segment transfer). Download rides the
23550
+ * framework addon data-plane. State persists in a DurableState blob (NOT
23551
+ * SQLite); history rows survive file deletion for audit.
23552
+ */
23553
+ /** Playback-speed multiplier for the render (1 = realtime). */
23554
+ var ExportSpeedSchema = number().min(.25).max(32);
23555
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
23556
+ var ExportTimelapseSchema = object({
23557
+ everyMs: number().int().positive(),
23558
+ outputFps: number().int().min(1).max(60).optional()
23559
+ });
23560
+ /**
23561
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
23562
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
23563
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
23564
+ * `deleteAfterDownload` removes it shortly after the first complete download.
23565
+ */
23566
+ var ExportOptionsSchema = object({
23567
+ speed: ExportSpeedSchema.optional(),
23568
+ timelapse: ExportTimelapseSchema.optional(),
23569
+ includeAudio: boolean(),
23570
+ maxLifeMs: number().int().positive(),
23571
+ deleteAfterDownload: boolean(),
23572
+ title: string().max(200).optional()
23573
+ }).superRefine((v, ctx) => {
23574
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
23575
+ code: ZodIssueCode.custom,
23576
+ message: "speed and timelapse are mutually exclusive",
23577
+ path: ["timelapse"]
23578
+ });
23579
+ });
23580
+ var ExportStateSchema = _enum([
23581
+ "queued",
23582
+ "rendering",
23583
+ "ready",
23584
+ "failed",
23585
+ "expired",
23586
+ "deleted"
23587
+ ]);
23588
+ /** One export job / history row. */
23589
+ var ExportRecordSchema = object({
23590
+ id: string(),
23591
+ deviceId: number(),
23592
+ profile: string(),
23593
+ fromMs: number(),
23594
+ toMs: number(),
23595
+ options: ExportOptionsSchema,
23596
+ state: ExportStateSchema,
23597
+ /** 0–100 while rendering; null otherwise. */
23598
+ progressPct: number().nullable(),
23599
+ /** File size once ready; null before. */
23600
+ fileBytes: number().nullable(),
23601
+ expiresAt: number(),
23602
+ deleteAfterDownload: boolean(),
23603
+ /** Epoch of the first complete download; null until then. */
23604
+ downloadedAt: number().nullable(),
23605
+ createdAt: number(),
23606
+ /** User id/name that requested the export. */
23607
+ createdBy: string(),
23608
+ /** Failure reason when state is 'failed'; null otherwise. */
23609
+ error: string().nullable()
23610
+ });
23611
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
23612
+ var ExportDownloadSchema = object({
23613
+ url: string(),
23614
+ endpoints: array(string())
23615
+ });
23616
+ method(object({
23617
+ deviceId: number(),
23618
+ profile: string(),
23619
+ fromMs: number(),
23620
+ toMs: number(),
23621
+ options: ExportOptionsSchema
23622
+ }), ExportRecordSchema, {
23623
+ kind: "mutation",
23624
+ auth: "protected"
23625
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
23626
+ kind: "query",
23627
+ auth: "protected"
23628
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23629
+ kind: "query",
23630
+ auth: "protected"
23631
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23632
+ kind: "mutation",
23633
+ auth: "protected"
23634
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
23635
+ kind: "mutation",
23636
+ auth: "protected"
23637
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
23638
+ kind: "query",
23639
+ auth: "protected"
23373
23640
  });
23374
23641
  /**
23375
23642
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -26389,6 +26656,12 @@ Object.freeze({
26389
26656
  addonId: null,
26390
26657
  access: "delete"
26391
26658
  },
26659
+ "pipelineAnalytics.deleteDeviceEvents": {
26660
+ capName: "pipeline-analytics",
26661
+ capScope: "device",
26662
+ addonId: null,
26663
+ access: "delete"
26664
+ },
26392
26665
  "pipelineAnalytics.deleteTracks": {
26393
26666
  capName: "pipeline-analytics",
26394
26667
  capScope: "device",
@@ -26419,6 +26692,12 @@ Object.freeze({
26419
26692
  addonId: null,
26420
26693
  access: "view"
26421
26694
  },
26695
+ "pipelineAnalytics.getEventStoreFootprint": {
26696
+ capName: "pipeline-analytics",
26697
+ capScope: "device",
26698
+ addonId: null,
26699
+ access: "view"
26700
+ },
26422
26701
  "pipelineAnalytics.getKeyEvents": {
26423
26702
  capName: "pipeline-analytics",
26424
26703
  capScope: "device",
@@ -26461,6 +26740,12 @@ Object.freeze({
26461
26740
  addonId: null,
26462
26741
  access: "view"
26463
26742
  },
26743
+ "pipelineAnalytics.listOpsLog": {
26744
+ capName: "pipeline-analytics",
26745
+ capScope: "device",
26746
+ addonId: null,
26747
+ access: "view"
26748
+ },
26464
26749
  "pipelineAnalytics.listRecentTracks": {
26465
26750
  capName: "pipeline-analytics",
26466
26751
  capScope: "device",
@@ -26473,6 +26758,12 @@ Object.freeze({
26473
26758
  addonId: null,
26474
26759
  access: "view"
26475
26760
  },
26761
+ "pipelineAnalytics.pruneEvents": {
26762
+ capName: "pipeline-analytics",
26763
+ capScope: "device",
26764
+ addonId: null,
26765
+ access: "create"
26766
+ },
26476
26767
  "pipelineAnalytics.pruneEventsBefore": {
26477
26768
  capName: "pipeline-analytics",
26478
26769
  capScope: "device",
@@ -27235,6 +27526,12 @@ Object.freeze({
27235
27526
  addonId: null,
27236
27527
  access: "create"
27237
27528
  },
27529
+ "recording.deleteFootprint": {
27530
+ capName: "recording",
27531
+ capScope: "system",
27532
+ addonId: null,
27533
+ access: "delete"
27534
+ },
27238
27535
  "recording.getAvailability": {
27239
27536
  capName: "recording",
27240
27537
  capScope: "system",
@@ -27265,6 +27562,12 @@ Object.freeze({
27265
27562
  addonId: null,
27266
27563
  access: "view"
27267
27564
  },
27565
+ "recording.listOpsLog": {
27566
+ capName: "recording",
27567
+ capScope: "system",
27568
+ addonId: null,
27569
+ access: "view"
27570
+ },
27268
27571
  "recording.locateSegment": {
27269
27572
  capName: "recording",
27270
27573
  capScope: "system",
@@ -27295,6 +27598,42 @@ Object.freeze({
27295
27598
  addonId: null,
27296
27599
  access: "create"
27297
27600
  },
27601
+ "recordingExport.cancelExport": {
27602
+ capName: "recordingExport",
27603
+ capScope: "system",
27604
+ addonId: null,
27605
+ access: "create"
27606
+ },
27607
+ "recordingExport.createExport": {
27608
+ capName: "recordingExport",
27609
+ capScope: "system",
27610
+ addonId: null,
27611
+ access: "create"
27612
+ },
27613
+ "recordingExport.deleteExport": {
27614
+ capName: "recordingExport",
27615
+ capScope: "system",
27616
+ addonId: null,
27617
+ access: "delete"
27618
+ },
27619
+ "recordingExport.getDownloadUrl": {
27620
+ capName: "recordingExport",
27621
+ capScope: "system",
27622
+ addonId: null,
27623
+ access: "view"
27624
+ },
27625
+ "recordingExport.getExport": {
27626
+ capName: "recordingExport",
27627
+ capScope: "system",
27628
+ addonId: null,
27629
+ access: "view"
27630
+ },
27631
+ "recordingExport.listExports": {
27632
+ capName: "recordingExport",
27633
+ capScope: "system",
27634
+ addonId: null,
27635
+ access: "view"
27636
+ },
27298
27637
  "sceneMonitor.captureReference": {
27299
27638
  capName: "scene-monitor",
27300
27639
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
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",