@camstack/addon-post-analysis 1.1.33 → 1.1.35

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.
@@ -7380,6 +7380,62 @@ var RecordingConfigSchema = object({
7380
7380
  scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7381
7381
  });
7382
7382
  /**
7383
+ * Ops-log — the durable, append-only operations audit shared by the
7384
+ * recordings and events management surfaces.
7385
+ *
7386
+ * ONE row shape is reused for both domains so a single "Activity" view can
7387
+ * merge the recorder's DurableState ring (recordings ops-log) and the
7388
+ * pipeline-analytics SQLite collection (events ops-log). Each row records a
7389
+ * management operation, WHY it ran (reason), and its measurable effect
7390
+ * (itemsAffected + bytesReclaimed). Writes are best-effort — a failed log must
7391
+ * never fail the operation it records.
7392
+ */
7393
+ /** Which management domain the operation belongs to. */
7394
+ var OpsLogDomainSchema = _enum(["recording", "events"]);
7395
+ /** The kind of management operation performed. */
7396
+ var OpsLogOpSchema = _enum([
7397
+ "prune",
7398
+ "manual-delete",
7399
+ "rescan",
7400
+ "retention-run"
7401
+ ]);
7402
+ /** Why the operation ran. */
7403
+ var OpsLogReasonSchema = _enum([
7404
+ "retention",
7405
+ "quota",
7406
+ "manual",
7407
+ "operator"
7408
+ ]);
7409
+ /** One audit row, shared verbatim by both domains. */
7410
+ var OpsLogEntrySchema = object({
7411
+ /** Unique row id. */
7412
+ id: string(),
7413
+ /** Epoch ms the operation completed. */
7414
+ at: number(),
7415
+ domain: OpsLogDomainSchema,
7416
+ op: OpsLogOpSchema,
7417
+ reason: OpsLogReasonSchema,
7418
+ /** The camera the op targeted; null for a cluster/global op. */
7419
+ deviceId: number().nullable(),
7420
+ /** Node that performed the op (the log carries nodeId — no cross-node aggregation). */
7421
+ nodeId: string(),
7422
+ /** Buckets / rows deleted (op-specific unit). */
7423
+ itemsAffected: number(),
7424
+ /** Bytes reclaimed by the op (0 when not measurable). */
7425
+ bytesReclaimed: number(),
7426
+ /** Free-text detail (e.g. "floor moved to <ts>"); null when none. */
7427
+ detail: string().nullable(),
7428
+ /** Who/what triggered the op. */
7429
+ actor: string()
7430
+ });
7431
+ /** Shared query input for the per-domain `listOpsLog` cap methods. */
7432
+ var OpsLogQueryInputSchema = object({
7433
+ /** Restrict to a single camera; omit for every row. */
7434
+ deviceId: number().optional(),
7435
+ /** Max rows returned, newest-first. */
7436
+ limit: number().int().min(1).max(1e3).optional()
7437
+ });
7438
+ /**
7383
7439
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
7384
7440
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
7385
7441
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -7670,6 +7726,178 @@ function cosineSimilarity(a, b) {
7670
7726
  const denom = Math.sqrt(normA) * Math.sqrt(normB);
7671
7727
  return denom === 0 ? 0 : dotProduct / denom;
7672
7728
  }
7729
+ var MACRO_LABELS = [
7730
+ {
7731
+ id: "person",
7732
+ name: "Person"
7733
+ },
7734
+ {
7735
+ id: "vehicle",
7736
+ name: "Vehicle"
7737
+ },
7738
+ {
7739
+ id: "animal",
7740
+ name: "Animal"
7741
+ },
7742
+ {
7743
+ id: "package",
7744
+ name: "Package"
7745
+ }
7746
+ ];
7747
+ var COCO_TO_MACRO = {
7748
+ mapping: {
7749
+ person: "person",
7750
+ bicycle: "vehicle",
7751
+ car: "vehicle",
7752
+ motorcycle: "vehicle",
7753
+ airplane: "vehicle",
7754
+ bus: "vehicle",
7755
+ train: "vehicle",
7756
+ truck: "vehicle",
7757
+ boat: "vehicle",
7758
+ bird: "animal",
7759
+ cat: "animal",
7760
+ dog: "animal",
7761
+ horse: "animal",
7762
+ sheep: "animal",
7763
+ cow: "animal",
7764
+ elephant: "animal",
7765
+ bear: "animal",
7766
+ zebra: "animal",
7767
+ giraffe: "animal",
7768
+ suitcase: "package",
7769
+ backpack: "package",
7770
+ handbag: "package"
7771
+ },
7772
+ preserveOriginal: false
7773
+ };
7774
+ var AUDIO_MACRO_LABELS = [
7775
+ {
7776
+ id: "speech",
7777
+ name: "Speech",
7778
+ icon: "🗣️"
7779
+ },
7780
+ {
7781
+ id: "scream",
7782
+ name: "Scream / Shout",
7783
+ icon: "😱"
7784
+ },
7785
+ {
7786
+ id: "crying",
7787
+ name: "Crying / Baby",
7788
+ icon: "😢"
7789
+ },
7790
+ {
7791
+ id: "laughter",
7792
+ name: "Laughter",
7793
+ icon: "😂"
7794
+ },
7795
+ {
7796
+ id: "music",
7797
+ name: "Music",
7798
+ icon: "🎵"
7799
+ },
7800
+ {
7801
+ id: "dog",
7802
+ name: "Dog",
7803
+ icon: "🐕"
7804
+ },
7805
+ {
7806
+ id: "cat",
7807
+ name: "Cat",
7808
+ icon: "🐈"
7809
+ },
7810
+ {
7811
+ id: "bird",
7812
+ name: "Bird",
7813
+ icon: "🐦"
7814
+ },
7815
+ {
7816
+ id: "animal",
7817
+ name: "Animal (other)",
7818
+ icon: "🐾"
7819
+ },
7820
+ {
7821
+ id: "alarm",
7822
+ name: "Alarm / Siren",
7823
+ icon: "🚨"
7824
+ },
7825
+ {
7826
+ id: "doorbell",
7827
+ name: "Doorbell / Knock",
7828
+ icon: "🔔"
7829
+ },
7830
+ {
7831
+ id: "glass_breaking",
7832
+ name: "Glass Breaking",
7833
+ icon: "💥"
7834
+ },
7835
+ {
7836
+ id: "gunshot",
7837
+ name: "Gunshot / Explosion",
7838
+ icon: "💣"
7839
+ },
7840
+ {
7841
+ id: "vehicle",
7842
+ name: "Vehicle",
7843
+ icon: "🚗"
7844
+ },
7845
+ {
7846
+ id: "siren",
7847
+ name: "Emergency Siren",
7848
+ icon: "🚑"
7849
+ },
7850
+ {
7851
+ id: "fire",
7852
+ name: "Fire / Smoke",
7853
+ icon: "🔥"
7854
+ },
7855
+ {
7856
+ id: "water",
7857
+ name: "Water",
7858
+ icon: "💧"
7859
+ },
7860
+ {
7861
+ id: "wind",
7862
+ name: "Wind / Weather",
7863
+ icon: "🌬️"
7864
+ },
7865
+ {
7866
+ id: "door",
7867
+ name: "Door",
7868
+ icon: "🚪"
7869
+ },
7870
+ {
7871
+ id: "footsteps",
7872
+ name: "Footsteps",
7873
+ icon: "👣"
7874
+ },
7875
+ {
7876
+ id: "crowd",
7877
+ name: "Crowd / Chatter",
7878
+ icon: "👥"
7879
+ },
7880
+ {
7881
+ id: "telephone",
7882
+ name: "Telephone",
7883
+ icon: "📞"
7884
+ },
7885
+ {
7886
+ id: "engine",
7887
+ name: "Engine / Motor",
7888
+ icon: "⚙️"
7889
+ },
7890
+ {
7891
+ id: "tools",
7892
+ name: "Tools / Construction",
7893
+ icon: "🔨"
7894
+ },
7895
+ {
7896
+ id: "silence",
7897
+ name: "Silence",
7898
+ icon: "🤫"
7899
+ }
7900
+ ];
7673
7901
  var YAMNET_TO_MACRO = {
7674
7902
  mapping: {
7675
7903
  Speech: "speech",
@@ -7936,6 +8164,133 @@ var _macroLookup = /* @__PURE__ */ new Map();
7936
8164
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7937
8165
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7938
8166
  /**
8167
+ * Unified event-kind taxonomy — THE single source of truth for
8168
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8169
+ * icon }`.
8170
+ *
8171
+ * This dictionary folds together what used to be scattered across four
8172
+ * copies:
8173
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8174
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8175
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8176
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8177
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8178
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8179
+ *
8180
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8181
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8182
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8183
+ * color or an icon: they read this dictionary (server descriptors carry the
8184
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8185
+ *
8186
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8187
+ */
8188
+ var TAXONOMY_COLORS = {
8189
+ motion: "#f59e0b",
8190
+ audio: "#06b6d4",
8191
+ person: "#22c55e",
8192
+ vehicle: "#3b82f6",
8193
+ animal: "#f97316",
8194
+ package: "#a855f7",
8195
+ sensor: "#8b5cf6",
8196
+ control: "#10b981",
8197
+ genericDetection: "#64748b"
8198
+ };
8199
+ /** Global default color when a kind is unknown (matches legacy box-drawer). */
8200
+ var DEFAULT_EVENT_COLOR = "#22ff55";
8201
+ var DETECTION_SUB_COLORS = {
8202
+ car: "#f59e0b",
8203
+ truck: "#d97706",
8204
+ bus: "#b45309",
8205
+ motorcycle: "#eab308",
8206
+ bicycle: "#ca8a04",
8207
+ airplane: "#60a5fa",
8208
+ boat: "#2563eb",
8209
+ train: "#1d4ed8",
8210
+ bird: "#14b8a6",
8211
+ dog: "#84cc16",
8212
+ cat: "#f97316",
8213
+ horse: "#a16207",
8214
+ sheep: "#a3a3a3",
8215
+ cow: "#78716c",
8216
+ elephant: "#6b7280",
8217
+ bear: "#7c2d12",
8218
+ zebra: "#404040",
8219
+ giraffe: "#d4a373"
8220
+ };
8221
+ function titleCase(id) {
8222
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8223
+ }
8224
+ var entries = /* @__PURE__ */ new Map();
8225
+ function macro(kind, category, color, iconId, label) {
8226
+ entries.set(kind, {
8227
+ kind,
8228
+ parentKind: null,
8229
+ level: "macro",
8230
+ category,
8231
+ color,
8232
+ iconId,
8233
+ labelKey: `eventKind.${kind}`,
8234
+ label
8235
+ });
8236
+ }
8237
+ function sub(kind, parentKind, category, color, iconId, label) {
8238
+ entries.set(kind, {
8239
+ kind,
8240
+ parentKind,
8241
+ level: "sub",
8242
+ category,
8243
+ color,
8244
+ iconId,
8245
+ labelKey: `eventKind.${kind}`,
8246
+ label
8247
+ });
8248
+ }
8249
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8250
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8251
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8252
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8253
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8254
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8255
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8256
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8257
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8258
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8259
+ if (entries.has(cocoClass)) continue;
8260
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8261
+ }
8262
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8263
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8264
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8265
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8266
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8267
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8268
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8269
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8270
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8271
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8272
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8273
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8274
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8275
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8276
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8277
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8278
+ sub("button", "control", "control", "#10b981", "button", "Button");
8279
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8280
+ for (const l of AUDIO_MACRO_LABELS) {
8281
+ const kind = `audio-${l.id}`;
8282
+ if (entries.has(kind)) continue;
8283
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8284
+ }
8285
+ /** The complete taxonomy dictionary, keyed by kind. */
8286
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8287
+ /** The sub kinds whose `parentKind` is `macro` (empty for a leaf macro). */
8288
+ function subKindsOf(macro) {
8289
+ const out = [];
8290
+ for (const e of Object.values(EVENT_TAXONOMY)) if (e.parentKind === macro) out.push(e);
8291
+ return out;
8292
+ }
8293
+ /**
7939
8294
  * Error types for the safe expression engine. Two distinct classes so callers
7940
8295
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7941
8296
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -15979,17 +16334,30 @@ var EventKindCategorySchema = _enum([
15979
16334
  "audio",
15980
16335
  "detection",
15981
16336
  "sensor",
16337
+ "control",
15982
16338
  "custom",
15983
16339
  "package"
15984
16340
  ]);
16341
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16342
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
15985
16343
  var EventKindDescriptorSchema = object({
15986
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16344
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
15987
16345
  kind: string(),
16346
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16347
+ labelKey: string(),
16348
+ /** English fallback label (kept for clients that don't translate). */
15988
16349
  label: string(),
