@camstack/addon-export-hap 1.1.25 → 1.1.27

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.
@@ -35,7 +35,7 @@ let node_fs_promises = require("node:fs/promises");
35
35
  node_fs_promises = __toESM(node_fs_promises);
36
36
  let node_dgram = require("node:dgram");
37
37
  let node_os = require("node:os");
38
- //#region ../types/dist/event-category-H4AVePnn.mjs
38
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
39
39
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
40
  EventCategory["SystemBoot"] = "system.boot";
41
41
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -188,6 +188,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
188
188
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
189
189
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
190
190
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
191
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
192
+ * progress bar the client reconciles via `recordingExport.getExport`. */
193
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
194
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
195
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
191
196
  EventCategory["DetectionEvent"] = "detection.event";
192
197
  EventCategory["SessionTrackNew"] = "session.track.new";
193
198
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -477,6 +482,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
477
482
  */
478
483
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
479
484
  /**
485
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
486
+ * a package zone — a newly-appeared stationary object of a package class
487
+ * that cleared the class / zone / dwell / size gates. Payload:
488
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
489
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
490
+ * Telemetry (D8): the durable record is the `package-events` store row;
491
+ * this bus topic drives notifier rules + live UI. See
492
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
493
+ */
494
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
495
+ /**
496
+ * Fired by `addon-post-analysis` when a previously-delivered package
497
+ * leaves its zone (the stationary entry departed — moved or swept).
498
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
499
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
500
+ * Telemetry (D8). See package-zones-design §5.2.
501
+ */
502
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
503
+ /**
480
504
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
481
505
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
482
506
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5215,6 +5239,25 @@ function _instanceof(cls, params = {}) {
5215
5239
  };
5216
5240
  return inst;
5217
5241
  }
5242
+ //#endregion
5243
+ //#region ../../node_modules/zod/v4/classic/compat.js
5244
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5245
+ var ZodIssueCode = {
5246
+ invalid_type: "invalid_type",
5247
+ too_big: "too_big",
5248
+ too_small: "too_small",
5249
+ invalid_format: "invalid_format",
5250
+ not_multiple_of: "not_multiple_of",
5251
+ unrecognized_keys: "unrecognized_keys",
5252
+ invalid_union: "invalid_union",
5253
+ invalid_key: "invalid_key",
5254
+ invalid_element: "invalid_element",
5255
+ invalid_value: "invalid_value",
5256
+ custom: "custom"
5257
+ };
5258
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5259
+ var ZodFirstPartyTypeKind;
5260
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5218
5261
  Object.fromEntries([
5219
5262
  {
5220
5263
  id: "overview",
@@ -7321,6 +7364,62 @@ var RecordingConfigSchema = object({
7321
7364
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7322
7365
  });
7323
7366
  /**
7367
+ * Ops-log — the durable, append-only operations audit shared by the
7368
+ * recordings and events management surfaces.
7369
+ *
7370
+ * ONE row shape is reused for both domains so a single "Activity" view can
7371
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7372
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7373
+ * management operation, WHY it ran (reason), and its measurable effect
7374
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7375
+ * never fail the operation it records.
7376
+ */
7377
+ /** Which management domain the operation belongs to. */
7378
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7379
+ /** The kind of management operation performed. */
7380
+ var OpsLogOpSchema = _enum([
7381
+ "prune",
7382
+ "manual-delete",
7383
+ "rescan",
7384
+ "retention-run"
7385
+ ]);
7386
+ /** Why the operation ran. */
7387
+ var OpsLogReasonSchema = _enum([
7388
+ "retention",
7389
+ "quota",
7390
+ "manual",
7391
+ "operator"
7392
+ ]);
7393
+ /** One audit row, shared verbatim by both domains. */
7394
+ var OpsLogEntrySchema = object({
7395
+ /** Unique row id. */
7396
+ id: string(),
7397
+ /** Epoch ms the operation completed. */
7398
+ at: number(),
7399
+ domain: OpsLogDomainSchema,
7400
+ op: OpsLogOpSchema,
7401
+ reason: OpsLogReasonSchema,
7402
+ /** The camera the op targeted; null for a cluster/global op. */
7403
+ deviceId: number().nullable(),
7404
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7405
+ nodeId: string(),
7406
+ /** Buckets / rows deleted (op-specific unit). */
7407
+ itemsAffected: number(),
7408
+ /** Bytes reclaimed by the op (0 when not measurable). */
7409
+ bytesReclaimed: number(),
7410
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7411
+ detail: string().nullable(),
7412
+ /** Who/what triggered the op. */
7413
+ actor: string()
7414
+ });
7415
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7416
+ var OpsLogQueryInputSchema = object({
7417
+ /** Restrict to a single camera; omit for every row. */
7418
+ deviceId: number().optional(),
7419
+ /** Max rows returned, newest-first. */
7420
+ limit: number().int().min(1).max(1e3).optional()
7421
+ });
7422
+ /**
7324
7423
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7325
7424
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7326
7425
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -12624,7 +12723,11 @@ array(ZoneRuleSchema).readonly();
12624
12723
  * Extend the enum here when a new gating consumer comes online (audio
12625
12724
  * gating, alert filtering, …) — no other surface needs to change.
12626
12725
  */
