@camstack/addon-provider-gree 0.2.98 → 0.2.100

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.
Files changed (3) hide show
  1. package/dist/addon.js +292 -58
  2. package/dist/addon.mjs +292 -58
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let dgram = require("dgram");
3
3
  let crypto$1 = require("crypto");
4
4
  let events = require("events");
5
- //#region ../types/dist/event-category-DAXzJeTX.mjs
5
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
6
6
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
7
7
  EventCategory["SystemBoot"] = "system.boot";
8
8
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -563,7 +563,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
563
563
  * a package zone — a newly-appeared stationary object of a package class
564
564
  * that cleared the class / zone / dwell / size gates. Payload:
565
565
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
566
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
566
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
567
567
  * Telemetry (D8): the durable record is the `package-events` store row;
568
568
  * this bus topic drives notifier rules + live UI. See
569
569
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5349,7 +5349,7 @@ var ZodIssueCode = {
5349
5349
  var ZodFirstPartyTypeKind;
5350
5350
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5351
5351
  //#endregion
5352
- //#region ../types/dist/sleep-BnujYGPe.mjs
5352
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5353
5353
  /**
5354
5354
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5355
5355
  * window to float samples (D455).
@@ -7316,13 +7316,30 @@ new Set([
7316
7316
  "scene",
7317
7317
  "motion",
7318
7318
  "object",
7319
- "audio"
7319
+ "audio",
7320
+ "mosaic"
7320
7321
  ]);
7321
- new Set([
7322
+ /**
7323
+ * The owner types that are event tables, in the order the event-media resolver
7324
+ * should consider them. Exported so a consumer asking "is this key an event?"
7325
+ * does not re-spell the list and drift from it.
7326
+ */
7327
+ var EVENT_OWNER_TYPES = [
7322
7328
  "motion",
7323
7329
  "object",
7324
7330
  "audio"
7325
- ]);
7331
+ ];
7332
+ /**
7333
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7334
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7335
+ * `(table, id)`).
7336
+ *
7337
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7338
+ * table would otherwise be accepted by the codec and refused at the door, with
7339
+ * nothing failing until a caller asked.
7340
+ */
7341
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7342
+ new Set(EVENT_OWNER_TYPES);
7326
7343
  var EncodeProfileSchema = object({
7327
7344
  video: object({
7328
7345
  codec: _enum([
@@ -7361,7 +7378,23 @@ var EncodeProfileSchema = object({
7361
7378
  "zerolatency",
7362
7379
  "film",
7363
7380
  "animation"
7364
- ]).optional()
7381
+ ]).optional(),
7382
+ /**
7383
+ * ONE slice per access unit.
7384
+ *
7385
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7386
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7387
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7388
+ * frame-end per frame: the first pictures render and the video then freezes
7389
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7390
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7391
+ *
7392
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7393
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7394
+ * consumers meaning the same thing spelled differently would stop sharing one
7395
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7396
+ */
7397
+ singleSlicePerFrame: boolean().optional()
7365
7398
  }),
7366
7399
  audio: union([literal("passthrough"), object({
7367
7400
  codec: _enum([
@@ -7411,7 +7444,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7411
7444
  audio: "passthrough"
7412
7445
  };
7413
7446
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7414
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7447
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7415
7448
  /**
7416
7449
  * Deep wiring healthcheck — snapshot of active reachability probes across
7417
7450
  * every declared capability + widget of every installed plugin, on every
@@ -12521,6 +12554,16 @@ method(object({
12521
12554
  filter: QueryFilterSchema.optional(),
12522
12555
  columns: array(string()).readonly().optional()
12523
12556
  }), array(SettingsRecordSchema).readonly()), method(object({
12557
+ namespace: string().optional(),
12558
+ collection: string(),
12559
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12560
+ * index on the collection. Never empty. */
12561
+ conflict: array(string()).readonly(),
12562
+ record: BulkRecordSchema
12563
+ }), object({
12564
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12565
+ * row's own id on an update. */
12566
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12524
12567
  namespace: string().optional(),
12525
12568
  collection: string(),
12526
12569
  record: object({
@@ -12655,6 +12698,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12655
12698
  id: union([string(), number()]) }), {
12656
12699
  kind: "mutation",
12657
12700
  auth: "admin"
12701
+ }), method(object({
12702
+ namespace: string().optional(),
12703
+ collection: string(),
12704
+ conflict: array(string()).readonly(),
12705
+ record: BulkRecordSchema
12706
+ }), object({ id: union([string(), number()]) }), {
12707
+ kind: "mutation",
12708
+ auth: "admin"
12658
12709
  }), method(object({
12659
12710
  namespace: string().optional(),
12660
12711
  collection: string(),
@@ -19137,8 +19188,8 @@ var TrackPositionSchema = object({
19137
19188
  var TrackSnapshotSchema = object({
19138
19189
  timestamp: number(),
19139
19190
  position: TrackPositionSchema,
19140
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19141
- mediaKey: string()
19191
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19192
+ mediaId: number().int()
19142
19193
  });
19143
19194
  /**
19144
19195
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19742,14 +19793,14 @@ var ObjectEventSchema = object({
19742
19793
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19743
19794
  frameWidth: number().optional(),
19744
19795
  frameHeight: number().optional(),
19745
- /** MediaStore key for the crop attached to this event (if any). */
19746
- mediaKey: string().optional(),
19747
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19796
+ /** Media row id of the crop attached to this event (if any) — D482. */
19797
+ mediaId: number().int().optional(),
19798
+ /** Design B: media row id of the track's native-resolution key frame (the
19748
19799
  * best-detection full frame). Resolve via the event-media data-plane
19749
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19800
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19750
19801
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19751
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19752
- keyFrameMediaKey: string().optional(),
19802
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19803
+ keyFrameMediaId: number().int().optional(),
19753
19804
  /** Populated by B5 (recording playback URL for this event). */
19754
19805
  mediaUrl: string().optional(),
19755
19806
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19799,7 +19850,7 @@ var MediaFileKindEnum = _enum([
19799
19850
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19800
19851
  *
19801
19852
  * `url` points at the `event-media` data plane
19802
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19853
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19803
19854
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19804
19855
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19805
19856
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19807,10 +19858,13 @@ var MediaFileKindEnum = _enum([
19807
19858
  * `data-plane-access.ts` for the rule and the one gap it does not close
19808
19859
  * (per-device scoping).
19809
19860
  *
19810
- * The URL is built from the row's **stored** key, which is not always its
19811
- * published `kind`: a track's face/plate crop is stored as `crop` under
19861
+ * The URL is built from the row's own id, which since D482 says nothing about
19862
+ * the row's owner or kind — and that is the point. The published `kind` is not
19863
+ * the stored one (a track's face/plate crop is stored as `crop` under
19812
19864
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19813
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19865
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19866
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19867
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19814
19868
  *
19815
19869
  * ## `base64` is TRANSITIONAL and is going away
19816
19870
  *
@@ -19823,7 +19877,19 @@ var MediaFileKindEnum = _enum([
19823
19877
  * `analytics-query-facade.ts`; nothing else reads it.
19824
19878
  */
19825
19879
  var MediaFileSchema = object({
19826
- key: string(),
19880
+ /**
19881
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19882
+ *
19883
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19884
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19885
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19886
+ * different rows that spell the same number, and a track whose
19887
+ * `bestEventId` was an object event was served a motion event's snapshot —
19888
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19889
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19890
+ * at all.
19891
+ */
19892
+ mediaId: number().int(),
19827
19893
  kind: MediaFileKindEnum,
19828
19894
  sizeBytes: number(),
19829
19895
  timestamp: number()
@@ -19931,7 +19997,7 @@ var RetrainTrackSchema = object({
19931
19997
  });
19932
19998
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19933
19999
  var RetrainFrameCandidateSchema = object({
19934
- mediaKey: string(),
20000
+ mediaId: number().int(),
19935
20001
  kind: MediaFileKindEnum,
19936
20002
  timestamp: number(),
19937
20003
  sizeBytes: number().int(),
@@ -19945,7 +20011,7 @@ var RetrainFrameSchema = object({
19945
20011
  deviceId: number(),
19946
20012
  trackId: string(),
19947
20013
  /** Provenance only. It may already point at nothing — that is expected. */
19948
- sourceMediaKey: string(),
20014
+ sourceMediaId: number().int(),
19949
20015
  sourceKind: MediaFileKindEnum,
19950
20016
  sizeBytes: number().int(),
19951
20017
  width: number().int(),
@@ -19961,7 +20027,7 @@ var RetrainCopyRefusalSchema = _enum([
19961
20027
  var RetrainFrameSelectionSchema = object({
19962
20028
  copied: array(RetrainFrameSchema).readonly(),
19963
20029
  refused: array(object({
19964
- sourceMediaKey: string(),
20030
+ sourceMediaId: number().int(),
19965
20031
  reason: RetrainCopyRefusalSchema
19966
20032
  })).readonly()
19967
20033
  });
@@ -19969,7 +20035,7 @@ var RetrainFrameListSchema = object({
19969
20035
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19970
20036
  copies: array(RetrainFrameSchema).readonly(),
19971
20037
  /** What the page pre-selects — the native key frame when one survives. */
19972
- autoPickMediaKey: string().optional()
20038
+ autoPickMediaId: number().int().optional()
19973
20039
  });
19974
20040
  /** What the operator asked the assist to look for. */
19975
20041
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20064,9 +20130,11 @@ var RecentTracksQueryInput = object({
20064
20130
  });
20065
20131
  /**
20066
20132
  * A Summary (D359): the per-camera session envelope that references tracks.
20067
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20068
- * served by the event-media plane by key), null until the first frame with a
20069
- * member visible has been materialised.
20133
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20134
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20135
+ * first frame with a member visible has been materialised (D482 — it was the
20136
+ * row's composite key, which embedded the owner and could name another table's
20137
+ * row once event ids became per-table rowids).
20070
20138
  */
20071
20139
  var SummarySchema = object({
20072
20140
  id: string(),
@@ -20075,19 +20143,115 @@ var SummarySchema = object({
20075
20143
  lastActiveAt: number(),
20076
20144
  sealedAt: number().nullable(),
20077
20145
  memberCount: number().int().nonnegative(),
20078
- groupShotKey: string().nullable()
20146
+ groupShotMediaId: number().int().nullable()
20079
20147
  });
20080
20148
  var SummariesQueryInput = object({
20081
20149
  deviceIds: array(number()).min(1),
20082
20150
  since: number().optional(),
20083
20151
  until: number().optional(),
20084
- limit: number().int().min(1).max(200).default(50)
20152
+ limit: number().int().min(1).max(200).default(50),
20153
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20154
+ * meaning of "dog" on every summary surface. */
20155
+ classes: array(string().min(1).max(60)).max(40).optional()
20085
20156
  });
20086
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20087
20157
  var SummaryDetailSchema = object({
20088
20158
  summary: SummarySchema,
20089
- /** Member track ids, in absorption order. */
20090
- trackIds: array(string()).readonly()
20159
+ /** Members in absorption order, oldest join first. */
20160
+ members: array(object({
20161
+ trackId: string(),
20162
+ /**
20163
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20164
+ * and the track no longer exists to fill it.
20165
+ *
20166
+ * The join, not the latest opinion: class voting can re-decide a track's
20167
+ * class afterwards, and a group's composition is the history of who came
20168
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20169
+ * dog" — rather than being handed a second, denormalised tally that would
20170
+ * drift from the members it claims to describe.
20171
+ */
20172
+ className: string().nullable(),
20173
+ /** `primary` is the one member that carries the group's identity. */
20174
+ role: _enum([
20175
+ "primary",
20176
+ "sibling",
20177
+ "child"
20178
+ ]).nullable(),
20179
+ joinedAt: number(),
20180
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20181
+ * at read time. Absent when nothing resolved, never an empty string. */
20182
+ label: string().optional(),
20183
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20184
+ * recognised nobody, and absent for a member whose track has been pruned —
20185
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20186
+ subLabel: string().optional()
20187
+ })).readonly()
20188
+ });
20189
+ /**
20190
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20191
+ *
20192
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20193
+ * we extend when we notice we need more". Enough to draw the card ("two people
20194
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20195
+ * are on {@link getSummary}: shipping every member of every group so a client
20196
+ * can count them turns a page of two hundred groups into a thousand records to
20197
+ * draw two hundred badges.
20198
+ */
20199
+ var SummaryListItemSchema = object({
20200
+ summary: SummarySchema,
20201
+ /** Class counts, most numerous first; `className: null` is a member whose
20202
+ * class never resolved, counted so the counts add up to `memberCount`. */
20203
+ classes: array(object({
20204
+ className: string().nullable(),
20205
+ count: number().int().positive()
20206
+ })).readonly()
20207
+ });
20208
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20209
+ /**
20210
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20211
+ *
20212
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20213
+ * summaries feed that could only answer "the last N for these cameras"
20214
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20215
+ * else. Two feeds with different ordering contracts would be two ideas of what
20216
+ * "newest" means, on one screen.
20217
+ */
20218
+ var RecentSummariesQueryInput = object({
20219
+ /** Devices to merge. An empty array yields an empty page. */
20220
+ deviceIds: array(number()),
20221
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20222
+ since: number().optional(),
20223
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20224
+ until: number().optional(),
20225
+ limit: number().int().min(1).max(500).default(100),
20226
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20227
+ cursor: string().optional(),
20228
+ /**
20229
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20230
+ * deliberately the same name: the events filter tree emits one list of ticked
20231
+ * taxonomy nodes, and a summaries feed that called it something else would
20232
+ * make the operator's one filter component produce two shapes.
20233
+ *
20234
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20235
+ * me the arrivals with a dog" has to return the group of four people who had
20236
+ * one with them; a filter on the group's own fields could only ever answer
20237
+ * about its primary.
20238
+ *
20239
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20240
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20241
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20242
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20243
+ * that read only the class would answer "animal" and never "dog".
20244
+ *
20245
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20246
+ */
20247
+ classes: array(string().min(1).max(60)).max(40).optional()
20248
+ });
20249
+ var RecentSummariesPageSchema = object({
20250
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20251
+ * {@link listSummaries} — one list shape, so a card renders the same
20252
+ * whichever feed drew it. */
20253
+ summaries: array(SummaryListItemSchema).readonly(),
20254
+ nextCursor: string().nullable()
20091
20255
  });
20092
20256
  var RecentTracksPageSchema = object({
20093
20257
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20501,7 +20665,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20501
20665
  classes: array(string()).optional(),
20502
20666
  /** See {@link ExcludeSourcesDoc}. */
20503
20667
  excludeSources: array(TrackSourceSchema).optional()
20504
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20668
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20505
20669
  kind: "mutation",
20506
20670
  auth: "admin"
20507
20671
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -20629,7 +20793,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20629
20793
  }), method(object({
20630
20794
  deviceId: number(),
20631
20795
  trackId: string(),
20632
- mediaKeys: array(string()).min(1)
20796
+ mediaIds: array(number().int()).min(1)
20633
20797
  }), RetrainFrameSelectionSchema, {
20634
20798
  kind: "mutation",
20635
20799
  auth: "admin"
@@ -20697,7 +20861,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20697
20861
  kind: "query",
20698
20862
  auth: "admin"
20699
20863
  }), method(object({
20700
- eventId: string(),
20864
+ /** Which of the three event tables owns the row. */
20865
+ ownerType: EventOwnerTypeSchema,
20866
+ /** The event's rowid in that table (D474: an event id is a number). */
20867
+ eventId: number().int(),
20701
20868
  kind: MediaFileKindEnum.optional(),
20702
20869
  deviceId: number()
20703
20870
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20708,7 +20875,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20708
20875
  trackId: string(),
20709
20876
  deviceId: number()
20710
20877
  }), array(MediaFileInfoSchema).readonly()), method(object({
20711
- eventId: string(),
20878
+ ownerType: EventOwnerTypeSchema,
20879
+ eventId: number().int(),
20712
20880
  deviceId: number()
20713
20881
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20714
20882
  kind: "mutation",
@@ -25901,8 +26069,8 @@ var IdentitySchema = object({
25901
26069
  id: string(),
25902
26070
  name: string(),
25903
26071
  sampleCount: number().int().nonnegative(),
25904
- coverMediaKey: string().optional(),
25905
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
26072
+ coverMediaId: number().int().optional(),
26073
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25906
26074
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25907
26075
  coverBase64: string().optional()
25908
26076
  });
@@ -25928,8 +26096,8 @@ var IdentitySampleAuditRowSchema = object({
25928
26096
  enrolledAt: number().int(),
25929
26097
  deviceId: number().int().optional(),
25930
26098
  source: _enum(["detected", "photo"]),
25931
- /** The enrolled crop's media key, so the panel can show the evidence. */
25932
- mediaKey: string().optional(),
26099
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
26100
+ mediaId: number().int().optional(),
25933
26101
  /**
25934
26102
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25935
26103
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25998,15 +26166,15 @@ var FaceInfoSchema = object({
25998
26166
  */
25999
26167
  cropUrl: string().optional(),
26000
26168
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
26001
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26169
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
26002
26170
  * legacy rows written before design B. */
26003
26171
  faceBbox: BoundingBoxSchema.optional(),
26004
26172
  /** Design B: MediaStore key of the track's native-resolution key frame.
26005
26173
  * Fetch the native JPEG via the event-media data-plane
26006
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26174
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26007
26175
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26008
26176
  * back to the inline `base64` face crop. */
26009
- keyFrameMediaKey: string().optional(),
26177
+ keyFrameMediaId: number().int().optional(),
26010
26178
  /** Winning identity-match cosine (0..1) for this face's track, when an
26011
26179
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26012
26180
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26075,11 +26243,14 @@ var FaceClusterSchema = object({
26075
26243
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26076
26244
  * dialog already rendering its key FRAME from the `event-media` plane.
26077
26245
  *
26078
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26079
- * media key directly, so this needed no new plane and no new access decision.
26246
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26247
+ * resolves a media row by its own id, so this needed no new plane and no new
26248
+ * access decision.
26080
26249
  */
26081
26250
  var MediaFileLiteSchema$1 = object({
26082
- key: string(),
26251
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26252
+ * (D482). It was the row's composite key, which embedded the owner. */
26253
+ mediaId: number().int(),
26083
26254
  kind: string(),
26084
26255
  url: string(),
26085
26256
  sizeBytes: number(),
@@ -26096,7 +26267,7 @@ method(object({
26096
26267
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26097
26268
  * four times and the viewer holds at `staleTime: 30_000`.
26098
26269
  *
26099
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26270
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26100
26271
  * the `event-media` plane serves that key `immutable` with an ETag.
26101
26272
  *
26102
26273
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29459,8 +29630,8 @@ var VehicleSchema = object({
29459
29630
  id: string(),
29460
29631
  name: string(),
29461
29632
  sampleCount: number().int().nonnegative(),
29462
- coverMediaKey: string().optional(),
29463
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29633
+ coverMediaId: number().int().optional(),
29634
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29464
29635
  coverBase64: string().optional()
29465
29636
  });
29466
29637
  var VehicleSampleInfoSchema = object({
@@ -29493,7 +29664,7 @@ var PlateInfoSchema = object({
29493
29664
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29494
29665
  plateBbox: BoundingBoxSchema.optional(),
29495
29666
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29496
- keyFrameMediaKey: string().optional(),
29667
+ keyFrameMediaId: number().int().optional(),
29497
29668
  base64: string().optional(),
29498
29669
  /**
29499
29670
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29514,11 +29685,14 @@ var PlateInfoSchema = object({
29514
29685
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29515
29686
  * dialog already rendering its key FRAME from the `event-media` plane.
29516
29687
  *
29517
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29518
- * media key directly, so this needed no new plane and no new access decision.
29688
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29689
+ * resolves a media row by its own id, so this needed no new plane and no new
29690
+ * access decision.
29519
29691
  */
29520
29692
  var MediaFileLiteSchema = object({
29521
- key: string(),
29693
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29694
+ * (D482). It was the row's composite key, which embedded the owner. */
29695
+ mediaId: number().int(),
29522
29696
  kind: string(),
29523
29697
  url: string(),
29524
29698
  sizeBytes: number(),
@@ -29577,7 +29751,7 @@ method(object({
29577
29751
  }), method(object({
29578
29752
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29579
29753
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29580
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29754
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29581
29755
  * INPUT field: stripped by the hub router until the train ships, which
29582
29756
  * resolves to `false` and is exactly the intended default. */
29583
29757
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31146,7 +31320,14 @@ var SceneReferenceSchema = object({
31146
31320
  modelId: string(),
31147
31321
  condition: SceneConditionSchema,
31148
31322
  capturedAt: number(),
31149
- thumbnailMediaId: string().optional(),
31323
+ /**
31324
+ * The reference thumbnail's media ROW id (D482).
31325
+ *
31326
+ * The name already said `mediaId` while the value was the row's composite
31327
+ * key — a leftover that read as done and was not. It is the row's own id
31328
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31329
+ */
31330
+ thumbnailMediaId: number().int().optional(),
31150
31331
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31151
31332
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31152
31333
  * normalized rect frame a different piece of world, and the scene would
@@ -32237,7 +32418,29 @@ var LoggingSettingsPatchSchema = object({
32237
32418
  * disarmed the channels it did not mention would make the Levels page and
32238
32419
  * the Diagnostics page fight over the same value.
32239
32420
  */
32240
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
32421
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
32422
+ /**
32423
+ * How many days the analytics OPS LOG is kept — the audit trail of what
32424
+ * retention actually deleted.
32425
+ *
32426
+ * It lives HERE, on the logging document, and not on the analytics addon's
32427
+ * settings form, on the operator's instruction: every other log-ish window in
32428
+ * this system is configured through this one document, and a retention audit
32429
+ * configured somewhere else is a second place to look when the question is
32430
+ * "what happened to my data".
32431
+ *
32432
+ * That placement has a cost worth stating: this document ships in the
32433
+ * framework closure, so this knob needs a published `@camstack/server` while
32434
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
32435
+ *
32436
+ * `retention-model.ts` argued this one must NOT follow a device window,
32437
+ * because the audit trail is the history of the SWEEP and not of the camera.
32438
+ * That argument stands and is untouched — what changes is that 30 days stops
32439
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
32440
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
32441
+ * state in which the next 9.5 GiB leak cannot be explained.
32442
+ */
32443
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
32241
32444
  });
32242
32445
  /**
32243
32446
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -32310,6 +32513,14 @@ var LoggingSettingsStateSchema = object({
32310
32513
  channels: array(LogChannelDescriptorSchema).readonly(),
32311
32514
  /** The channels ARMED right now, each with its deadline. */
32312
32515
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
32516
+ /**
32517
+ * The ops-log window in days, as it is in force. `0` = kept forever.
32518
+ *
32519
+ * Reported rather than left to the caller's memory of what it wrote: this is
32520
+ * the value the analytics sweep will actually use, and an operator asking how
32521
+ * long their audit trail lives should not have to infer it from a default.
32522
+ */
32523
+ opsLogRetentionDays: number().int().min(0),
32313
32524
  persisted: boolean()
32314
32525
  });
32315
32526
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -33130,7 +33341,7 @@ var StationaryObjectSchema = object({
33130
33341
  /** Enrichment label carried from the source track (identity / plate). */
33131
33342
  label: string().optional(),
33132
33343
  /** Native-resolution key-frame media key for the parked object's best image. */
33133
- keyFrameMediaKey: string().optional()
33344
+ keyFrameMediaId: number().int().optional()
33134
33345
  });
33135
33346
  var CameraOccupancySnapshotSchema = object({
33136
33347
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35868,6 +36079,12 @@ Object.freeze({
35868
36079
  addonId: null,
35869
36080
  access: "create"
35870
36081
  },
36082
+ "dataStoreProvider.upsert": {
36083
+ capName: "data-store-provider",
36084
+ capScope: "system",
36085
+ addonId: null,
36086
+ access: "create"
36087
+ },
35871
36088
  "dayNight.getOptions": {
35872
36089
  capName: "day-night",
35873
36090
  capScope: "device",
@@ -38304,6 +38521,12 @@ Object.freeze({
38304
38521
  addonId: null,
38305
38522
  access: "view"
38306
38523
  },
38524
+ "pipelineAnalytics.listRecentSummaries": {
38525
+ capName: "pipeline-analytics",
38526
+ capScope: "device",
38527
+ addonId: null,
38528
+ access: "view"
38529
+ },
38307
38530
  "pipelineAnalytics.listRecentTracks": {
38308
38531
  capName: "pipeline-analytics",
38309
38532
  capScope: "device",
@@ -39732,6 +39955,12 @@ Object.freeze({
39732
39955
  addonId: null,
39733
39956
  access: "create"
39734
39957
  },
39958
+ "settingsStore.upsert": {
39959
+ capName: "settings-store",
39960
+ capScope: "system",
39961
+ addonId: null,
39962
+ access: "create"
39963
+ },
39735
39964
  "smtpProvider.getStatus": {
39736
39965
  capName: "smtp-provider",
39737
39966
  capScope: "system",
@@ -42125,6 +42354,11 @@ Object.freeze({
42125
42354
  form: "single",
42126
42355
  optional: true
42127
42356
  }],
42357
+ "pipelineAnalytics.listRecentSummaries": [{
42358
+ name: "deviceIds",
42359
+ form: "array",
42360
+ optional: false
42361
+ }],
42128
42362
  "pipelineAnalytics.listRecentTracks": [{
42129
42363
  name: "deviceIds",
42130
42364
  form: "array",
package/dist/addon.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createSocket } from "dgram";
2
2
  import { createCipheriv, createDecipheriv } from "crypto";
3
3
  import { EventEmitter } from "events";
4
- //#region ../types/dist/event-category-DAXzJeTX.mjs
4
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
5
5
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
6
6
  EventCategory["SystemBoot"] = "system.boot";
7
7
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -562,7 +562,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
562
562
  * a package zone — a newly-appeared stationary object of a package class
563
563
  * that cleared the class / zone / dwell / size gates. Payload:
564
564
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
565
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
565
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
566
566
  * Telemetry (D8): the durable record is the `package-events` store row;
567
567
  * this bus topic drives notifier rules + live UI. See
568
568
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5348,7 +5348,7 @@ var ZodIssueCode = {
5348
5348
  var ZodFirstPartyTypeKind;
5349
5349
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5350
5350
  //#endregion
5351
- //#region ../types/dist/sleep-BnujYGPe.mjs
5351
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5352
5352
  /**
5353
5353
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5354
5354
  * window to float samples (D455).
@@ -7315,13 +7315,30 @@ new Set([
7315
7315
  "scene",
7316
7316
  "motion",
7317
7317
  "object",
7318
- "audio"
7318
+ "audio",
7319
+ "mosaic"
7319
7320
  ]);
7320
- new Set([
7321
+ /**
7322
+ * The owner types that are event tables, in the order the event-media resolver
7323
+ * should consider them. Exported so a consumer asking "is this key an event?"
7324
+ * does not re-spell the list and drift from it.
7325
+ */
7326
+ var EVENT_OWNER_TYPES = [
7321
7327
  "motion",
7322
7328
  "object",
7323
7329
  "audio"
7324
- ]);
7330
+ ];
7331
+ /**
7332
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7333
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7334
+ * `(table, id)`).
7335
+ *
7336
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7337
+ * table would otherwise be accepted by the codec and refused at the door, with
7338
+ * nothing failing until a caller asked.
7339
+ */
7340
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7341
+ new Set(EVENT_OWNER_TYPES);
7325
7342
  var EncodeProfileSchema = object({
7326
7343
  video: object({
7327
7344
  codec: _enum([
@@ -7360,7 +7377,23 @@ var EncodeProfileSchema = object({
7360
7377
  "zerolatency",
7361
7378
  "film",
7362
7379
  "animation"
7363
- ]).optional()
7380
+ ]).optional(),
7381
+ /**
7382
+ * ONE slice per access unit.
7383
+ *
7384
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7385
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7386
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7387
+ * frame-end per frame: the first pictures render and the video then freezes
7388
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7389
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7390
+ *
7391
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7392
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7393
+ * consumers meaning the same thing spelled differently would stop sharing one
7394
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7395
+ */
7396
+ singleSlicePerFrame: boolean().optional()
7364
7397
  }),
7365
7398
  audio: union([literal("passthrough"), object({
7366
7399
  codec: _enum([
@@ -7410,7 +7443,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7410
7443
  audio: "passthrough"
7411
7444
  };
7412
7445
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7413
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7446
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7414
7447
  /**
7415
7448
  * Deep wiring healthcheck — snapshot of active reachability probes across
7416
7449
  * every declared capability + widget of every installed plugin, on every
@@ -12520,6 +12553,16 @@ method(object({
12520
12553
  filter: QueryFilterSchema.optional(),
12521
12554
  columns: array(string()).readonly().optional()
12522
12555
  }), array(SettingsRecordSchema).readonly()), method(object({
12556
+ namespace: string().optional(),
12557
+ collection: string(),
12558
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12559
+ * index on the collection. Never empty. */
12560
+ conflict: array(string()).readonly(),
12561
+ record: BulkRecordSchema
12562
+ }), object({
12563
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12564
+ * row's own id on an update. */
12565
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12523
12566
  namespace: string().optional(),
12524
12567
  collection: string(),
12525
12568
  record: object({
@@ -12654,6 +12697,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12654
12697
  id: union([string(), number()]) }), {
12655
12698
  kind: "mutation",
12656
12699
  auth: "admin"
12700
+ }), method(object({
12701
+ namespace: string().optional(),
12702
+ collection: string(),
12703
+ conflict: array(string()).readonly(),
12704
+ record: BulkRecordSchema
12705
+ }), object({ id: union([string(), number()]) }), {
12706
+ kind: "mutation",
12707
+ auth: "admin"
12657
12708
  }), method(object({
12658
12709
  namespace: string().optional(),
12659
12710
  collection: string(),
@@ -19136,8 +19187,8 @@ var TrackPositionSchema = object({
19136
19187
  var TrackSnapshotSchema = object({
19137
19188
  timestamp: number(),
19138
19189
  position: TrackPositionSchema,
19139
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19140
- mediaKey: string()
19190
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19191
+ mediaId: number().int()
19141
19192
  });
19142
19193
  /**
19143
19194
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19741,14 +19792,14 @@ var ObjectEventSchema = object({
19741
19792
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19742
19793
  frameWidth: number().optional(),
19743
19794
  frameHeight: number().optional(),
19744
- /** MediaStore key for the crop attached to this event (if any). */
19745
- mediaKey: string().optional(),
19746
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19795
+ /** Media row id of the crop attached to this event (if any) — D482. */
19796
+ mediaId: number().int().optional(),
19797
+ /** Design B: media row id of the track's native-resolution key frame (the
19747
19798
  * best-detection full frame). Resolve via the event-media data-plane
19748
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19799
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19749
19800
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19750
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19751
- keyFrameMediaKey: string().optional(),
19801
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19802
+ keyFrameMediaId: number().int().optional(),
19752
19803
  /** Populated by B5 (recording playback URL for this event). */
19753
19804
  mediaUrl: string().optional(),
19754
19805
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19798,7 +19849,7 @@ var MediaFileKindEnum = _enum([
19798
19849
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19799
19850
  *
19800
19851
  * `url` points at the `event-media` data plane
19801
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19852
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19802
19853
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19803
19854
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19804
19855
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19806,10 +19857,13 @@ var MediaFileKindEnum = _enum([
19806
19857
  * `data-plane-access.ts` for the rule and the one gap it does not close
19807
19858
  * (per-device scoping).
19808
19859
  *
19809
- * The URL is built from the row's **stored** key, which is not always its
19810
- * published `kind`: a track's face/plate crop is stored as `crop` under
19860
+ * The URL is built from the row's own id, which since D482 says nothing about
19861
+ * the row's owner or kind — and that is the point. The published `kind` is not
19862
+ * the stored one (a track's face/plate crop is stored as `crop` under
19811
19863
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19812
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19864
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19865
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19866
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19813
19867
  *
19814
19868
  * ## `base64` is TRANSITIONAL and is going away
19815
19869
  *
@@ -19822,7 +19876,19 @@ var MediaFileKindEnum = _enum([
19822
19876
  * `analytics-query-facade.ts`; nothing else reads it.
19823
19877
  */
19824
19878
  var MediaFileSchema = object({
19825
- key: string(),
19879
+ /**
19880
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19881
+ *
19882
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19883
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19884
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19885
+ * different rows that spell the same number, and a track whose
19886
+ * `bestEventId` was an object event was served a motion event's snapshot —
19887
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19888
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19889
+ * at all.
19890
+ */
19891
+ mediaId: number().int(),
19826
19892
  kind: MediaFileKindEnum,
19827
19893
  sizeBytes: number(),
19828
19894
  timestamp: number()
@@ -19930,7 +19996,7 @@ var RetrainTrackSchema = object({
19930
19996
  });
19931
19997
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19932
19998
  var RetrainFrameCandidateSchema = object({
19933
- mediaKey: string(),
19999
+ mediaId: number().int(),
19934
20000
  kind: MediaFileKindEnum,
19935
20001
  timestamp: number(),
19936
20002
  sizeBytes: number().int(),
@@ -19944,7 +20010,7 @@ var RetrainFrameSchema = object({
19944
20010
  deviceId: number(),
19945
20011
  trackId: string(),
19946
20012
  /** Provenance only. It may already point at nothing — that is expected. */
19947
- sourceMediaKey: string(),
20013
+ sourceMediaId: number().int(),
19948
20014
  sourceKind: MediaFileKindEnum,
19949
20015
  sizeBytes: number().int(),
19950
20016
  width: number().int(),
@@ -19960,7 +20026,7 @@ var RetrainCopyRefusalSchema = _enum([
19960
20026
  var RetrainFrameSelectionSchema = object({
19961
20027
  copied: array(RetrainFrameSchema).readonly(),
19962
20028
  refused: array(object({
19963
- sourceMediaKey: string(),
20029
+ sourceMediaId: number().int(),
19964
20030
  reason: RetrainCopyRefusalSchema
19965
20031
  })).readonly()
19966
20032
  });
@@ -19968,7 +20034,7 @@ var RetrainFrameListSchema = object({
19968
20034
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19969
20035
  copies: array(RetrainFrameSchema).readonly(),
19970
20036
  /** What the page pre-selects — the native key frame when one survives. */
19971
- autoPickMediaKey: string().optional()
20037
+ autoPickMediaId: number().int().optional()
19972
20038
  });
19973
20039
  /** What the operator asked the assist to look for. */
19974
20040
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20063,9 +20129,11 @@ var RecentTracksQueryInput = object({
20063
20129
  });
20064
20130
  /**
20065
20131
  * A Summary (D359): the per-camera session envelope that references tracks.
20066
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20067
- * served by the event-media plane by key), null until the first frame with a
20068
- * member visible has been materialised.
20132
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20133
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20134
+ * first frame with a member visible has been materialised (D482 — it was the
20135
+ * row's composite key, which embedded the owner and could name another table's
20136
+ * row once event ids became per-table rowids).
20069
20137
  */
20070
20138
  var SummarySchema = object({
20071
20139
  id: string(),
@@ -20074,19 +20142,115 @@ var SummarySchema = object({
20074
20142
  lastActiveAt: number(),
20075
20143
  sealedAt: number().nullable(),
20076
20144
  memberCount: number().int().nonnegative(),
20077
- groupShotKey: string().nullable()
20145
+ groupShotMediaId: number().int().nullable()
20078
20146
  });
20079
20147
  var SummariesQueryInput = object({
20080
20148
  deviceIds: array(number()).min(1),
20081
20149
  since: number().optional(),
20082
20150
  until: number().optional(),
20083
- limit: number().int().min(1).max(200).default(50)
20151
+ limit: number().int().min(1).max(200).default(50),
20152
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20153
+ * meaning of "dog" on every summary surface. */
20154
+ classes: array(string().min(1).max(60)).max(40).optional()
20084
20155
  });
20085
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20086
20156
  var SummaryDetailSchema = object({
20087
20157
  summary: SummarySchema,
20088
- /** Member track ids, in absorption order. */
20089
- trackIds: array(string()).readonly()
20158
+ /** Members in absorption order, oldest join first. */
20159
+ members: array(object({
20160
+ trackId: string(),
20161
+ /**
20162
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20163
+ * and the track no longer exists to fill it.
20164
+ *
20165
+ * The join, not the latest opinion: class voting can re-decide a track's
20166
+ * class afterwards, and a group's composition is the history of who came
20167
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20168
+ * dog" — rather than being handed a second, denormalised tally that would
20169
+ * drift from the members it claims to describe.
20170
+ */
20171
+ className: string().nullable(),
20172
+ /** `primary` is the one member that carries the group's identity. */
20173
+ role: _enum([
20174
+ "primary",
20175
+ "sibling",
20176
+ "child"
20177
+ ]).nullable(),
20178
+ joinedAt: number(),
20179
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20180
+ * at read time. Absent when nothing resolved, never an empty string. */
20181
+ label: string().optional(),
20182
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20183
+ * recognised nobody, and absent for a member whose track has been pruned —
20184
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20185
+ subLabel: string().optional()
20186
+ })).readonly()
20187
+ });
20188
+ /**
20189
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20190
+ *
20191
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20192
+ * we extend when we notice we need more". Enough to draw the card ("two people
20193
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20194
+ * are on {@link getSummary}: shipping every member of every group so a client
20195
+ * can count them turns a page of two hundred groups into a thousand records to
20196
+ * draw two hundred badges.
20197
+ */
20198
+ var SummaryListItemSchema = object({
20199
+ summary: SummarySchema,
20200
+ /** Class counts, most numerous first; `className: null` is a member whose
20201
+ * class never resolved, counted so the counts add up to `memberCount`. */
20202
+ classes: array(object({
20203
+ className: string().nullable(),
20204
+ count: number().int().positive()
20205
+ })).readonly()
20206
+ });
20207
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20208
+ /**
20209
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20210
+ *
20211
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20212
+ * summaries feed that could only answer "the last N for these cameras"
20213
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20214
+ * else. Two feeds with different ordering contracts would be two ideas of what
20215
+ * "newest" means, on one screen.
20216
+ */
20217
+ var RecentSummariesQueryInput = object({
20218
+ /** Devices to merge. An empty array yields an empty page. */
20219
+ deviceIds: array(number()),
20220
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20221
+ since: number().optional(),
20222
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20223
+ until: number().optional(),
20224
+ limit: number().int().min(1).max(500).default(100),
20225
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20226
+ cursor: string().optional(),
20227
+ /**
20228
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20229
+ * deliberately the same name: the events filter tree emits one list of ticked
20230
+ * taxonomy nodes, and a summaries feed that called it something else would
20231
+ * make the operator's one filter component produce two shapes.
20232
+ *
20233
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20234
+ * me the arrivals with a dog" has to return the group of four people who had
20235
+ * one with them; a filter on the group's own fields could only ever answer
20236
+ * about its primary.
20237
+ *
20238
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20239
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20240
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20241
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20242
+ * that read only the class would answer "animal" and never "dog".
20243
+ *
20244
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20245
+ */
20246
+ classes: array(string().min(1).max(60)).max(40).optional()
20247
+ });
20248
+ var RecentSummariesPageSchema = object({
20249
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20250
+ * {@link listSummaries} — one list shape, so a card renders the same
20251
+ * whichever feed drew it. */
20252
+ summaries: array(SummaryListItemSchema).readonly(),
20253
+ nextCursor: string().nullable()
20090
20254
  });
20091
20255
  var RecentTracksPageSchema = object({
20092
20256
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20500,7 +20664,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20500
20664
  classes: array(string()).optional(),
20501
20665
  /** See {@link ExcludeSourcesDoc}. */
20502
20666
  excludeSources: array(TrackSourceSchema).optional()
20503
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20667
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20504
20668
  kind: "mutation",
20505
20669
  auth: "admin"
20506
20670
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -20628,7 +20792,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20628
20792
  }), method(object({
20629
20793
  deviceId: number(),
20630
20794
  trackId: string(),
20631
- mediaKeys: array(string()).min(1)
20795
+ mediaIds: array(number().int()).min(1)
20632
20796
  }), RetrainFrameSelectionSchema, {
20633
20797
  kind: "mutation",
20634
20798
  auth: "admin"
@@ -20696,7 +20860,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20696
20860
  kind: "query",
20697
20861
  auth: "admin"
20698
20862
  }), method(object({
20699
- eventId: string(),
20863
+ /** Which of the three event tables owns the row. */
20864
+ ownerType: EventOwnerTypeSchema,
20865
+ /** The event's rowid in that table (D474: an event id is a number). */
20866
+ eventId: number().int(),
20700
20867
  kind: MediaFileKindEnum.optional(),
20701
20868
  deviceId: number()
20702
20869
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20707,7 +20874,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20707
20874
  trackId: string(),
20708
20875
  deviceId: number()
20709
20876
  }), array(MediaFileInfoSchema).readonly()), method(object({
20710
- eventId: string(),
20877
+ ownerType: EventOwnerTypeSchema,
20878
+ eventId: number().int(),
20711
20879
  deviceId: number()
20712
20880
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20713
20881
  kind: "mutation",
@@ -25900,8 +26068,8 @@ var IdentitySchema = object({
25900
26068
  id: string(),
25901
26069
  name: string(),
25902
26070
  sampleCount: number().int().nonnegative(),
25903
- coverMediaKey: string().optional(),
25904
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
26071
+ coverMediaId: number().int().optional(),
26072
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25905
26073
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25906
26074
  coverBase64: string().optional()
25907
26075
  });
@@ -25927,8 +26095,8 @@ var IdentitySampleAuditRowSchema = object({
25927
26095
  enrolledAt: number().int(),
25928
26096
  deviceId: number().int().optional(),
25929
26097
  source: _enum(["detected", "photo"]),
25930
- /** The enrolled crop's media key, so the panel can show the evidence. */
25931
- mediaKey: string().optional(),
26098
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
26099
+ mediaId: number().int().optional(),
25932
26100
  /**
25933
26101
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25934
26102
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25997,15 +26165,15 @@ var FaceInfoSchema = object({
25997
26165
  */
25998
26166
  cropUrl: string().optional(),
25999
26167
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
26000
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26168
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
26001
26169
  * legacy rows written before design B. */
26002
26170
  faceBbox: BoundingBoxSchema.optional(),
26003
26171
  /** Design B: MediaStore key of the track's native-resolution key frame.
26004
26172
  * Fetch the native JPEG via the event-media data-plane
26005
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26173
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26006
26174
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26007
26175
  * back to the inline `base64` face crop. */
26008
- keyFrameMediaKey: string().optional(),
26176
+ keyFrameMediaId: number().int().optional(),
26009
26177
  /** Winning identity-match cosine (0..1) for this face's track, when an
26010
26178
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26011
26179
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26074,11 +26242,14 @@ var FaceClusterSchema = object({
26074
26242
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26075
26243
  * dialog already rendering its key FRAME from the `event-media` plane.
26076
26244
  *
26077
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26078
- * media key directly, so this needed no new plane and no new access decision.
26245
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26246
+ * resolves a media row by its own id, so this needed no new plane and no new
26247
+ * access decision.
26079
26248
  */
26080
26249
  var MediaFileLiteSchema$1 = object({
26081
- key: string(),
26250
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26251
+ * (D482). It was the row's composite key, which embedded the owner. */
26252
+ mediaId: number().int(),
26082
26253
  kind: string(),
26083
26254
  url: string(),
26084
26255
  sizeBytes: number(),
@@ -26095,7 +26266,7 @@ method(object({
26095
26266
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26096
26267
  * four times and the viewer holds at `staleTime: 30_000`.
26097
26268
  *
26098
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26269
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26099
26270
  * the `event-media` plane serves that key `immutable` with an ETag.
26100
26271
  *
26101
26272
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29458,8 +29629,8 @@ var VehicleSchema = object({
29458
29629
  id: string(),
29459
29630
  name: string(),
29460
29631
  sampleCount: number().int().nonnegative(),
29461
- coverMediaKey: string().optional(),
29462
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29632
+ coverMediaId: number().int().optional(),
29633
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29463
29634
  coverBase64: string().optional()
29464
29635
  });
29465
29636
  var VehicleSampleInfoSchema = object({
@@ -29492,7 +29663,7 @@ var PlateInfoSchema = object({
29492
29663
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29493
29664
  plateBbox: BoundingBoxSchema.optional(),
29494
29665
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29495
- keyFrameMediaKey: string().optional(),
29666
+ keyFrameMediaId: number().int().optional(),
29496
29667
  base64: string().optional(),
29497
29668
  /**
29498
29669
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29513,11 +29684,14 @@ var PlateInfoSchema = object({
29513
29684
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29514
29685
  * dialog already rendering its key FRAME from the `event-media` plane.
29515
29686
  *
29516
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29517
- * media key directly, so this needed no new plane and no new access decision.
29687
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29688
+ * resolves a media row by its own id, so this needed no new plane and no new
29689
+ * access decision.
29518
29690
  */
29519
29691
  var MediaFileLiteSchema = object({
29520
- key: string(),
29692
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29693
+ * (D482). It was the row's composite key, which embedded the owner. */
29694
+ mediaId: number().int(),
29521
29695
  kind: string(),
29522
29696
  url: string(),
29523
29697
  sizeBytes: number(),
@@ -29576,7 +29750,7 @@ method(object({
29576
29750
  }), method(object({
29577
29751
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29578
29752
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29579
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29753
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29580
29754
  * INPUT field: stripped by the hub router until the train ships, which
29581
29755
  * resolves to `false` and is exactly the intended default. */
29582
29756
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31145,7 +31319,14 @@ var SceneReferenceSchema = object({
31145
31319
  modelId: string(),
31146
31320
  condition: SceneConditionSchema,
31147
31321
  capturedAt: number(),
31148
- thumbnailMediaId: string().optional(),
31322
+ /**
31323
+ * The reference thumbnail's media ROW id (D482).
31324
+ *
31325
+ * The name already said `mediaId` while the value was the row's composite
31326
+ * key — a leftover that read as done and was not. It is the row's own id
31327
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31328
+ */
31329
+ thumbnailMediaId: number().int().optional(),
31149
31330
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31150
31331
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31151
31332
  * normalized rect frame a different piece of world, and the scene would
@@ -32236,7 +32417,29 @@ var LoggingSettingsPatchSchema = object({
32236
32417
  * disarmed the channels it did not mention would make the Levels page and
32237
32418
  * the Diagnostics page fight over the same value.
32238
32419
  */
32239
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
32420
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
32421
+ /**
32422
+ * How many days the analytics OPS LOG is kept — the audit trail of what
32423
+ * retention actually deleted.
32424
+ *
32425
+ * It lives HERE, on the logging document, and not on the analytics addon's
32426
+ * settings form, on the operator's instruction: every other log-ish window in
32427
+ * this system is configured through this one document, and a retention audit
32428
+ * configured somewhere else is a second place to look when the question is
32429
+ * "what happened to my data".
32430
+ *
32431
+ * That placement has a cost worth stating: this document ships in the
32432
+ * framework closure, so this knob needs a published `@camstack/server` while
32433
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
32434
+ *
32435
+ * `retention-model.ts` argued this one must NOT follow a device window,
32436
+ * because the audit trail is the history of the SWEEP and not of the camera.
32437
+ * That argument stands and is untouched — what changes is that 30 days stops
32438
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
32439
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
32440
+ * state in which the next 9.5 GiB leak cannot be explained.
32441
+ */
32442
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
32240
32443
  });
32241
32444
  /**
32242
32445
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -32309,6 +32512,14 @@ var LoggingSettingsStateSchema = object({
32309
32512
  channels: array(LogChannelDescriptorSchema).readonly(),
32310
32513
  /** The channels ARMED right now, each with its deadline. */
32311
32514
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
32515
+ /**
32516
+ * The ops-log window in days, as it is in force. `0` = kept forever.
32517
+ *
32518
+ * Reported rather than left to the caller's memory of what it wrote: this is
32519
+ * the value the analytics sweep will actually use, and an operator asking how
32520
+ * long their audit trail lives should not have to infer it from a default.
32521
+ */
32522
+ opsLogRetentionDays: number().int().min(0),
32312
32523
  persisted: boolean()
32313
32524
  });
32314
32525
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -33129,7 +33340,7 @@ var StationaryObjectSchema = object({
33129
33340
  /** Enrichment label carried from the source track (identity / plate). */
33130
33341
  label: string().optional(),
33131
33342
  /** Native-resolution key-frame media key for the parked object's best image. */
33132
- keyFrameMediaKey: string().optional()
33343
+ keyFrameMediaId: number().int().optional()
33133
33344
  });
33134
33345
  var CameraOccupancySnapshotSchema = object({
33135
33346
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35867,6 +36078,12 @@ Object.freeze({
35867
36078
  addonId: null,
35868
36079
  access: "create"
35869
36080
  },
36081
+ "dataStoreProvider.upsert": {
36082
+ capName: "data-store-provider",
36083
+ capScope: "system",
36084
+ addonId: null,
36085
+ access: "create"
36086
+ },
35870
36087
  "dayNight.getOptions": {
35871
36088
  capName: "day-night",
35872
36089
  capScope: "device",
@@ -38303,6 +38520,12 @@ Object.freeze({
38303
38520
  addonId: null,
38304
38521
  access: "view"
38305
38522
  },
38523
+ "pipelineAnalytics.listRecentSummaries": {
38524
+ capName: "pipeline-analytics",
38525
+ capScope: "device",
38526
+ addonId: null,
38527
+ access: "view"
38528
+ },
38306
38529
  "pipelineAnalytics.listRecentTracks": {
38307
38530
  capName: "pipeline-analytics",
38308
38531
  capScope: "device",
@@ -39731,6 +39954,12 @@ Object.freeze({
39731
39954
  addonId: null,
39732
39955
  access: "create"
39733
39956
  },
39957
+ "settingsStore.upsert": {
39958
+ capName: "settings-store",
39959
+ capScope: "system",
39960
+ addonId: null,
39961
+ access: "create"
39962
+ },
39734
39963
  "smtpProvider.getStatus": {
39735
39964
  capName: "smtp-provider",
39736
39965
  capScope: "system",
@@ -42124,6 +42353,11 @@ Object.freeze({
42124
42353
  form: "single",
42125
42354
  optional: true
42126
42355
  }],
42356
+ "pipelineAnalytics.listRecentSummaries": [{
42357
+ name: "deviceIds",
42358
+ form: "array",
42359
+ optional: false
42360
+ }],
42127
42361
  "pipelineAnalytics.listRecentTracks": [{
42128
42362
  name: "deviceIds",
42129
42363
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-gree",
3
- "version": "0.2.98",
3
+ "version": "0.2.100",
4
4
  "description": "Gree air-conditioner device-provider addon for CamStack — wraps the @apocaliss92/nodegree local-UDP client (LAN discovery + AES control), exposing climate-control and fan-control",
5
5
  "keywords": [
6
6
  "camstack",