@camstack/addon-notifiers 1.1.27 → 1.1.29

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
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  let node_zlib = require("node:zlib");
6
- //#region ../types/dist/event-category-H4AVePnn.mjs
6
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
7
7
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
8
8
  EventCategory["SystemBoot"] = "system.boot";
9
9
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -156,6 +156,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
156
156
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
157
157
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
158
158
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
159
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
160
+ * progress bar the client reconciles via `recordingExport.getExport`. */
161
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
162
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
163
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
159
164
  EventCategory["DetectionEvent"] = "detection.event";
160
165
  EventCategory["SessionTrackNew"] = "session.track.new";
161
166
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -445,6 +450,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
445
450
  */
446
451
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
447
452
  /**
453
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
454
+ * a package zone — a newly-appeared stationary object of a package class
455
+ * that cleared the class / zone / dwell / size gates. Payload:
456
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
457
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
458
+ * Telemetry (D8): the durable record is the `package-events` store row;
459
+ * this bus topic drives notifier rules + live UI. See
460
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
461
+ */
462
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
463
+ /**
464
+ * Fired by `addon-post-analysis` when a previously-delivered package
465
+ * leaves its zone (the stationary entry departed — moved or swept).
466
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
467
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
468
+ * Telemetry (D8). See package-zones-design §5.2.
469
+ */
470
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
471
+ /**
448
472
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
449
473
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
450
474
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5183,6 +5207,25 @@ function _instanceof(cls, params = {}) {
5183
5207
  };
5184
5208
  return inst;
5185
5209
  }
5210
+ //#endregion
5211
+ //#region ../../node_modules/zod/v4/classic/compat.js
5212
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5213
+ var ZodIssueCode = {
5214
+ invalid_type: "invalid_type",
5215
+ too_big: "too_big",
5216
+ too_small: "too_small",
5217
+ invalid_format: "invalid_format",
5218
+ not_multiple_of: "not_multiple_of",
5219
+ unrecognized_keys: "unrecognized_keys",
5220
+ invalid_union: "invalid_union",
5221
+ invalid_key: "invalid_key",
5222
+ invalid_element: "invalid_element",
5223
+ invalid_value: "invalid_value",
5224
+ custom: "custom"
5225
+ };
5226
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5227
+ var ZodFirstPartyTypeKind;
5228
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5186
5229
  Object.fromEntries([
5187
5230
  {
5188
5231
  id: "overview",
@@ -7406,6 +7449,62 @@ var RecordingConfigSchema = object({
7406
7449
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7407
7450
  });
7408
7451
  /**
7452
+ * Ops-log — the durable, append-only operations audit shared by the
7453
+ * recordings and events management surfaces.
7454
+ *
7455
+ * ONE row shape is reused for both domains so a single "Activity" view can
7456
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7457
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7458
+ * management operation, WHY it ran (reason), and its measurable effect
7459
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7460
+ * never fail the operation it records.
7461
+ */
7462
+ /** Which management domain the operation belongs to. */
7463
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7464
+ /** The kind of management operation performed. */
7465
+ var OpsLogOpSchema = _enum([
7466
+ "prune",
7467
+ "manual-delete",
7468
+ "rescan",
7469
+ "retention-run"
7470
+ ]);
7471
+ /** Why the operation ran. */
7472
+ var OpsLogReasonSchema = _enum([
7473
+ "retention",
7474
+ "quota",
7475
+ "manual",
7476
+ "operator"
7477
+ ]);
7478
+ /** One audit row, shared verbatim by both domains. */
7479
+ var OpsLogEntrySchema = object({
7480
+ /** Unique row id. */
7481
+ id: string(),
7482
+ /** Epoch ms the operation completed. */
7483
+ at: number(),
7484
+ domain: OpsLogDomainSchema,
7485
+ op: OpsLogOpSchema,
7486
+ reason: OpsLogReasonSchema,
7487
+ /** The camera the op targeted; null for a cluster/global op. */
7488
+ deviceId: number().nullable(),
7489
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7490
+ nodeId: string(),
7491
+ /** Buckets / rows deleted (op-specific unit). */
7492
+ itemsAffected: number(),
7493
+ /** Bytes reclaimed by the op (0 when not measurable). */
7494
+ bytesReclaimed: number(),
7495
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7496
+ detail: string().nullable(),
7497
+ /** Who/what triggered the op. */
7498
+ actor: string()
7499
+ });
7500
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7501
+ var OpsLogQueryInputSchema = object({
7502
+ /** Restrict to a single camera; omit for every row. */
7503
+ deviceId: number().optional(),
7504
+ /** Max rows returned, newest-first. */
7505
+ limit: number().int().min(1).max(1e3).optional()
7506
+ });
7507
+ /**
7409
7508
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7410
7509
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7411
7510
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -12848,7 +12947,11 @@ array(ZoneRuleSchema).readonly();
12848
12947
  * Extend the enum here when a new gating consumer comes online (audio
12849
12948
  * gating, alert filtering, …) — no other surface needs to change.
12850
12949
  */