12627
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
12726
+ var ZoneRuleStageEnum = _enum([
12727
+ "motion",
12728
+ "detection",
12729
+ "package"
12730
+ ]);
12628
12731
  DeviceType.Camera, method(object({
12629
12732
  deviceId: number(),
12630
12733
  stage: ZoneRuleStageEnum
@@ -12637,7 +12740,8 @@ DeviceType.Camera, method(object({
12637
12740
  auth: "admin"
12638
12741
  }), object({
12639
12742
  motion: array(ZoneRuleSchema).readonly(),
12640
- detection: array(ZoneRuleSchema).readonly()
12743
+ detection: array(ZoneRuleSchema).readonly(),
12744
+ package: array(ZoneRuleSchema).readonly()
12641
12745
  });
12642
12746
  var ProviderStatusSchema = object({
12643
12747
  connected: boolean(),
@@ -15845,6 +15949,7 @@ var EventKindIconSchema = _enum([
15845
15949
  "smoke",
15846
15950
  "water",
15847
15951
  "button",
15952
+ "package",
15848
15953
  "generic"
15849
15954
  ]);
15850
15955
  var EventKindCategorySchema = _enum([
@@ -15852,7 +15957,8 @@ var EventKindCategorySchema = _enum([
15852
15957
  "audio",
15853
15958
  "detection",
15854
15959
  "sensor",
15855
- "custom"
15960
+ "custom",
15961
+ "package"
15856
15962
  ]);
15857
15963
  var EventKindDescriptorSchema = object({
15858
15964
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -16193,6 +16299,26 @@ var TrackCascadeCountsSchema = object({
16193
16299
  /** Per-track CLIP search vectors removed (best-effort). */
16194
16300
  embeddings: number().int()
16195
16301
  });
16302
+ /** Event-store footprint for one camera. */
16303
+ var EventStoreDeviceFootprintSchema = object({
16304
+ deviceId: number(),
16305
+ /** Persisted event rows (motion + object + audio) for the camera. */
16306
+ rows: number().int(),
16307
+ /** Event-owned media bytes on disk for the camera. */
16308
+ bytes: number().int()
16309
+ });
16310
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
16311
+ var EventStoreFootprintSchema = object({
16312
+ totalRows: number().int(),
16313
+ totalBytes: number().int(),
16314
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
16315
+ });
16316
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
16317
+ var EventPruneCountsSchema = object({
16318
+ motion: number().int(),
16319
+ object: number().int(),
16320
+ audio: number().int()
16321
+ });
16196
16322
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
16197
16323
  deviceId: number(),
16198
16324
  trackId: string()
@@ -16256,6 +16382,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16256
16382
  }), {
16257
16383
  kind: "mutation",
16258
16384
  auth: "admin"
16385
+ }), method(object({}), EventStoreFootprintSchema, {
16386
+ kind: "query",
16387
+ auth: "admin"
16388
+ }), method(object({
16389
+ olderThanMs: number(),
16390
+ reason: OpsLogReasonSchema.optional()
16391
+ }), EventPruneCountsSchema, {
16392
+ kind: "mutation",
16393
+ auth: "admin"
16394
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
16395
+ kind: "mutation",
16396
+ auth: "admin"
16397
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
16398
+ kind: "query",
16399
+ auth: "admin"
16259
16400
  }), method(object({
16260
16401
  eventId: string(),
16261
16402
  kind: MediaFileKindEnum.optional()
@@ -19489,13 +19630,131 @@ method(object({
19489
19630
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
19490
19631
  kind: "mutation",
19491
19632
  auth: "admin"
19492
- }), method(object({ deviceId: number() }), object({
19633
+ }), method(object({
19634
+ deviceId: number(),
19635
+ reason: OpsLogReasonSchema.optional()
19636
+ }), object({
19493
19637
  floorMs: number().nullable(),
19494
19638
  deletedBuckets: number().int(),
19495
19639
  reclaimedBytes: number().int()
19496
19640
  }), {
19497
19641
  kind: "mutation",
19498
19642
  auth: "admin"
19643
+ }), method(object({
19644
+ deviceId: number(),
19645
+ fromMs: number().optional(),
19646
+ toMs: number().optional()
19647
+ }), object({
19648
+ deletedBuckets: number().int(),
19649
+ reclaimedBytes: number().int()
19650
+ }), {
19651
+ kind: "mutation",
19652
+ auth: "admin"
19653
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19654
+ kind: "query",
19655
+ auth: "admin"
19656
+ });
19657
+ /**
19658
+ * `recordingExport` cap — render a footage time range into a single downloadable
19659
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
19660
+ * bounded lifetime with a durable history, auto-expiry, and optional
19661
+ * delete-after-download.
19662
+ *
19663
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
19664
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
19665
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
19666
+ * input and dispatch to that single provider; the render runs on the node that
19667
+ * owns the footage (no cross-node segment transfer). Download rides the
19668
+ * framework addon data-plane. State persists in a DurableState blob (NOT
19669
+ * SQLite); history rows survive file deletion for audit.
19670
+ */
19671
+ /** Playback-speed multiplier for the render (1 = realtime). */
19672
+ var ExportSpeedSchema = number().min(.25).max(32);
19673
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
19674
+ var ExportTimelapseSchema = object({
19675
+ everyMs: number().int().positive(),
19676
+ outputFps: number().int().min(1).max(60).optional()
19677
+ });
19678
+ /**
19679
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
19680
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
19681
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
19682
+ * `deleteAfterDownload` removes it shortly after the first complete download.
19683
+ */
19684
+ var ExportOptionsSchema = object({
19685
+ speed: ExportSpeedSchema.optional(),
19686
+ timelapse: ExportTimelapseSchema.optional(),
19687
+ includeAudio: boolean(),
19688
+ maxLifeMs: number().int().positive(),
19689
+ deleteAfterDownload: boolean(),
19690
+ title: string().max(200).optional()
19691
+ }).superRefine((v, ctx) => {
19692
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
19693
+ code: ZodIssueCode.custom,
19694
+ message: "speed and timelapse are mutually exclusive",
19695
+ path: ["timelapse"]
19696
+ });
19697
+ });
19698
+ var ExportStateSchema = _enum([
19699
+ "queued",
19700
+ "rendering",
19701
+ "ready",
19702
+ "failed",
19703
+ "expired",
19704
+ "deleted"
19705
+ ]);
19706
+ /** One export job / history row. */
19707
+ var ExportRecordSchema = object({
19708
+ id: string(),
19709
+ deviceId: number(),
19710
+ profile: string(),
19711
+ fromMs: number(),
19712
+ toMs: number(),
19713
+ options: ExportOptionsSchema,
19714
+ state: ExportStateSchema,
19715
+ /** 0–100 while rendering; null otherwise. */
19716
+ progressPct: number().nullable(),
19717
+ /** File size once ready; null before. */
19718
+ fileBytes: number().nullable(),
19719
+ expiresAt: number(),
19720
+ deleteAfterDownload: boolean(),
19721
+ /** Epoch of the first complete download; null until then. */
19722
+ downloadedAt: number().nullable(),
19723
+ createdAt: number(),
19724
+ /** User id/name that requested the export. */
19725
+ createdBy: string(),
19726
+ /** Failure reason when state is 'failed'; null otherwise. */
19727
+ error: string().nullable()
19728
+ });
19729
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
19730
+ var ExportDownloadSchema = object({
19731
+ url: string(),
19732
+ endpoints: array(string())
19733
+ });
19734
+ method(object({
19735
+ deviceId: number(),
19736
+ profile: string(),
19737
+ fromMs: number(),
19738
+ toMs: number(),
19739
+ options: ExportOptionsSchema
19740
+ }), ExportRecordSchema, {
19741
+ kind: "mutation",
19742
+ auth: "protected"
19743
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
19744
+ kind: "query",
19745
+ auth: "protected"
19746
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19747
+ kind: "query",
19748
+ auth: "protected"
19749
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19750
+ kind: "mutation",
19751
+ auth: "protected"
19752
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19753
+ kind: "mutation",
19754
+ auth: "protected"
19755
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
19756
+ kind: "query",
19757
+ auth: "protected"
19499
19758
  });
19500
19759
  /**
19501
19760
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -22515,6 +22774,12 @@ Object.freeze({
22515
22774
  addonId: null,
22516
22775
  access: "delete"
22517
22776
  },
22777
+ "pipelineAnalytics.deleteDeviceEvents": {
22778
+ capName: "pipeline-analytics",
22779
+ capScope: "device",
22780
+ addonId: null,
22781
+ access: "delete"
22782
+ },
22518
22783
  "pipelineAnalytics.deleteTracks": {
22519
22784
  capName: "pipeline-analytics",
22520
22785
  capScope: "device",
@@ -22545,6 +22810,12 @@ Object.freeze({
22545
22810
  addonId: null,
22546
22811
  access: "view"
22547
22812
  },
22813
+ "pipelineAnalytics.getEventStoreFootprint": {
22814
+ capName: "pipeline-analytics",
22815
+ capScope: "device",
22816
+ addonId: null,
22817
+ access: "view"
22818
+ },
22548
22819
  "pipelineAnalytics.getKeyEvents": {
22549
22820
  capName: "pipeline-analytics",
22550
22821
  capScope: "device",
@@ -22587,6 +22858,12 @@ Object.freeze({
22587
22858
  addonId: null,
22588
22859
  access: "view"
22589
22860
  },
22861
+ "pipelineAnalytics.listOpsLog": {
22862
+ capName: "pipeline-analytics",
22863
+ capScope: "device",
22864
+ addonId: null,
22865
+ access: "view"
22866
+ },
22590
22867
  "pipelineAnalytics.listRecentTracks": {
22591
22868
  capName: "pipeline-analytics",
22592
22869
  capScope: "device",
@@ -22599,6 +22876,12 @@ Object.freeze({
22599
22876
  addonId: null,
22600
22877
  access: "view"
22601
22878
  },
22879
+ "pipelineAnalytics.pruneEvents": {
22880
+ capName: "pipeline-analytics",
22881
+ capScope: "device",
22882
+ addonId: null,
22883
+ access: "create"
22884
+ },
22602
22885
  "pipelineAnalytics.pruneEventsBefore": {
22603
22886
  capName: "pipeline-analytics",
22604
22887
  capScope: "device",
@@ -23361,6 +23644,12 @@ Object.freeze({
23361
23644
  addonId: null,
23362
23645
  access: "create"
23363
23646
  },
23647
+ "recording.deleteFootprint": {
23648
+ capName: "recording",
23649
+ capScope: "system",
23650
+ addonId: null,
23651
+ access: "delete"
23652
+ },
23364
23653
  "recording.getAvailability": {
23365
23654
  capName: "recording",
23366
23655
  capScope: "system",
@@ -23391,6 +23680,12 @@ Object.freeze({
23391
23680
  addonId: null,
23392
23681
  access: "view"
23393
23682
  },
23683
+ "recording.listOpsLog": {
23684
+ capName: "recording",
23685
+ capScope: "system",
23686
+ addonId: null,
23687
+ access: "view"
23688
+ },
23394
23689
  "recording.locateSegment": {
23395
23690
  capName: "recording",
23396
23691
  capScope: "system",
@@ -23421,6 +23716,42 @@ Object.freeze({
23421
23716
  addonId: null,
23422
23717
  access: "create"
23423
23718
  },
23719
+ "recordingExport.cancelExport": {
23720
+ capName: "recordingExport",
23721
+ capScope: "system",
23722
+ addonId: null,
23723
+ access: "create"
23724
+ },
23725
+ "recordingExport.createExport": {
23726
+ capName: "recordingExport",
23727
+ capScope: "system",
23728
+ addonId: null,
23729
+ access: "create"
23730
+ },
23731
+ "recordingExport.deleteExport": {
23732
+ capName: "recordingExport",
23733
+ capScope: "system",
23734
+ addonId: null,
23735
+ access: "delete"
23736
+ },
23737
+ "recordingExport.getDownloadUrl": {
23738
+ capName: "recordingExport",
23739
+ capScope: "system",
23740
+ addonId: null,
23741
+ access: "view"
23742
+ },
23743
+ "recordingExport.getExport": {
23744
+ capName: "recordingExport",
23745
+ capScope: "system",
23746
+ addonId: null,
23747
+ access: "view"
23748
+ },
23749
+ "recordingExport.listExports": {
23750
+ capName: "recordingExport",
23751
+ capScope: "system",
23752
+ addonId: null,
23753
+ access: "view"
23754
+ },
23424
23755
  "sceneMonitor.captureReference": {
23425
23756
  capName: "scene-monitor",
23426
23757
  capScope: "device",
@@ -10,7 +10,7 @@ import { networkInterfaces } from "node:os";
10
10
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
11
11
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
12
12
  //#endregion
13
- //#region ../types/dist/event-category-H4AVePnn.mjs
13
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
14
14
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
15
15
  EventCategory["SystemBoot"] = "system.boot";
16
16
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -163,6 +163,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
163
163
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
164
164
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
165
165
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
166
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
167
+ * progress bar the client reconciles via `recordingExport.getExport`. */
168
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
169
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
170
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
166
171
  EventCategory["DetectionEvent"] = "detection.event";
167
172
  EventCategory["SessionTrackNew"] = "session.track.new";
168
173
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -452,6 +457,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
452
457
  */
453
458
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
454
459
  /**
460
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
461
+ * a package zone — a newly-appeared stationary object of a package class
462
+ * that cleared the class / zone / dwell / size gates. Payload:
463
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
464
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
465
+ * Telemetry (D8): the durable record is the `package-events` store row;
466
+ * this bus topic drives notifier rules + live UI. See
467
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
468
+ */
469
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
470
+ /**
471
+ * Fired by `addon-post-analysis` when a previously-delivered package
472
+ * leaves its zone (the stationary entry departed — moved or swept).
473
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
474
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
475
+ * Telemetry (D8). See package-zones-design §5.2.
476
+ */
477
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
478
+ /**
455
479
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
456
480
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
457
481
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5190,6 +5214,25 @@ function _instanceof(cls, params = {}) {
5190
5214
  };
5191
5215
  return inst;
5192
5216
  }
5217
+ //#endregion
5218
+ //#region ../../node_modules/zod/v4/classic/compat.js
5219
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5220
+ var ZodIssueCode = {
5221
+ invalid_type: "invalid_type",
5222
+ too_big: "too_big",
5223
+ too_small: "too_small",
5224
+ invalid_format: "invalid_format",
5225
+ not_multiple_of: "not_multiple_of",
5226
+ unrecognized_keys: "unrecognized_keys",
5227
+ invalid_union: "invalid_union",
5228
+ invalid_key: "invalid_key",
5229
+ invalid_element: "invalid_element",
5230
+ invalid_value: "invalid_value",
5231
+ custom: "custom"
5232
+ };
5233
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5234
+ var ZodFirstPartyTypeKind;
5235
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5193
5236
  Object.fromEntries([
5194
5237
  {
5195
5238
  id: "overview",
@@ -7296,6 +7339,62 @@ var RecordingConfigSchema = object({
7296
7339
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7297
7340
  });
7298
7341
  /**
7342
+ * Ops-log — the durable, append-only operations audit shared by the
7343
+ * recordings and events management surfaces.
7344
+ *
7345
+ * ONE row shape is reused for both domains so a single "Activity" view can
7346
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7347
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7348
+ * management operation, WHY it ran (reason), and its measurable effect
7349
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7350
+ * never fail the operation it records.
7351
+ */
7352
+ /** Which management domain the operation belongs to. */
7353
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7354
+ /** The kind of management operation performed. */
7355
+ var OpsLogOpSchema = _enum([
7356
+ "prune",
7357
+ "manual-delete",
7358
+ "rescan",
7359
+ "retention-run"
7360
+ ]);
7361
+ /** Why the operation ran. */
7362
+ var OpsLogReasonSchema = _enum([
7363
+ "retention",
7364
+ "quota",
7365
+ "manual",
7366
+ "operator"
7367
+ ]);
7368
+ /** One audit row, shared verbatim by both domains. */
7369
+ var OpsLogEntrySchema = object({
7370
+ /** Unique row id. */
7371
+ id: string(),
7372
+ /** Epoch ms the operation completed. */
7373
+ at: number(),
7374
+ domain: OpsLogDomainSchema,
7375
+ op: OpsLogOpSchema,
7376
+ reason: OpsLogReasonSchema,
7377
+ /** The camera the op targeted; null for a cluster/global op. */
7378
+ deviceId: number().nullable(),
7379
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7380
+ nodeId: string(),
7381
+ /** Buckets / rows deleted (op-specific unit). */
7382
+ itemsAffected: number(),
7383
+ /** Bytes reclaimed by the op (0 when not measurable). */
7384
+ bytesReclaimed: number(),
7385
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7386
+ detail: string().nullable(),
7387
+ /** Who/what triggered the op. */
7388
+ actor: string()
7389
+ });
7390
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7391
+ var OpsLogQueryInputSchema = object({
7392
+ /** Restrict to a single camera; omit for every row. */
7393
+ deviceId: number().optional(),
7394
+ /** Max rows returned, newest-first. */
7395
+ limit: number().int().min(1).max(1e3).optional()
7396
+ });
7397
+ /**
7299
7398
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7300
7399
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7301
7400
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -12599,7 +12698,11 @@ array(ZoneRuleSchema).readonly();
12599
12698
  * Extend the enum here when a new gating consumer comes online (audio
12600
12699
  * gating, alert filtering, …) — no other surface needs to change.
12601
12700
  */
12602
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
12701
+ var ZoneRuleStageEnum = _enum([
12702
+ "motion",
12703
+ "detection",
12704
+ "package"
12705
+ ]);
12603
12706
  DeviceType.Camera, method(object({
12604
12707
  deviceId: number(),
12605
12708
  stage: ZoneRuleStageEnum
@@ -12612,7 +12715,8 @@ DeviceType.Camera, method(object({
12612
12715
  auth: "admin"
12613
12716
  }), object({
12614
12717
  motion: array(ZoneRuleSchema).readonly(),
12615
- detection: array(ZoneRuleSchema).readonly()
12718
+ detection: array(ZoneRuleSchema).readonly(),
12719
+ package: array(ZoneRuleSchema).readonly()
12616
12720
  });
12617
12721
  var ProviderStatusSchema = object({
12618
12722
  connected: boolean(),
@@ -15820,6 +15924,7 @@ var EventKindIconSchema = _enum([
15820
15924
  "smoke",
15821
15925
  "water",
15822
15926
  "button",
15927
+ "package",
15823
15928
  "generic"
15824
15929
  ]);
15825
15930
  var EventKindCategorySchema = _enum([
@@ -15827,7 +15932,8 @@ var EventKindCategorySchema = _enum([
15827
15932
  "audio",
15828
15933
  "detection",
15829
15934
  "sensor",
15830
- "custom"
15935
+ "custom",
15936
+ "package"
15831
15937
  ]);
15832
15938
  var EventKindDescriptorSchema = object({
15833
15939
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -16168,6 +16274,26 @@ var TrackCascadeCountsSchema = object({
16168
16274
  /** Per-track CLIP search vectors removed (best-effort). */
16169
16275
  embeddings: number().int()
16170
16276
  });
16277
+ /** Event-store footprint for one camera. */
16278
+ var EventStoreDeviceFootprintSchema = object({
16279
+ deviceId: number(),
16280
+ /** Persisted event rows (motion + object + audio) for the camera. */
16281
+ rows: number().int(),
16282
+ /** Event-owned media bytes on disk for the camera. */
16283
+ bytes: number().int()
16284
+ });
16285
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
16286
+ var EventStoreFootprintSchema = object({
16287
+ totalRows: number().int(),
16288
+ totalBytes: number().int(),
16289
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
16290
+ });
16291
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
16292
+ var EventPruneCountsSchema = object({
16293
+ motion: number().int(),
16294
+ object: number().int(),
16295
+ audio: number().int()
16296
+ });
16171
16297
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
16172
16298
  deviceId: number(),
16173
16299
  trackId: string()
@@ -16231,6 +16357,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16231
16357
  }), {
16232
16358
  kind: "mutation",
16233
16359
  auth: "admin"
16360
+ }), method(object({}), EventStoreFootprintSchema, {
16361
+ kind: "query",
16362
+ auth: "admin"
16363
+ }), method(object({
16364
+ olderThanMs: number(),
16365
+ reason: OpsLogReasonSchema.optional()
16366
+ }), EventPruneCountsSchema, {
16367
+ kind: "mutation",
16368
+ auth: "admin"
16369
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
16370
+ kind: "mutation",
16371
+ auth: "admin"
16372
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
16373
+ kind: "query",
16374
+ auth: "admin"
16234
16375
  }), method(object({
16235
16376
  eventId: string(),
16236
16377
  kind: MediaFileKindEnum.optional()
@@ -19464,13 +19605,131 @@ method(object({
19464
19605
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
19465
19606
  kind: "mutation",
19466
19607
  auth: "admin"
19467
- }), method(object({ deviceId: number() }), object({
19608
+ }), method(object({
19609
+ deviceId: number(),
19610
+ reason: OpsLogReasonSchema.optional()
19611
+ }), object({
19468
19612
  floorMs: number().nullable(),
19469
19613
  deletedBuckets: number().int(),
19470
19614
  reclaimedBytes: number().int()
19471
19615
  }), {
19472
19616
  kind: "mutation",
19473
19617
  auth: "admin"
19618
+ }), method(object({
19619
+ deviceId: number(),
19620
+ fromMs: number().optional(),
19621
+ toMs: number().optional()
19622
+ }), object({
19623
+ deletedBuckets: number().int(),
19624
+ reclaimedBytes: number().int()
19625
+ }), {
19626
+ kind: "mutation",
19627
+ auth: "admin"
19628
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19629
+ kind: "query",
19630
+ auth: "admin"
19631
+ });
19632
+ /**
19633
+ * `recordingExport` cap — render a footage time range into a single downloadable
19634
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
19635
+ * bounded lifetime with a durable history, auto-expiry, and optional
19636
+ * delete-after-download.
19637
+ *
19638
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
19639
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
19640
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
19641
+ * input and dispatch to that single provider; the render runs on the node that
19642
+ * owns the footage (no cross-node segment transfer). Download rides the
19643
+ * framework addon data-plane. State persists in a DurableState blob (NOT
19644
+ * SQLite); history rows survive file deletion for audit.
19645
+ */
19646
+ /** Playback-speed multiplier for the render (1 = realtime). */
19647
+ var ExportSpeedSchema = number().min(.25).max(32);
19648
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
19649
+ var ExportTimelapseSchema = object({
19650
+ everyMs: number().int().positive(),
19651
+ outputFps: number().int().min(1).max(60).optional()
19652
+ });
19653
+ /**
19654
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
19655
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
19656
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
19657
+ * `deleteAfterDownload` removes it shortly after the first complete download.
19658
+ */
19659
+ var ExportOptionsSchema = object({
19660
+ speed: ExportSpeedSchema.optional(),
19661
+ timelapse: ExportTimelapseSchema.optional(),
19662
+ includeAudio: boolean(),
19663
+ maxLifeMs: number().int().positive(),
19664
+ deleteAfterDownload: boolean(),
19665
+ title: string().max(200).optional()
19666
+ }).superRefine((v, ctx) => {
19667
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
19668
+ code: ZodIssueCode.custom,
19669
+ message: "speed and timelapse are mutually exclusive",
19670
+ path: ["timelapse"]
19671
+ });
19672
+ });
19673
+ var ExportStateSchema = _enum([
19674
+ "queued",
19675
+ "rendering",
19676
+ "ready",
19677
+ "failed",
19678
+ "expired",
19679
+ "deleted"
19680
+ ]);
19681
+ /** One export job / history row. */
19682
+ var ExportRecordSchema = object({
19683
+ id: string(),
19684
+ deviceId: number(),
19685
+ profile: string(),
19686
+ fromMs: number(),
19687
+ toMs: number(),
19688
+ options: ExportOptionsSchema,
19689
+ state: ExportStateSchema,
19690
+ /** 0–100 while rendering; null otherwise. */
19691
+ progressPct: number().nullable(),
19692
+ /** File size once ready; null before. */
19693
+ fileBytes: number().nullable(),
19694
+ expiresAt: number(),
19695
+ deleteAfterDownload: boolean(),
19696
+ /** Epoch of the first complete download; null until then. */
19697
+ downloadedAt: number().nullable(),
19698
+ createdAt: number(),
19699
+ /** User id/name that requested the export. */
19700
+ createdBy: string(),
19701
+ /** Failure reason when state is 'failed'; null otherwise. */
19702
+ error: string().nullable()
19703
+ });
19704
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
19705
+ var ExportDownloadSchema = object({
19706
+ url: string(),
19707
+ endpoints: array(string())
19708
+ });
19709
+ method(object({
19710
+ deviceId: number(),
19711
+ profile: string(),
19712
+ fromMs: number(),
19713
+ toMs: number(),
19714
+ options: ExportOptionsSchema
19715
+ }), ExportRecordSchema, {
19716
+ kind: "mutation",
19717
+ auth: "protected"
19718
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
19719
+ kind: "query",
19720
+ auth: "protected"
19721
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19722
+ kind: "query",
19723
+ auth: "protected"
19724
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19725
+ kind: "mutation",
19726
+ auth: "protected"
19727
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19728
+ kind: "mutation",
19729
+ auth: "protected"
19730
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
19731
+ kind: "query",
19732
+ auth: "protected"
19474
19733
  });
19475
19734
  /**
19476
19735
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -22490,6 +22749,12 @@ Object.freeze({
22490
22749
  addonId: null,
22491
22750
  access: "delete"
22492
22751
  },
22752
+ "pipelineAnalytics.deleteDeviceEvents": {
22753
+ capName: "pipeline-analytics",
22754
+ capScope: "device",
22755
+ addonId: null,
22756
+ access: "delete"
22757
+ },
22493
22758
  "pipelineAnalytics.deleteTracks": {
22494
22759
  capName: "pipeline-analytics",
22495
22760
  capScope: "device",
@@ -22520,6 +22785,12 @@ Object.freeze({
22520
22785
  addonId: null,
22521
22786
  access: "view"
22522
22787
  },
22788
+ "pipelineAnalytics.getEventStoreFootprint": {
22789
+ capName: "pipeline-analytics",
22790
+ capScope: "device",
22791
+ addonId: null,
22792
+ access: "view"
22793
+ },
22523
22794
  "pipelineAnalytics.getKeyEvents": {
22524
22795
  capName: "pipeline-analytics",
22525
22796
  capScope: "device",
@@ -22562,6 +22833,12 @@ Object.freeze({
22562
22833
  addonId: null,
22563
22834
  access: "view"
22564
22835
  },
22836
+ "pipelineAnalytics.listOpsLog": {
22837
+ capName: "pipeline-analytics",
22838
+ capScope: "device",
22839
+ addonId: null,
22840
+ access: "view"
22841
+ },
22565
22842
  "pipelineAnalytics.listRecentTracks": {
22566
22843
  capName: "pipeline-analytics",
22567
22844
  capScope: "device",
@@ -22574,6 +22851,12 @@ Object.freeze({
22574
22851
  addonId: null,
22575
22852
  access: "view"
22576
22853
  },
22854
+ "pipelineAnalytics.pruneEvents": {
22855
+ capName: "pipeline-analytics",
22856
+ capScope: "device",
22857
+ addonId: null,
22858
+ access: "create"
22859
+ },
22577
22860
  "pipelineAnalytics.pruneEventsBefore": {
22578
22861
  capName: "pipeline-analytics",
22579
22862
  capScope: "device",
@@ -23336,6 +23619,12 @@ Object.freeze({
23336
23619
  addonId: null,
23337
23620
  access: "create"
23338
23621
  },
23622
+ "recording.deleteFootprint": {
23623
+ capName: "recording",
23624
+ capScope: "system",
23625
+ addonId: null,
23626
+ access: "delete"
23627
+ },
23339
23628
  "recording.getAvailability": {
23340
23629
  capName: "recording",
23341
23630
  capScope: "system",
@@ -23366,6 +23655,12 @@ Object.freeze({
23366
23655
  addonId: null,
23367
23656
  access: "view"
23368
23657
  },
23658
+ "recording.listOpsLog": {
23659
+ capName: "recording",
23660
+ capScope: "system",
23661
+ addonId: null,
23662
+ access: "view"
23663
+ },
23369
23664
  "recording.locateSegment": {
23370
23665
  capName: "recording",
23371
23666
  capScope: "system",
@@ -23396,6 +23691,42 @@ Object.freeze({
23396
23691
  addonId: null,
23397
23692
  access: "create"
23398
23693
  },
23694
+ "recordingExport.cancelExport": {
23695
+ capName: "recordingExport",
23696
+ capScope: "system",
23697
+ addonId: null,
23698
+ access: "create"
23699
+ },
23700
+ "recordingExport.createExport": {
23701
+ capName: "recordingExport",
23702
+ capScope: "system",
23703
+ addonId: null,
23704
+ access: "create"
23705
+ },
23706
+ "recordingExport.deleteExport": {
23707
+ capName: "recordingExport",
23708
+ capScope: "system",
23709
+ addonId: null,
23710
+ access: "delete"
23711
+ },
23712
+ "recordingExport.getDownloadUrl": {
23713
+ capName: "recordingExport",
23714
+ capScope: "system",
23715
+ addonId: null,
23716
+ access: "view"
23717
+ },
23718
+ "recordingExport.getExport": {
23719
+ capName: "recordingExport",
23720
+ capScope: "system",
23721
+ addonId: null,
23722
+ access: "view"
23723
+ },
23724
+ "recordingExport.listExports": {
23725
+ capName: "recordingExport",
23726
+ capScope: "system",
23727
+ addonId: null,
23728
+ access: "view"
23729
+ },
23399
23730
  "sceneMonitor.captureReference": {
23400
23731
  capName: "scene-monitor",
23401
23732
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-hap",
3
- "version": "1.1.25",
3
+ "version": "1.1.27",
4
4
  "description": "HomeKit (HAP) bridge exporter for CamStack devices. Publishes a bridged accessory per exposed device — MotionSensor in this MVP; Camera/Doorbell/Switch/Light follow.",
5
5
  "keywords": [
6
6
  "camstack",