15989
16350
  /** Hex color for timeline/legend rendering. */
15990
16351
  color: string(),
16352
+ /** Dictionary id → lucide component on the UI side. */
16353
+ iconId: string(),
16354
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
15991
16355
  icon: EventKindIconSchema,
15992
16356
  category: EventKindCategorySchema,
16357
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16358
+ parentKind: string().nullable(),
16359
+ /** Derived from `parentKind`, explicit for the client tree. */
16360
+ level: EventKindLevelSchema,
15993
16361
  /** Which cap + device contributes this kind. For built-ins the camera
15994
16362
  * itself; for sensor kinds the LINKED source device. */
15995
16363
  source: object({
@@ -16062,11 +16430,21 @@ var TrackAudioLabelSchema = object({
16062
16430
  firstAt: number(),
16063
16431
  lastAt: number()
16064
16432
  });
16433
+ /**
16434
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16435
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16436
+ * linked sensor/control state change (no positions; carries a snapshot). The
16437
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16438
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16439
+ */
16440
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16065
16441
  var TrackSchema = object({
16066
16442
  trackId: string(),
16067
16443
  deviceId: number(),
16068
16444
  className: string(),
16069
16445
  label: string().optional(),
16446
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16447
+ source: TrackSourceSchema.optional(),
16070
16448
  firstSeen: number(),
16071
16449
  lastSeen: number(),
16072
16450
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16321,6 +16699,26 @@ var TrackCascadeCountsSchema = object({
16321
16699
  /** Per-track CLIP search vectors removed (best-effort). */
16322
16700
  embeddings: number().int()
16323
16701
  });
16702
+ /** Event-store footprint for one camera. */
16703
+ var EventStoreDeviceFootprintSchema = object({
16704
+ deviceId: number(),
16705
+ /** Persisted event rows (motion + object + audio) for the camera. */
16706
+ rows: number().int(),
16707
+ /** Event-owned media bytes on disk for the camera. */
16708
+ bytes: number().int()
16709
+ });
16710
+ /** Aggregate event-store footprint: global totals + per-camera breakdown. */
16711
+ var EventStoreFootprintSchema = object({
16712
+ totalRows: number().int(),
16713
+ totalBytes: number().int(),
16714
+ devices: array(EventStoreDeviceFootprintSchema).readonly()
16715
+ });
16716
+ /** Per-kind counts returned by the event-prune / device-delete mutations. */
16717
+ var EventPruneCountsSchema = object({
16718
+ motion: number().int(),
16719
+ object: number().int(),
16720
+ audio: number().int()
16721
+ });
16324
16722
  var pipelineAnalyticsCapability = {
16325
16723
  name: "pipeline-analytics",
16326
16724
  scope: "device",
@@ -16482,6 +16880,45 @@ var pipelineAnalyticsCapability = {
16482
16880
  kind: "mutation",
16483
16881
  auth: "admin"
16484
16882
  }),
16883
+ /**
16884
+ * Durable event-store footprint for the management UI: event rows
16885
+ * (motion + object + audio) counted per camera + total, plus the
16886
+ * event-owned media bytes on disk per camera + total. Stat/count-based,
16887
+ * computed on demand.
16888
+ */
16889
+ getEventStoreFootprint: method(object({}), EventStoreFootprintSchema, {
16890
+ kind: "query",
16891
+ auth: "admin"
16892
+ }),
16893
+ /**
16894
+ * Cluster-wide prune of events older than `olderThanMs` (exclusive) across
16895
+ * every camera, deleting each event's media in lockstep. Logged to the
16896
+ * events ops-log with `reason` (default `'retention'`). Returns the summed
16897
+ * per-kind deleted counts.
16898
+ */
16899
+ pruneEvents: method(object({
16900
+ olderThanMs: number(),
16901
+ reason: OpsLogReasonSchema.optional()
16902
+ }), EventPruneCountsSchema, {
16903
+ kind: "mutation",
16904
+ auth: "admin"
16905
+ }),
16906
+ /**
16907
+ * Manually delete EVERY event (motion + object + audio) for one camera and
16908
+ * its event-owned media in lockstep. Logged to the events ops-log as
16909
+ * `op:'manual-delete', reason:'manual'`. Destructive — the admin UI guards
16910
+ * it behind a confirm.
16911
+ */
16912
+ deleteDeviceEvents: method(object({ deviceId: number() }), EventPruneCountsSchema, {
16913
+ kind: "mutation",
16914
+ auth: "admin"
16915
+ }),
16916
+ /** The events ops-log rows (newest-first), optionally scoped to one camera.
16917
+ * Backed by a declared pipeline-analytics SQLite collection. */
16918
+ listOpsLog: method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
16919
+ kind: "query",
16920
+ auth: "admin"
16921
+ }),
16485
16922
  getEventMedia: method(object({
16486
16923
  eventId: string(),
16487
16924
  kind: MediaFileKindEnum.optional()
@@ -16536,53 +16973,105 @@ var pipelineAnalyticsCapability = {
16536
16973
  }
16537
16974
  };
16538
16975
  /**
16539
- * Sensor cap name static event-kind descriptor. A linked device
16540
- * contributes one entry per bound cap present in this map.
16976
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16977
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16978
+ * caps into per-camera event-kind descriptors.
16979
+ *
16980
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16981
+ * is NOT duplicated here — every entry is derived from the single
16982
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16983
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16984
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16985
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16986
+ * eventful cap is missing.
16541
16987
  */
16542
- var EVENT_KIND_BY_CAP = {
16543
- contact: {
16544
- kind: "contact",
16545
- label: "Contact",
16546
- color: "#f59e0b",
16547
- icon: "door",
16548
- category: "sensor"
16549
- },
16550
- flood: {
16551
- kind: "flood",
16552
- label: "Water leak",
16553
- color: "#3b82f6",
16554
- icon: "water",
16555
- category: "sensor"
16556
- },
16557
- gas: {
16558
- kind: "gas",
16559
- label: "Gas",
16560
- color: "#ef4444",
16561
- icon: "smoke",
16562
- category: "sensor"
16563
- },
16564
- "carbon-monoxide": {
16565
- kind: "carbon-monoxide",
16566
- label: "Carbon monoxide",
16567
- color: "#dc2626",
16568
- icon: "smoke",
16569
- category: "sensor"
16570
- },
16571
- "enum-sensor": {
16572
- kind: "enum-sensor",
16573
- label: "Sensor state",
16574
- color: "#8b5cf6",
16575
- icon: "generic",
16576
- category: "sensor"
16577
- },
16578
- "event-emitter": {
16579
- kind: "device-event",
16580
- label: "Device event",
16581
- color: "#10b981",
16582
- icon: "button",
16583
- category: "sensor"
16584
- }
16988
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16989
+ var LEGACY_ICON = {
16990
+ motion: "motion",
16991
+ audio: "audio",
16992
+ person: "person",
16993
+ vehicle: "vehicle",
16994
+ animal: "animal",
16995
+ package: "package",
16996
+ door: "door",
16997
+ pir: "pir",
16998
+ smoke: "smoke",
16999
+ water: "water",
17000
+ button: "button",
17001
+ generic: "generic",
17002
+ gas: "smoke",
17003
+ vibration: "generic",
17004
+ tamper: "generic",
17005
+ presence: "person",
17006
+ lock: "generic",
17007
+ siren: "generic",
17008
+ switch: "generic",
17009
+ doorbell: "button"
17010
+ };
17011
+ function legacyIcon(iconId) {
17012
+ return LEGACY_ICON[iconId] ?? "generic";
17013
+ }
17014
+ /**
17015
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
17016
+ * The anti-drift guard cross-checks this against the eventful caps declared
17017
+ * in `packages/types/src/capabilities/*.cap.ts`.
17018
+ */
17019
+ var CAP_TO_KIND = {
17020
+ contact: "contact",
17021
+ motion: "motion-sensor",
17022
+ smoke: "smoke",
17023
+ flood: "flood",
17024
+ gas: "gas",
17025
+ "carbon-monoxide": "carbon-monoxide",
17026
+ vibration: "vibration",
17027
+ tamper: "tamper",
17028
+ presence: "presence",
17029
+ "enum-sensor": "enum-sensor",
17030
+ "event-emitter": "device-event",
17031
+ "lock-control": "lock",
17032
+ switch: "switch",
17033
+ button: "button",
17034
+ doorbell: "doorbell"
16585
17035
  };
17036
+ function buildDescriptor(capName, kind) {
17037
+ const t = EVENT_TAXONOMY[kind];
17038
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
17039
+ return {
17040
+ ...t,
17041
+ icon: legacyIcon(t.iconId)
17042
+ };
17043
+ }
17044
+ /**
17045
+ * Sensor / control cap name → static event-kind descriptor. A linked device
17046
+ * contributes one entry per bound cap present in this map.
17047
+ */
17048
+ var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
17049
+ /**
17050
+ * Build a full `EventKindDescriptor` for a taxonomy `kind`, stamping the
17051
+ * per-device `source`. Returns null when `kind` is not in the taxonomy.
17052
+ * This is THE bridge from the serializable taxonomy dictionary to the cap
17053
+ * wire shape — every event-kind descriptor the server emits goes through it,
17054
+ * so color/iconId/labelKey are never re-declared at a call site.
17055
+ */
17056
+ function buildEventKindDescriptor(kind, source) {
17057
+ const t = EVENT_TAXONOMY[kind];
17058
+ if (t === void 0) return null;
17059
+ return {
17060
+ kind: t.kind,
17061
+ labelKey: t.labelKey,
17062
+ label: t.label,
17063
+ color: t.color,
17064
+ iconId: t.iconId,
17065
+ icon: legacyIcon(t.iconId),
17066
+ category: t.category,
17067
+ parentKind: t.parentKind,
17068
+ level: t.level,
17069
+ source: {
17070
+ capName: source.capName,
17071
+ deviceId: source.deviceId
17072
+ }
17073
+ };
17074
+ }
16586
17075
  var CameraPipelineConfigSchema = object({
16587
17076
  engine: PipelineEngineChoiceSchema.optional(),
16588
17077
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19862,13 +20351,29 @@ method(object({
19862
20351
  }), method(object({ deviceId: number() }), RecordingStatusSchema, {
19863
20352
  kind: "mutation",
19864
20353
  auth: "admin"
19865
- }), method(object({ deviceId: number() }), object({
20354
+ }), method(object({
20355
+ deviceId: number(),
20356
+ reason: OpsLogReasonSchema.optional()
20357
+ }), object({
19866
20358
  floorMs: number().nullable(),
19867
20359
  deletedBuckets: number().int(),
19868
20360
  reclaimedBytes: number().int()
19869
20361
  }), {
19870
20362
  kind: "mutation",
19871
20363
  auth: "admin"
20364
+ }), method(object({
20365
+ deviceId: number(),
20366
+ fromMs: number().optional(),
20367
+ toMs: number().optional()
20368
+ }), object({
20369
+ deletedBuckets: number().int(),
20370
+ reclaimedBytes: number().int()
20371
+ }), {
20372
+ kind: "mutation",
20373
+ auth: "admin"
20374
+ }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20375
+ kind: "query",
20376
+ auth: "admin"
19872
20377
  });
19873
20378
  /**
19874
20379
  * `recordingExport` cap — render a footage time range into a single downloadable
@@ -22990,6 +23495,12 @@ Object.freeze({
22990
23495
  addonId: null,
22991
23496
  access: "delete"
22992
23497
  },
23498
+ "pipelineAnalytics.deleteDeviceEvents": {
23499
+ capName: "pipeline-analytics",
23500
+ capScope: "device",
23501
+ addonId: null,
23502
+ access: "delete"
23503
+ },
22993
23504
  "pipelineAnalytics.deleteTracks": {
22994
23505
  capName: "pipeline-analytics",
22995
23506
  capScope: "device",
@@ -23020,6 +23531,12 @@ Object.freeze({
23020
23531
  addonId: null,
23021
23532
  access: "view"
23022
23533
  },
23534
+ "pipelineAnalytics.getEventStoreFootprint": {
23535
+ capName: "pipeline-analytics",
23536
+ capScope: "device",
23537
+ addonId: null,
23538
+ access: "view"
23539
+ },
23023
23540
  "pipelineAnalytics.getKeyEvents": {
23024
23541
  capName: "pipeline-analytics",
23025
23542
  capScope: "device",
@@ -23062,6 +23579,12 @@ Object.freeze({
23062
23579
  addonId: null,
23063
23580
  access: "view"
23064
23581
  },
23582
+ "pipelineAnalytics.listOpsLog": {
23583
+ capName: "pipeline-analytics",
23584
+ capScope: "device",
23585
+ addonId: null,
23586
+ access: "view"
23587
+ },
23065
23588
  "pipelineAnalytics.listRecentTracks": {
23066
23589
  capName: "pipeline-analytics",
23067
23590
  capScope: "device",
@@ -23074,6 +23597,12 @@ Object.freeze({
23074
23597
  addonId: null,
23075
23598
  access: "view"
23076
23599
  },
23600
+ "pipelineAnalytics.pruneEvents": {
23601
+ capName: "pipeline-analytics",
23602
+ capScope: "device",
23603
+ addonId: null,
23604
+ access: "create"
23605
+ },
23077
23606
  "pipelineAnalytics.pruneEventsBefore": {
23078
23607
  capName: "pipeline-analytics",
23079
23608
  capScope: "device",
@@ -23836,6 +24365,12 @@ Object.freeze({
23836
24365
  addonId: null,
23837
24366
  access: "create"
23838
24367
  },
24368
+ "recording.deleteFootprint": {
24369
+ capName: "recording",
24370
+ capScope: "system",
24371
+ addonId: null,
24372
+ access: "delete"
24373
+ },
23839
24374
  "recording.getAvailability": {
23840
24375
  capName: "recording",
23841
24376
  capScope: "system",
@@ -23866,6 +24401,12 @@ Object.freeze({
23866
24401
  addonId: null,
23867
24402
  access: "view"
23868
24403
  },
24404
+ "recording.listOpsLog": {
24405
+ capName: "recording",
24406
+ capScope: "system",
24407
+ addonId: null,
24408
+ access: "view"
24409
+ },
23869
24410
  "recording.locateSegment": {
23870
24411
  capName: "recording",
23871
24412
  capScope: "system",
@@ -25097,6 +25638,12 @@ Object.defineProperty(exports, "BaseAddon", {
25097
25638
  return BaseAddon;
25098
25639
  }
25099
25640
  });
25641
+ Object.defineProperty(exports, "DEFAULT_EVENT_COLOR", {
25642
+ enumerable: true,
25643
+ get: function() {
25644
+ return DEFAULT_EVENT_COLOR;
25645
+ }
25646
+ });
25100
25647
  Object.defineProperty(exports, "DeviceType", {
25101
25648
  enumerable: true,
25102
25649
  get: function() {
@@ -25121,6 +25668,18 @@ Object.defineProperty(exports, "EventCategory", {
25121
25668
  return EventCategory;
25122
25669
  }
25123
25670
  });
25671
+ Object.defineProperty(exports, "MACRO_LABELS", {
25672
+ enumerable: true,
25673
+ get: function() {
25674
+ return MACRO_LABELS;
25675
+ }
25676
+ });
25677
+ Object.defineProperty(exports, "OpsLogEntrySchema", {
25678
+ enumerable: true,
25679
+ get: function() {
25680
+ return OpsLogEntrySchema;
25681
+ }
25682
+ });
25124
25683
  Object.defineProperty(exports, "__toESM", {
25125
25684
  enumerable: true,
25126
25685
  get: function() {
@@ -25151,6 +25710,12 @@ Object.defineProperty(exports, "boolean", {
25151
25710
  return boolean;
25152
25711
  }
25153
25712
  });
25713
+ Object.defineProperty(exports, "buildEventKindDescriptor", {
25714
+ enumerable: true,
25715
+ get: function() {
25716
+ return buildEventKindDescriptor;
25717
+ }
25718
+ });
25154
25719
  Object.defineProperty(exports, "cosineSimilarity", {
25155
25720
  enumerable: true,
25156
25721
  get: function() {
@@ -25229,6 +25794,12 @@ Object.defineProperty(exports, "string", {
25229
25794
  return string;
25230
25795
  }
25231
25796
  });
25797
+ Object.defineProperty(exports, "subKindsOf", {
25798
+ enumerable: true,
25799
+ get: function() {
25800
+ return subKindsOf;
25801
+ }
25802
+ });
25232
25803
  Object.defineProperty(exports, "videoclipsCapability", {
25233
25804
  enumerable: true,
25234
25805
  get: function() {