12851
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
12950
+ var ZoneRuleStageEnum = _enum([
12951
+ "motion",
12952
+ "detection",
12953
+ "package"
12954
+ ]);
12852
12955
  DeviceType.Camera, method(object({
12853
12956
  deviceId: number(),
12854
12957
  stage: ZoneRuleStageEnum
@@ -12861,7 +12964,8 @@ DeviceType.Camera, method(object({
12861
12964
  auth: "admin"
12862
12965
  }), object({
12863
12966
  motion: array(ZoneRuleSchema).readonly(),
12864
- detection: array(ZoneRuleSchema).readonly()
12967
+ detection: array(ZoneRuleSchema).readonly(),
12968
+ package: array(ZoneRuleSchema).readonly()
12865
12969
  });
12866
12970
  var ProviderStatusSchema = object({
12867
12971
  connected: boolean(),
@@ -16077,6 +16181,7 @@ var EventKindIconSchema = _enum([
16077
16181
  "smoke",
16078
16182
  "water",
16079
16183
  "button",
16184
+ "package",
16080
16185
  "generic"
16081
16186
  ]);
16082
16187
  var EventKindCategorySchema = _enum([
@@ -16084,7 +16189,8 @@ var EventKindCategorySchema = _enum([
16084
16189
  "audio",
16085
16190
  "detection",
16086
16191
  "sensor",
16087
- "custom"
16192
+ "custom",
16193
+ "package"
16088
16194
  ]);
16089
16195
  var EventKindDescriptorSchema = object({
16090
16196
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -16425,6 +16531,26 @@ var TrackCascadeCountsSchema = object({
16425
16531
  /** Per-track CLIP search vectors removed (best-effort). */
16426
16532
  embeddings: number().int()
16427
16533
  });
16534
+ /** Event-store footprint for one camera. */
16535
+ var EventStoreDeviceFootprintSchema = object({
16536
+ deviceId: number(),
16537
+ /** Persisted event rows (motion + object + audio) for the camera. */
16538
+ rows: number().int(),
16539
+ /** Event-owned media bytes on disk for the camera. */
16540
+ bytes: number().int()
16541
+ });
16542
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
16543
+ var EventStoreFootprintSchema = object({
16544
+ totalRows: number().int(),
16545
+ totalBytes: number().int(),
16546
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
16547
+ });
16548
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
16549
+ var EventPruneCountsSchema = object({
16550
+ motion: number().int(),
16551
+ object: number().int(),
16552
+ audio: number().int()
16553
+ });
16428
16554
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
16429
16555
  deviceId: number(),
16430
16556
  trackId: string()
@@ -16488,6 +16614,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16488
16614
  }), {
16489
16615
  kind: "mutation",
16490
16616
  auth: "admin"
16617
+ }), method(object({}), EventStoreFootprintSchema, {
16618
+ kind: "query",
16619
+ auth: "admin"
16620
+ }), method(object({
16621
+ olderThanMs: number(),
16622
+ reason: OpsLogReasonSchema.optional()
16623
+ }), EventPruneCountsSchema, {
16624
+ kind: "mutation",
16625
+ auth: "admin"
16626
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
16627
+ kind: "mutation",
16628
+ auth: "admin"
16629
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
16630
+ kind: "query",
16631
+ auth: "admin"
16491
16632
  }), method(object({
16492
16633
  eventId: string(),
16493
16634
  kind: MediaFileKindEnum.optional()
@@ -19721,13 +19862,131 @@ method(object({
19721
19862
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
19722
19863
  kind: "mutation",
19723
19864
  auth: "admin"
19724
- }), method(object({ deviceId: number() }), object({
19865
+ }), method(object({
19866
+ deviceId: number(),
19867
+ reason: OpsLogReasonSchema.optional()
19868
+ }), object({
19725
19869
  floorMs: number().nullable(),
19726
19870
  deletedBuckets: number().int(),
19727
19871
  reclaimedBytes: number().int()
19728
19872
  }), {
19729
19873
  kind: "mutation",
19730
19874
  auth: "admin"
19875
+ }), method(object({
19876
+ deviceId: number(),
19877
+ fromMs: number().optional(),
19878
+ toMs: number().optional()
19879
+ }), object({
19880
+ deletedBuckets: number().int(),
19881
+ reclaimedBytes: number().int()
19882
+ }), {
19883
+ kind: "mutation",
19884
+ auth: "admin"
19885
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19886
+ kind: "query",
19887
+ auth: "admin"
19888
+ });
19889
+ /**
19890
+ * `recordingExport` cap — render a footage time range into a single downloadable
19891
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
19892
+ * bounded lifetime with a durable history, auto-expiry, and optional
19893
+ * delete-after-download.
19894
+ *
19895
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
19896
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
19897
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
19898
+ * input and dispatch to that single provider; the render runs on the node that
19899
+ * owns the footage (no cross-node segment transfer). Download rides the
19900
+ * framework addon data-plane. State persists in a DurableState blob (NOT
19901
+ * SQLite); history rows survive file deletion for audit.
19902
+ */
19903
+ /** Playback-speed multiplier for the render (1 = realtime). */
19904
+ var ExportSpeedSchema = number().min(.25).max(32);
19905
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
19906
+ var ExportTimelapseSchema = object({
19907
+ everyMs: number().int().positive(),
19908
+ outputFps: number().int().min(1).max(60).optional()
19909
+ });
19910
+ /**
19911
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
19912
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
19913
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
19914
+ * `deleteAfterDownload` removes it shortly after the first complete download.
19915
+ */
19916
+ var ExportOptionsSchema = object({
19917
+ speed: ExportSpeedSchema.optional(),
19918
+ timelapse: ExportTimelapseSchema.optional(),
19919
+ includeAudio: boolean(),
19920
+ maxLifeMs: number().int().positive(),
19921
+ deleteAfterDownload: boolean(),
19922
+ title: string().max(200).optional()
19923
+ }).superRefine((v, ctx) => {
19924
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
19925
+ code: ZodIssueCode.custom,
19926
+ message: "speed and timelapse are mutually exclusive",
19927
+ path: ["timelapse"]
19928
+ });
19929
+ });
19930
+ var ExportStateSchema = _enum([
19931
+ "queued",
19932
+ "rendering",
19933
+ "ready",
19934
+ "failed",
19935
+ "expired",
19936
+ "deleted"
19937
+ ]);
19938
+ /** One export job / history row. */
19939
+ var ExportRecordSchema = object({
19940
+ id: string(),
19941
+ deviceId: number(),
19942
+ profile: string(),
19943
+ fromMs: number(),
19944
+ toMs: number(),
19945
+ options: ExportOptionsSchema,
19946
+ state: ExportStateSchema,
19947
+ /** 0–100 while rendering; null otherwise. */
19948
+ progressPct: number().nullable(),
19949
+ /** File size once ready; null before. */
19950
+ fileBytes: number().nullable(),
19951
+ expiresAt: number(),
19952
+ deleteAfterDownload: boolean(),
19953
+ /** Epoch of the first complete download; null until then. */
19954
+ downloadedAt: number().nullable(),
19955
+ createdAt: number(),
19956
+ /** User id/name that requested the export. */
19957
+ createdBy: string(),
19958
+ /** Failure reason when state is 'failed'; null otherwise. */
19959
+ error: string().nullable()
19960
+ });
19961
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
19962
+ var ExportDownloadSchema = object({
19963
+ url: string(),
19964
+ endpoints: array(string())
19965
+ });
19966
+ method(object({
19967
+ deviceId: number(),
19968
+ profile: string(),
19969
+ fromMs: number(),
19970
+ toMs: number(),
19971
+ options: ExportOptionsSchema
19972
+ }), ExportRecordSchema, {
19973
+ kind: "mutation",
19974
+ auth: "protected"
19975
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
19976
+ kind: "query",
19977
+ auth: "protected"
19978
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19979
+ kind: "query",
19980
+ auth: "protected"
19981
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19982
+ kind: "mutation",
19983
+ auth: "protected"
19984
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19985
+ kind: "mutation",
19986
+ auth: "protected"
19987
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
19988
+ kind: "query",
19989
+ auth: "protected"
19731
19990
  });
19732
19991
  /**
19733
19992
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -22747,6 +23006,12 @@ Object.freeze({
22747
23006
  addonId: null,
22748
23007
  access: "delete"
22749
23008
  },
23009
+ "pipelineAnalytics.deleteDeviceEvents": {
23010
+ capName: "pipeline-analytics",
23011
+ capScope: "device",
23012
+ addonId: null,
23013
+ access: "delete"
23014
+ },
22750
23015
  "pipelineAnalytics.deleteTracks": {
22751
23016
  capName: "pipeline-analytics",
22752
23017
  capScope: "device",
@@ -22777,6 +23042,12 @@ Object.freeze({
22777
23042
  addonId: null,
22778
23043
  access: "view"
22779
23044
  },
23045
+ "pipelineAnalytics.getEventStoreFootprint": {
23046
+ capName: "pipeline-analytics",
23047
+ capScope: "device",
23048
+ addonId: null,
23049
+ access: "view"
23050
+ },
22780
23051
  "pipelineAnalytics.getKeyEvents": {
22781
23052
  capName: "pipeline-analytics",
22782
23053
  capScope: "device",
@@ -22819,6 +23090,12 @@ Object.freeze({
22819
23090
  addonId: null,
22820
23091
  access: "view"
22821
23092
  },
23093
+ "pipelineAnalytics.listOpsLog": {
23094
+ capName: "pipeline-analytics",
23095
+ capScope: "device",
23096
+ addonId: null,
23097
+ access: "view"
23098
+ },
22822
23099
  "pipelineAnalytics.listRecentTracks": {
22823
23100
  capName: "pipeline-analytics",
22824
23101
  capScope: "device",
@@ -22831,6 +23108,12 @@ Object.freeze({
22831
23108
  addonId: null,
22832
23109
  access: "view"
22833
23110
  },
23111
+ "pipelineAnalytics.pruneEvents": {
23112
+ capName: "pipeline-analytics",
23113
+ capScope: "device",
23114
+ addonId: null,
23115
+ access: "create"
23116
+ },
22834
23117
  "pipelineAnalytics.pruneEventsBefore": {
22835
23118
  capName: "pipeline-analytics",
22836
23119
  capScope: "device",
@@ -23593,6 +23876,12 @@ Object.freeze({
23593
23876
  addonId: null,
23594
23877
  access: "create"
23595
23878
  },
23879
+ "recording.deleteFootprint": {
23880
+ capName: "recording",
23881
+ capScope: "system",
23882
+ addonId: null,
23883
+ access: "delete"
23884
+ },
23596
23885
  "recording.getAvailability": {
23597
23886
  capName: "recording",
23598
23887
  capScope: "system",
@@ -23623,6 +23912,12 @@ Object.freeze({
23623
23912
  addonId: null,
23624
23913
  access: "view"
23625
23914
  },
23915
+ "recording.listOpsLog": {
23916
+ capName: "recording",
23917
+ capScope: "system",
23918
+ addonId: null,
23919
+ access: "view"
23920
+ },
23626
23921
  "recording.locateSegment": {
23627
23922
  capName: "recording",
23628
23923
  capScope: "system",
@@ -23653,6 +23948,42 @@ Object.freeze({
23653
23948
  addonId: null,
23654
23949
  access: "create"
23655
23950
  },
23951
+ "recordingExport.cancelExport": {
23952
+ capName: "recordingExport",
23953
+ capScope: "system",
23954
+ addonId: null,
23955
+ access: "create"
23956
+ },
23957
+ "recordingExport.createExport": {
23958
+ capName: "recordingExport",
23959
+ capScope: "system",
23960
+ addonId: null,
23961
+ access: "create"
23962
+ },
23963
+ "recordingExport.deleteExport": {
23964
+ capName: "recordingExport",
23965
+ capScope: "system",
23966
+ addonId: null,
23967
+ access: "delete"
23968
+ },
23969
+ "recordingExport.getDownloadUrl": {
23970
+ capName: "recordingExport",
23971
+ capScope: "system",
23972
+ addonId: null,
23973
+ access: "view"
23974
+ },
23975
+ "recordingExport.getExport": {
23976
+ capName: "recordingExport",
23977
+ capScope: "system",
23978
+ addonId: null,
23979
+ access: "view"
23980
+ },
23981
+ "recordingExport.listExports": {
23982
+ capName: "recordingExport",
23983
+ capScope: "system",
23984
+ addonId: null,
23985
+ access: "view"
23986
+ },
23656
23987
  "sceneMonitor.captureReference": {
23657
23988
  capName: "scene-monitor",
23658
23989
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
2
- //#region ../types/dist/event-category-H4AVePnn.mjs
2
+ //#region ../types/dist/event-category-D4HJq7Mw.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -152,6 +152,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
152
152
  /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
153
153
  * thumb is a scrub gap the recorder's keyframe backfill covers. */
154
154
  EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
155
+ /** Export render progress (0–100). Telemetry (D8): a lost tick is a stale
156
+ * progress bar the client reconciles via `recordingExport.getExport`. */
157
+ EventCategory["RecordingExportProgress"] = "recording.export.progress";
158
+ EventCategory["RecordingExportCompleted"] = "recording.export.completed";
159
+ EventCategory["RecordingExportFailed"] = "recording.export.failed";
155
160
  EventCategory["DetectionEvent"] = "detection.event";
156
161
  EventCategory["SessionTrackNew"] = "session.track.new";
157
162
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -441,6 +446,25 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
441
446
  */
442
447
  EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
443
448
  /**
449
+ * Fired by `addon-post-analysis` when a package-drop is confirmed inside
450
+ * a package zone — a newly-appeared stationary object of a package class
451
+ * that cleared the class / zone / dwell / size gates. Payload:
452
+ * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
453
+ * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
454
+ * Telemetry (D8): the durable record is the `package-events` store row;
455
+ * this bus topic drives notifier rules + live UI. See
456
+ * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
457
+ */
458
+ EventCategory["PipelineAnalyticsPackageDelivered"] = "pipeline-analytics.package-delivered";
459
+ /**
460
+ * Fired by `addon-post-analysis` when a previously-delivered package
461
+ * leaves its zone (the stationary entry departed — moved or swept).
462
+ * Payload: `PipelineAnalyticsPackagePickedUpPayload` carrying
463
+ * `{ deviceId, entryId, deliveredEventId, className, timestamp }`.
464
+ * Telemetry (D8). See package-zones-design §5.2.
465
+ */
466
+ EventCategory["PipelineAnalyticsPackagePickedUp"] = "pipeline-analytics.package-picked-up";
467
+ /**
444
468
  * Fired by `addon-post-analysis` whenever a gallery face row changes:
445
469
  * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
446
470
  * `'unassigned'` its identity link changed, `'deleted'` the row was
@@ -5179,6 +5203,25 @@ function _instanceof(cls, params = {}) {
5179
5203
  };
5180
5204
  return inst;
5181
5205
  }
5206
+ //#endregion
5207
+ //#region ../../node_modules/zod/v4/classic/compat.js
5208
+ /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
5209
+ var ZodIssueCode = {
5210
+ invalid_type: "invalid_type",
5211
+ too_big: "too_big",
5212
+ too_small: "too_small",
5213
+ invalid_format: "invalid_format",
5214
+ not_multiple_of: "not_multiple_of",
5215
+ unrecognized_keys: "unrecognized_keys",
5216
+ invalid_union: "invalid_union",
5217
+ invalid_key: "invalid_key",
5218
+ invalid_element: "invalid_element",
5219
+ invalid_value: "invalid_value",
5220
+ custom: "custom"
5221
+ };
5222
+ /** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
5223
+ var ZodFirstPartyTypeKind;
5224
+ ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5182
5225
  Object.fromEntries([
5183
5226
  {
5184
5227
  id: "overview",
@@ -7402,6 +7445,62 @@ var RecordingConfigSchema = object({
7402
7445
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7403
7446
  });
7404
7447
  /**
7448
+ * Ops-log — the durable, append-only operations audit shared by the
7449
+ * recordings and events management surfaces.
7450
+ *
7451
+ * ONE row shape is reused for both domains so a single "Activity" view can
7452
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7453
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7454
+ * management operation, WHY it ran (reason), and its measurable effect
7455
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7456
+ * never fail the operation it records.
7457
+ */
7458
+ /** Which management domain the operation belongs to. */
7459
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7460
+ /** The kind of management operation performed. */
7461
+ var OpsLogOpSchema = _enum([
7462
+ "prune",
7463
+ "manual-delete",
7464
+ "rescan",
7465
+ "retention-run"
7466
+ ]);
7467
+ /** Why the operation ran. */
7468
+ var OpsLogReasonSchema = _enum([
7469
+ "retention",
7470
+ "quota",
7471
+ "manual",
7472
+ "operator"
7473
+ ]);
7474
+ /** One audit row, shared verbatim by both domains. */
7475
+ var OpsLogEntrySchema = object({
7476
+ /** Unique row id. */
7477
+ id: string(),
7478
+ /** Epoch ms the operation completed. */
7479
+ at: number(),
7480
+ domain: OpsLogDomainSchema,
7481
+ op: OpsLogOpSchema,
7482
+ reason: OpsLogReasonSchema,
7483
+ /** The camera the op targeted; null for a cluster/global op. */
7484
+ deviceId: number().nullable(),
7485
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7486
+ nodeId: string(),
7487
+ /** Buckets / rows deleted (op-specific unit). */
7488
+ itemsAffected: number(),
7489
+ /** Bytes reclaimed by the op (0 when not measurable). */
7490
+ bytesReclaimed: number(),
7491
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7492
+ detail: string().nullable(),
7493
+ /** Who/what triggered the op. */
7494
+ actor: string()
7495
+ });
7496
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7497
+ var OpsLogQueryInputSchema = object({
7498
+ /** Restrict to a single camera; omit for every row. */
7499
+ deviceId: number().optional(),
7500
+ /** Max rows returned, newest-first. */
7501
+ limit: number().int().min(1).max(1e3).optional()
7502
+ });
7503
+ /**
7405
7504
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7406
7505
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7407
7506
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -12844,7 +12943,11 @@ array(ZoneRuleSchema).readonly();
12844
12943
  * Extend the enum here when a new gating consumer comes online (audio
12845
12944
  * gating, alert filtering, …) — no other surface needs to change.
12846
12945
  */
12847
- var ZoneRuleStageEnum = _enum(["motion", "detection"]);
12946
+ var ZoneRuleStageEnum = _enum([
12947
+ "motion",
12948
+ "detection",
12949
+ "package"
12950
+ ]);
12848
12951
  DeviceType.Camera, method(object({
12849
12952
  deviceId: number(),
12850
12953
  stage: ZoneRuleStageEnum
@@ -12857,7 +12960,8 @@ DeviceType.Camera, method(object({
12857
12960
  auth: "admin"
12858
12961
  }), object({
12859
12962
  motion: array(ZoneRuleSchema).readonly(),
12860
- detection: array(ZoneRuleSchema).readonly()
12963
+ detection: array(ZoneRuleSchema).readonly(),
12964
+ package: array(ZoneRuleSchema).readonly()
12861
12965
  });
12862
12966
  var ProviderStatusSchema = object({
12863
12967
  connected: boolean(),
@@ -16073,6 +16177,7 @@ var EventKindIconSchema = _enum([
16073
16177
  "smoke",
16074
16178
  "water",
16075
16179
  "button",
16180
+ "package",
16076
16181
  "generic"
16077
16182
  ]);
16078
16183
  var EventKindCategorySchema = _enum([
@@ -16080,7 +16185,8 @@ var EventKindCategorySchema = _enum([
16080
16185
  "audio",
16081
16186
  "detection",
16082
16187
  "sensor",
16083
- "custom"
16188
+ "custom",
16189
+ "package"
16084
16190
  ]);
16085
16191
  var EventKindDescriptorSchema = object({
16086
16192
  /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
@@ -16421,6 +16527,26 @@ var TrackCascadeCountsSchema = object({
16421
16527
  /** Per-track CLIP search vectors removed (best-effort). */
16422
16528
  embeddings: number().int()
16423
16529
  });
16530
+ /** Event-store footprint for one camera. */
16531
+ var EventStoreDeviceFootprintSchema = object({
16532
+ deviceId: number(),
16533
+ /** Persisted event rows (motion + object + audio) for the camera. */
16534
+ rows: number().int(),
16535
+ /** Event-owned media bytes on disk for the camera. */
16536
+ bytes: number().int()
16537
+ });
16538
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
16539
+ var EventStoreFootprintSchema = object({
16540
+ totalRows: number().int(),
16541
+ totalBytes: number().int(),
16542
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
16543
+ });
16544
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
16545
+ var EventPruneCountsSchema = object({
16546
+ motion: number().int(),
16547
+ object: number().int(),
16548
+ audio: number().int()
16549
+ });
16424
16550
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
16425
16551
  deviceId: number(),
16426
16552
  trackId: string()
@@ -16484,6 +16610,21 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16484
16610
  }), {
16485
16611
  kind: "mutation",
16486
16612
  auth: "admin"
16613
+ }), method(object({}), EventStoreFootprintSchema, {
16614
+ kind: "query",
16615
+ auth: "admin"
16616
+ }), method(object({
16617
+ olderThanMs: number(),
16618
+ reason: OpsLogReasonSchema.optional()
16619
+ }), EventPruneCountsSchema, {
16620
+ kind: "mutation",
16621
+ auth: "admin"
16622
+ }), method(object({ deviceId: number() }), EventPruneCountsSchema, {
16623
+ kind: "mutation",
16624
+ auth: "admin"
16625
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
16626
+ kind: "query",
16627
+ auth: "admin"
16487
16628
  }), method(object({
16488
16629
  eventId: string(),
16489
16630
  kind: MediaFileKindEnum.optional()
@@ -19717,13 +19858,131 @@ method(object({
19717
19858
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
19718
19859
  kind: "mutation",
19719
19860
  auth: "admin"
19720
- }), method(object({ deviceId: number() }), object({
19861
+ }), method(object({
19862
+ deviceId: number(),
19863
+ reason: OpsLogReasonSchema.optional()
19864
+ }), object({
19721
19865
  floorMs: number().nullable(),
19722
19866
  deletedBuckets: number().int(),
19723
19867
  reclaimedBytes: number().int()
19724
19868
  }), {
19725
19869
  kind: "mutation",
19726
19870
  auth: "admin"
19871
+ }), method(object({
19872
+ deviceId: number(),
19873
+ fromMs: number().optional(),
19874
+ toMs: number().optional()
19875
+ }), object({
19876
+ deletedBuckets: number().int(),
19877
+ reclaimedBytes: number().int()
19878
+ }), {
19879
+ kind: "mutation",
19880
+ auth: "admin"
19881
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19882
+ kind: "query",
19883
+ auth: "admin"
19884
+ });
19885
+ /**
19886
+ * `recordingExport` cap — render a footage time range into a single downloadable
19887
+ * MP4 (regular / accelerated / decelerated / timelapse, ± audio), kept for a
19888
+ * bounded lifetime with a durable history, auto-expiry, and optional
19889
+ * delete-after-download.
19890
+ *
19891
+ * Like `recording` this is a `scope:'system', mode:'singleton'` cap: the
19892
+ * recorder self-gates so exactly ONE node (the designated `recordingNodeId`,
19893
+ * default `hub`) registers it. Device-scoped methods carry `deviceId` in their
19894
+ * input and dispatch to that single provider; the render runs on the node that
19895
+ * owns the footage (no cross-node segment transfer). Download rides the
19896
+ * framework addon data-plane. State persists in a DurableState blob (NOT
19897
+ * SQLite); history rows survive file deletion for audit.
19898
+ */
19899
+ /** Playback-speed multiplier for the render (1 = realtime). */
19900
+ var ExportSpeedSchema = number().min(.25).max(32);
19901
+ /** Timelapse cadence — sample one source frame per `everyMs`, output at `outputFps`. */
19902
+ var ExportTimelapseSchema = object({
19903
+ everyMs: number().int().positive(),
19904
+ outputFps: number().int().min(1).max(60).optional()
19905
+ });
19906
+ /**
19907
+ * Render options. `speed` and `timelapse` are mutually exclusive. `includeAudio`
19908
+ * is honoured only for a realtime-ish speed (0.5–2×); timelapse is always
19909
+ * silent. `maxLifeMs` bounds how long the finished file is kept;
19910
+ * `deleteAfterDownload` removes it shortly after the first complete download.
19911
+ */
19912
+ var ExportOptionsSchema = object({
19913
+ speed: ExportSpeedSchema.optional(),
19914
+ timelapse: ExportTimelapseSchema.optional(),
19915
+ includeAudio: boolean(),
19916
+ maxLifeMs: number().int().positive(),
19917
+ deleteAfterDownload: boolean(),
19918
+ title: string().max(200).optional()
19919
+ }).superRefine((v, ctx) => {
19920
+ if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
19921
+ code: ZodIssueCode.custom,
19922
+ message: "speed and timelapse are mutually exclusive",
19923
+ path: ["timelapse"]
19924
+ });
19925
+ });
19926
+ var ExportStateSchema = _enum([
19927
+ "queued",
19928
+ "rendering",
19929
+ "ready",
19930
+ "failed",
19931
+ "expired",
19932
+ "deleted"
19933
+ ]);
19934
+ /** One export job / history row. */
19935
+ var ExportRecordSchema = object({
19936
+ id: string(),
19937
+ deviceId: number(),
19938
+ profile: string(),
19939
+ fromMs: number(),
19940
+ toMs: number(),
19941
+ options: ExportOptionsSchema,
19942
+ state: ExportStateSchema,
19943
+ /** 0–100 while rendering; null otherwise. */
19944
+ progressPct: number().nullable(),
19945
+ /** File size once ready; null before. */
19946
+ fileBytes: number().nullable(),
19947
+ expiresAt: number(),
19948
+ deleteAfterDownload: boolean(),
19949
+ /** Epoch of the first complete download; null until then. */
19950
+ downloadedAt: number().nullable(),
19951
+ createdAt: number(),
19952
+ /** User id/name that requested the export. */
19953
+ createdBy: string(),
19954
+ /** Failure reason when state is 'failed'; null otherwise. */
19955
+ error: string().nullable()
19956
+ });
19957
+ /** Candidate download URLs (LAN first, then operator extra hosts). */
19958
+ var ExportDownloadSchema = object({
19959
+ url: string(),
19960
+ endpoints: array(string())
19961
+ });
19962
+ method(object({
19963
+ deviceId: number(),
19964
+ profile: string(),
19965
+ fromMs: number(),
19966
+ toMs: number(),
19967
+ options: ExportOptionsSchema
19968
+ }), ExportRecordSchema, {
19969
+ kind: "mutation",
19970
+ auth: "protected"
19971
+ }), method(object({ deviceId: number().optional() }), array(ExportRecordSchema), {
19972
+ kind: "query",
19973
+ auth: "protected"
19974
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19975
+ kind: "query",
19976
+ auth: "protected"
19977
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19978
+ kind: "mutation",
19979
+ auth: "protected"
19980
+ }), method(object({ exportId: string() }), ExportRecordSchema, {
19981
+ kind: "mutation",
19982
+ auth: "protected"
19983
+ }), method(object({ exportId: string() }), ExportDownloadSchema, {
19984
+ kind: "query",
19985
+ auth: "protected"
19727
19986
  });
19728
19987
  /**
19729
19988
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -22743,6 +23002,12 @@ Object.freeze({
22743
23002
  addonId: null,
22744
23003
  access: "delete"
22745
23004
  },
23005
+ "pipelineAnalytics.deleteDeviceEvents": {
23006
+ capName: "pipeline-analytics",
23007
+ capScope: "device",
23008
+ addonId: null,
23009
+ access: "delete"
23010
+ },
22746
23011
  "pipelineAnalytics.deleteTracks": {
22747
23012
  capName: "pipeline-analytics",
22748
23013
  capScope: "device",
@@ -22773,6 +23038,12 @@ Object.freeze({
22773
23038
  addonId: null,
22774
23039
  access: "view"
22775
23040
  },
23041
+ "pipelineAnalytics.getEventStoreFootprint": {
23042
+ capName: "pipeline-analytics",
23043
+ capScope: "device",
23044
+ addonId: null,
23045
+ access: "view"
23046
+ },
22776
23047
  "pipelineAnalytics.getKeyEvents": {
22777
23048
  capName: "pipeline-analytics",
22778
23049
  capScope: "device",
@@ -22815,6 +23086,12 @@ Object.freeze({
22815
23086
  addonId: null,
22816
23087
  access: "view"
22817
23088
  },
23089
+ "pipelineAnalytics.listOpsLog": {
23090
+ capName: "pipeline-analytics",
23091
+ capScope: "device",
23092
+ addonId: null,
23093
+ access: "view"
23094
+ },
22818
23095
  "pipelineAnalytics.listRecentTracks": {
22819
23096
  capName: "pipeline-analytics",
22820
23097
  capScope: "device",
@@ -22827,6 +23104,12 @@ Object.freeze({
22827
23104
  addonId: null,
22828
23105
  access: "view"
22829
23106
  },
23107
+ "pipelineAnalytics.pruneEvents": {
23108
+ capName: "pipeline-analytics",
23109
+ capScope: "device",
23110
+ addonId: null,
23111
+ access: "create"
23112
+ },
22830
23113
  "pipelineAnalytics.pruneEventsBefore": {
22831
23114
  capName: "pipeline-analytics",
22832
23115
  capScope: "device",
@@ -23589,6 +23872,12 @@ Object.freeze({
23589
23872
  addonId: null,
23590
23873
  access: "create"
23591
23874
  },
23875
+ "recording.deleteFootprint": {
23876
+ capName: "recording",
23877
+ capScope: "system",
23878
+ addonId: null,
23879
+ access: "delete"
23880
+ },
23592
23881
  "recording.getAvailability": {
23593
23882
  capName: "recording",
23594
23883
  capScope: "system",
@@ -23619,6 +23908,12 @@ Object.freeze({
23619
23908
  addonId: null,
23620
23909
  access: "view"
23621
23910
  },
23911
+ "recording.listOpsLog": {
23912
+ capName: "recording",
23913
+ capScope: "system",
23914
+ addonId: null,
23915
+ access: "view"
23916
+ },
23622
23917
  "recording.locateSegment": {
23623
23918
  capName: "recording",
23624
23919
  capScope: "system",
@@ -23649,6 +23944,42 @@ Object.freeze({
23649
23944
  addonId: null,
23650
23945
  access: "create"
23651
23946
  },
23947
+ "recordingExport.cancelExport": {
23948
+ capName: "recordingExport",
23949
+ capScope: "system",
23950
+ addonId: null,
23951
+ access: "create"
23952
+ },
23953
+ "recordingExport.createExport": {
23954
+ capName: "recordingExport",
23955
+ capScope: "system",
23956
+ addonId: null,
23957
+ access: "create"
23958
+ },
23959
+ "recordingExport.deleteExport": {
23960
+ capName: "recordingExport",
23961
+ capScope: "system",
23962
+ addonId: null,
23963
+ access: "delete"
23964
+ },
23965
+ "recordingExport.getDownloadUrl": {
23966
+ capName: "recordingExport",
23967
+ capScope: "system",
23968
+ addonId: null,
23969
+ access: "view"
23970
+ },
23971
+ "recordingExport.getExport": {
23972
+ capName: "recordingExport",
23973
+ capScope: "system",
23974
+ addonId: null,
23975
+ access: "view"
23976
+ },
23977
+ "recordingExport.listExports": {
23978
+ capName: "recordingExport",
23979
+ capScope: "system",
23980
+ addonId: null,
23981
+ access: "view"
23982
+ },
23652
23983
  "sceneMonitor.captureReference": {
23653
23984
  capName: "scene-monitor",
23654
23985
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-notifiers",
3
- "version": "1.1.27",
3
+ "version": "1.1.29",
4
4
  "description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
5
5
  "keywords": [
6
6
  "camstack",