@camstack/addon-provider-unifi 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 +131 -50
  2. package/dist/addon.mjs +131 -50
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region ../types/dist/event-category-DAXzJeTX.mjs
2
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -560,7 +560,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
560
560
  * a package zone — a newly-appeared stationary object of a package class
561
561
  * that cleared the class / zone / dwell / size gates. Payload:
562
562
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
563
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
563
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
564
564
  * Telemetry (D8): the durable record is the `package-events` store row;
565
565
  * this bus topic drives notifier rules + live UI. See
566
566
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5346,7 +5346,7 @@ var ZodIssueCode = {
5346
5346
  var ZodFirstPartyTypeKind;
5347
5347
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5348
5348
  //#endregion
5349
- //#region ../types/dist/sleep-BnujYGPe.mjs
5349
+ //#region ../types/dist/sleep-CnLyvg3w.mjs
5350
5350
  /**
5351
5351
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5352
5352
  * window to float samples (D455).
@@ -7313,13 +7313,30 @@ new Set([
7313
7313
  "scene",
7314
7314
  "motion",
7315
7315
  "object",
7316
- "audio"
7316
+ "audio",
7317
+ "mosaic"
7317
7318
  ]);
7318
- new Set([
7319
+ /**
7320
+ * The owner types that are event tables, in the order the event-media resolver
7321
+ * should consider them. Exported so a consumer asking "is this key an event?"
7322
+ * does not re-spell the list and drift from it.
7323
+ */
7324
+ var EVENT_OWNER_TYPES = [
7319
7325
  "motion",
7320
7326
  "object",
7321
7327
  "audio"
7322
- ]);
7328
+ ];
7329
+ /**
7330
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7331
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7332
+ * `(table, id)`).
7333
+ *
7334
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7335
+ * table would otherwise be accepted by the codec and refused at the door, with
7336
+ * nothing failing until a caller asked.
7337
+ */
7338
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7339
+ new Set(EVENT_OWNER_TYPES);
7323
7340
  var EncodeProfileSchema = object({
7324
7341
  video: object({
7325
7342
  codec: _enum([
@@ -12518,6 +12535,16 @@ method(object({
12518
12535
  filter: QueryFilterSchema.optional(),
12519
12536
  columns: array(string()).readonly().optional()
12520
12537
  }), array(SettingsRecordSchema).readonly()), method(object({
12538
+ namespace: string().optional(),
12539
+ collection: string(),
12540
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12541
+ * index on the collection. Never empty. */
12542
+ conflict: array(string()).readonly(),
12543
+ record: BulkRecordSchema
12544
+ }), object({
12545
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12546
+ * row's own id on an update. */
12547
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12521
12548
  namespace: string().optional(),
12522
12549
  collection: string(),
12523
12550
  record: object({
@@ -12652,6 +12679,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12652
12679
  id: union([string(), number()]) }), {
12653
12680
  kind: "mutation",
12654
12681
  auth: "admin"
12682
+ }), method(object({
12683
+ namespace: string().optional(),
12684
+ collection: string(),
12685
+ conflict: array(string()).readonly(),
12686
+ record: BulkRecordSchema
12687
+ }), object({ id: union([string(), number()]) }), {
12688
+ kind: "mutation",
12689
+ auth: "admin"
12655
12690
  }), method(object({
12656
12691
  namespace: string().optional(),
12657
12692
  collection: string(),
@@ -19134,8 +19169,8 @@ var TrackPositionSchema = object({
19134
19169
  var TrackSnapshotSchema = object({
19135
19170
  timestamp: number(),
19136
19171
  position: TrackPositionSchema,
19137
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19138
- mediaKey: string()
19172
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19173
+ mediaId: number().int()
19139
19174
  });
19140
19175
  /**
19141
19176
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19739,14 +19774,14 @@ var ObjectEventSchema = object({
19739
19774
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19740
19775
  frameWidth: number().optional(),
19741
19776
  frameHeight: number().optional(),
19742
- /** MediaStore key for the crop attached to this event (if any). */
19743
- mediaKey: string().optional(),
19744
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19777
+ /** Media row id of the crop attached to this event (if any) — D482. */
19778
+ mediaId: number().int().optional(),
19779
+ /** Design B: media row id of the track's native-resolution key frame (the
19745
19780
  * best-detection full frame). Resolve via the event-media data-plane
19746
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19781
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19747
19782
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19748
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19749
- keyFrameMediaKey: string().optional(),
19783
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19784
+ keyFrameMediaId: number().int().optional(),
19750
19785
  /** Populated by B5 (recording playback URL for this event). */
19751
19786
  mediaUrl: string().optional(),
19752
19787
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19796,7 +19831,7 @@ var MediaFileKindEnum = _enum([
19796
19831
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19797
19832
  *
19798
19833
  * `url` points at the `event-media` data plane
19799
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19834
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19800
19835
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19801
19836
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19802
19837
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19804,10 +19839,13 @@ var MediaFileKindEnum = _enum([
19804
19839
  * `data-plane-access.ts` for the rule and the one gap it does not close
19805
19840
  * (per-device scoping).
19806
19841
  *
19807
- * The URL is built from the row's **stored** key, which is not always its
19808
- * published `kind`: a track's face/plate crop is stored as `crop` under
19842
+ * The URL is built from the row's own id, which since D482 says nothing about
19843
+ * the row's owner or kind — and that is the point. The published `kind` is not
19844
+ * the stored one (a track's face/plate crop is stored as `crop` under
19809
19845
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19810
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19846
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19847
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19848
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19811
19849
  *
19812
19850
  * ## `base64` is TRANSITIONAL and is going away
19813
19851
  *
@@ -19820,7 +19858,19 @@ var MediaFileKindEnum = _enum([
19820
19858
  * `analytics-query-facade.ts`; nothing else reads it.
19821
19859
  */
19822
19860
  var MediaFileSchema = object({
19823
- key: string(),
19861
+ /**
19862
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19863
+ *
19864
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19865
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19866
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19867
+ * different rows that spell the same number, and a track whose
19868
+ * `bestEventId` was an object event was served a motion event's snapshot —
19869
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19870
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19871
+ * at all.
19872
+ */
19873
+ mediaId: number().int(),
19824
19874
  kind: MediaFileKindEnum,
19825
19875
  sizeBytes: number(),
19826
19876
  timestamp: number()
@@ -19928,7 +19978,7 @@ var RetrainTrackSchema = object({
19928
19978
  });
19929
19979
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19930
19980
  var RetrainFrameCandidateSchema = object({
19931
- mediaKey: string(),
19981
+ mediaId: number().int(),
19932
19982
  kind: MediaFileKindEnum,
19933
19983
  timestamp: number(),
19934
19984
  sizeBytes: number().int(),
@@ -19942,7 +19992,7 @@ var RetrainFrameSchema = object({
19942
19992
  deviceId: number(),
19943
19993
  trackId: string(),
19944
19994
  /** Provenance only. It may already point at nothing — that is expected. */
19945
- sourceMediaKey: string(),
19995
+ sourceMediaId: number().int(),
19946
19996
  sourceKind: MediaFileKindEnum,
19947
19997
  sizeBytes: number().int(),
19948
19998
  width: number().int(),
@@ -19958,7 +20008,7 @@ var RetrainCopyRefusalSchema = _enum([
19958
20008
  var RetrainFrameSelectionSchema = object({
19959
20009
  copied: array(RetrainFrameSchema).readonly(),
19960
20010
  refused: array(object({
19961
- sourceMediaKey: string(),
20011
+ sourceMediaId: number().int(),
19962
20012
  reason: RetrainCopyRefusalSchema
19963
20013
  })).readonly()
19964
20014
  });
@@ -19966,7 +20016,7 @@ var RetrainFrameListSchema = object({
19966
20016
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19967
20017
  copies: array(RetrainFrameSchema).readonly(),
19968
20018
  /** What the page pre-selects — the native key frame when one survives. */
19969
- autoPickMediaKey: string().optional()
20019
+ autoPickMediaId: number().int().optional()
19970
20020
  });
19971
20021
  /** What the operator asked the assist to look for. */
19972
20022
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20061,9 +20111,11 @@ var RecentTracksQueryInput = object({
20061
20111
  });
20062
20112
  /**
20063
20113
  * A Summary (D359): the per-camera session envelope that references tracks.
20064
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20065
- * served by the event-media plane by key), null until the first frame with a
20066
- * member visible has been materialised.
20114
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20115
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20116
+ * first frame with a member visible has been materialised (D482 — it was the
20117
+ * row's composite key, which embedded the owner and could name another table's
20118
+ * row once event ids became per-table rowids).
20067
20119
  */
20068
20120
  var SummarySchema = object({
20069
20121
  id: string(),
@@ -20072,7 +20124,7 @@ var SummarySchema = object({
20072
20124
  lastActiveAt: number(),
20073
20125
  sealedAt: number().nullable(),
20074
20126
  memberCount: number().int().nonnegative(),
20075
- groupShotKey: string().nullable()
20127
+ groupShotMediaId: number().int().nullable()
20076
20128
  });
20077
20129
  var SummariesQueryInput = object({
20078
20130
  deviceIds: array(number()).min(1),
@@ -20626,7 +20678,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20626
20678
  }), method(object({
20627
20679
  deviceId: number(),
20628
20680
  trackId: string(),
20629
- mediaKeys: array(string()).min(1)
20681
+ mediaIds: array(number().int()).min(1)
20630
20682
  }), RetrainFrameSelectionSchema, {
20631
20683
  kind: "mutation",
20632
20684
  auth: "admin"
@@ -20694,7 +20746,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20694
20746
  kind: "query",
20695
20747
  auth: "admin"
20696
20748
  }), method(object({
20697
- eventId: string(),
20749
+ /** Which of the three event tables owns the row. */
20750
+ ownerType: EventOwnerTypeSchema,
20751
+ /** The event's rowid in that table (D474: an event id is a number). */
20752
+ eventId: number().int(),
20698
20753
  kind: MediaFileKindEnum.optional(),
20699
20754
  deviceId: number()
20700
20755
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20705,7 +20760,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20705
20760
  trackId: string(),
20706
20761
  deviceId: number()
20707
20762
  }), array(MediaFileInfoSchema).readonly()), method(object({
20708
- eventId: string(),
20763
+ ownerType: EventOwnerTypeSchema,
20764
+ eventId: number().int(),
20709
20765
  deviceId: number()
20710
20766
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20711
20767
  kind: "mutation",
@@ -25915,8 +25971,8 @@ var IdentitySchema = object({
25915
25971
  id: string(),
25916
25972
  name: string(),
25917
25973
  sampleCount: number().int().nonnegative(),
25918
- coverMediaKey: string().optional(),
25919
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25974
+ coverMediaId: number().int().optional(),
25975
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25920
25976
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25921
25977
  coverBase64: string().optional()
25922
25978
  });
@@ -25942,8 +25998,8 @@ var IdentitySampleAuditRowSchema = object({
25942
25998
  enrolledAt: number().int(),
25943
25999
  deviceId: number().int().optional(),
25944
26000
  source: _enum(["detected", "photo"]),
25945
- /** The enrolled crop's media key, so the panel can show the evidence. */
25946
- mediaKey: string().optional(),
26001
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
26002
+ mediaId: number().int().optional(),
25947
26003
  /**
25948
26004
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25949
26005
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -26012,15 +26068,15 @@ var FaceInfoSchema = object({
26012
26068
  */
26013
26069
  cropUrl: string().optional(),
26014
26070
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
26015
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26071
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
26016
26072
  * legacy rows written before design B. */
26017
26073
  faceBbox: BoundingBoxSchema.optional(),
26018
26074
  /** Design B: MediaStore key of the track's native-resolution key frame.
26019
26075
  * Fetch the native JPEG via the event-media data-plane
26020
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26076
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26021
26077
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26022
26078
  * back to the inline `base64` face crop. */
26023
- keyFrameMediaKey: string().optional(),
26079
+ keyFrameMediaId: number().int().optional(),
26024
26080
  /** Winning identity-match cosine (0..1) for this face's track, when an
26025
26081
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26026
26082
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26089,11 +26145,14 @@ var FaceClusterSchema = object({
26089
26145
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26090
26146
  * dialog already rendering its key FRAME from the `event-media` plane.
26091
26147
  *
26092
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26093
- * media key directly, so this needed no new plane and no new access decision.
26148
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26149
+ * resolves a media row by its own id, so this needed no new plane and no new
26150
+ * access decision.
26094
26151
  */
26095
26152
  var MediaFileLiteSchema$1 = object({
26096
- key: string(),
26153
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26154
+ * (D482). It was the row's composite key, which embedded the owner. */
26155
+ mediaId: number().int(),
26097
26156
  kind: string(),
26098
26157
  url: string(),
26099
26158
  sizeBytes: number(),
@@ -26110,7 +26169,7 @@ method(object({
26110
26169
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26111
26170
  * four times and the viewer holds at `staleTime: 30_000`.
26112
26171
  *
26113
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26172
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26114
26173
  * the `event-media` plane serves that key `immutable` with an ETag.
26115
26174
  *
26116
26175
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29473,8 +29532,8 @@ var VehicleSchema = object({
29473
29532
  id: string(),
29474
29533
  name: string(),
29475
29534
  sampleCount: number().int().nonnegative(),
29476
- coverMediaKey: string().optional(),
29477
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29535
+ coverMediaId: number().int().optional(),
29536
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29478
29537
  coverBase64: string().optional()
29479
29538
  });
29480
29539
  var VehicleSampleInfoSchema = object({
@@ -29507,7 +29566,7 @@ var PlateInfoSchema = object({
29507
29566
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29508
29567
  plateBbox: BoundingBoxSchema.optional(),
29509
29568
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29510
- keyFrameMediaKey: string().optional(),
29569
+ keyFrameMediaId: number().int().optional(),
29511
29570
  base64: string().optional(),
29512
29571
  /**
29513
29572
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29528,11 +29587,14 @@ var PlateInfoSchema = object({
29528
29587
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29529
29588
  * dialog already rendering its key FRAME from the `event-media` plane.
29530
29589
  *
29531
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29532
- * media key directly, so this needed no new plane and no new access decision.
29590
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29591
+ * resolves a media row by its own id, so this needed no new plane and no new
29592
+ * access decision.
29533
29593
  */
29534
29594
  var MediaFileLiteSchema = object({
29535
- key: string(),
29595
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29596
+ * (D482). It was the row's composite key, which embedded the owner. */
29597
+ mediaId: number().int(),
29536
29598
  kind: string(),
29537
29599
  url: string(),
29538
29600
  sizeBytes: number(),
@@ -29591,7 +29653,7 @@ method(object({
29591
29653
  }), method(object({
29592
29654
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29593
29655
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29594
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29656
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29595
29657
  * INPUT field: stripped by the hub router until the train ships, which
29596
29658
  * resolves to `false` and is exactly the intended default. */
29597
29659
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31160,7 +31222,14 @@ var SceneReferenceSchema = object({
31160
31222
  modelId: string(),
31161
31223
  condition: SceneConditionSchema,
31162
31224
  capturedAt: number(),
31163
- thumbnailMediaId: string().optional(),
31225
+ /**
31226
+ * The reference thumbnail's media ROW id (D482).
31227
+ *
31228
+ * The name already said `mediaId` while the value was the row's composite
31229
+ * key — a leftover that read as done and was not. It is the row's own id
31230
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31231
+ */
31232
+ thumbnailMediaId: number().int().optional(),
31164
31233
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31165
31234
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31166
31235
  * normalized rect frame a different piece of world, and the scene would
@@ -33144,7 +33213,7 @@ var StationaryObjectSchema = object({
33144
33213
  /** Enrichment label carried from the source track (identity / plate). */
33145
33214
  label: string().optional(),
33146
33215
  /** Native-resolution key-frame media key for the parked object's best image. */
33147
- keyFrameMediaKey: string().optional()
33216
+ keyFrameMediaId: number().int().optional()
33148
33217
  });
33149
33218
  var CameraOccupancySnapshotSchema = object({
33150
33219
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35882,6 +35951,12 @@ Object.freeze({
35882
35951
  addonId: null,
35883
35952
  access: "create"
35884
35953
  },
35954
+ "dataStoreProvider.upsert": {
35955
+ capName: "data-store-provider",
35956
+ capScope: "system",
35957
+ addonId: null,
35958
+ access: "create"
35959
+ },
35885
35960
  "dayNight.getOptions": {
35886
35961
  capName: "day-night",
35887
35962
  capScope: "device",
@@ -39746,6 +39821,12 @@ Object.freeze({
39746
39821
  addonId: null,
39747
39822
  access: "create"
39748
39823
  },
39824
+ "settingsStore.upsert": {
39825
+ capName: "settings-store",
39826
+ capScope: "system",
39827
+ addonId: null,
39828
+ access: "create"
39829
+ },
39749
39830
  "smtpProvider.getStatus": {
39750
39831
  capName: "smtp-provider",
39751
39832
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../types/dist/event-category-DAXzJeTX.mjs
1
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
2
2
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
3
3
  EventCategory["SystemBoot"] = "system.boot";
4
4
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -559,7 +559,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
559
559
  * a package zone — a newly-appeared stationary object of a package class
560
560
  * that cleared the class / zone / dwell / size gates. Payload:
561
561
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
562
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
562
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
563
563
  * Telemetry (D8): the durable record is the `package-events` store row;
564
564
  * this bus topic drives notifier rules + live UI. See
565
565
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5345,7 +5345,7 @@ var ZodIssueCode = {
5345
5345
  var ZodFirstPartyTypeKind;
5346
5346
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5347
5347
  //#endregion
5348
- //#region ../types/dist/sleep-BnujYGPe.mjs
5348
+ //#region ../types/dist/sleep-CnLyvg3w.mjs
5349
5349
  /**
5350
5350
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5351
5351
  * window to float samples (D455).
@@ -7312,13 +7312,30 @@ new Set([
7312
7312
  "scene",
7313
7313
  "motion",
7314
7314
  "object",
7315
- "audio"
7315
+ "audio",
7316
+ "mosaic"
7316
7317
  ]);
7317
- new Set([
7318
+ /**
7319
+ * The owner types that are event tables, in the order the event-media resolver
7320
+ * should consider them. Exported so a consumer asking "is this key an event?"
7321
+ * does not re-spell the list and drift from it.
7322
+ */
7323
+ var EVENT_OWNER_TYPES = [
7318
7324
  "motion",
7319
7325
  "object",
7320
7326
  "audio"
7321
- ]);
7327
+ ];
7328
+ /**
7329
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7330
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7331
+ * `(table, id)`).
7332
+ *
7333
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7334
+ * table would otherwise be accepted by the codec and refused at the door, with
7335
+ * nothing failing until a caller asked.
7336
+ */
7337
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7338
+ new Set(EVENT_OWNER_TYPES);
7322
7339
  var EncodeProfileSchema = object({
7323
7340
  video: object({
7324
7341
  codec: _enum([
@@ -12517,6 +12534,16 @@ method(object({
12517
12534
  filter: QueryFilterSchema.optional(),
12518
12535
  columns: array(string()).readonly().optional()
12519
12536
  }), array(SettingsRecordSchema).readonly()), method(object({
12537
+ namespace: string().optional(),
12538
+ collection: string(),
12539
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12540
+ * index on the collection. Never empty. */
12541
+ conflict: array(string()).readonly(),
12542
+ record: BulkRecordSchema
12543
+ }), object({
12544
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12545
+ * row's own id on an update. */
12546
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12520
12547
  namespace: string().optional(),
12521
12548
  collection: string(),
12522
12549
  record: object({
@@ -12651,6 +12678,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12651
12678
  id: union([string(), number()]) }), {
12652
12679
  kind: "mutation",
12653
12680
  auth: "admin"
12681
+ }), method(object({
12682
+ namespace: string().optional(),
12683
+ collection: string(),
12684
+ conflict: array(string()).readonly(),
12685
+ record: BulkRecordSchema
12686
+ }), object({ id: union([string(), number()]) }), {
12687
+ kind: "mutation",
12688
+ auth: "admin"
12654
12689
  }), method(object({
12655
12690
  namespace: string().optional(),
12656
12691
  collection: string(),
@@ -19133,8 +19168,8 @@ var TrackPositionSchema = object({
19133
19168
  var TrackSnapshotSchema = object({
19134
19169
  timestamp: number(),
19135
19170
  position: TrackPositionSchema,
19136
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19137
- mediaKey: string()
19171
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19172
+ mediaId: number().int()
19138
19173
  });
19139
19174
  /**
19140
19175
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19738,14 +19773,14 @@ var ObjectEventSchema = object({
19738
19773
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19739
19774
  frameWidth: number().optional(),
19740
19775
  frameHeight: number().optional(),
19741
- /** MediaStore key for the crop attached to this event (if any). */
19742
- mediaKey: string().optional(),
19743
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19776
+ /** Media row id of the crop attached to this event (if any) — D482. */
19777
+ mediaId: number().int().optional(),
19778
+ /** Design B: media row id of the track's native-resolution key frame (the
19744
19779
  * best-detection full frame). Resolve via the event-media data-plane
19745
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19780
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19746
19781
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19747
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19748
- keyFrameMediaKey: string().optional(),
19782
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19783
+ keyFrameMediaId: number().int().optional(),
19749
19784
  /** Populated by B5 (recording playback URL for this event). */
19750
19785
  mediaUrl: string().optional(),
19751
19786
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19795,7 +19830,7 @@ var MediaFileKindEnum = _enum([
19795
19830
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19796
19831
  *
19797
19832
  * `url` points at the `event-media` data plane
19798
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19833
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19799
19834
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19800
19835
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19801
19836
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19803,10 +19838,13 @@ var MediaFileKindEnum = _enum([
19803
19838
  * `data-plane-access.ts` for the rule and the one gap it does not close
19804
19839
  * (per-device scoping).
19805
19840
  *
19806
- * The URL is built from the row's **stored** key, which is not always its
19807
- * published `kind`: a track's face/plate crop is stored as `crop` under
19841
+ * The URL is built from the row's own id, which since D482 says nothing about
19842
+ * the row's owner or kind — and that is the point. The published `kind` is not
19843
+ * the stored one (a track's face/plate crop is stored as `crop` under
19808
19844
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19809
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19845
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19846
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19847
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19810
19848
  *
19811
19849
  * ## `base64` is TRANSITIONAL and is going away
19812
19850
  *
@@ -19819,7 +19857,19 @@ var MediaFileKindEnum = _enum([
19819
19857
  * `analytics-query-facade.ts`; nothing else reads it.
19820
19858
  */
19821
19859
  var MediaFileSchema = object({
19822
- key: string(),
19860
+ /**
19861
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19862
+ *
19863
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19864
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19865
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19866
+ * different rows that spell the same number, and a track whose
19867
+ * `bestEventId` was an object event was served a motion event's snapshot —
19868
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19869
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19870
+ * at all.
19871
+ */
19872
+ mediaId: number().int(),
19823
19873
  kind: MediaFileKindEnum,
19824
19874
  sizeBytes: number(),
19825
19875
  timestamp: number()
@@ -19927,7 +19977,7 @@ var RetrainTrackSchema = object({
19927
19977
  });
19928
19978
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19929
19979
  var RetrainFrameCandidateSchema = object({
19930
- mediaKey: string(),
19980
+ mediaId: number().int(),
19931
19981
  kind: MediaFileKindEnum,
19932
19982
  timestamp: number(),
19933
19983
  sizeBytes: number().int(),
@@ -19941,7 +19991,7 @@ var RetrainFrameSchema = object({
19941
19991
  deviceId: number(),
19942
19992
  trackId: string(),
19943
19993
  /** Provenance only. It may already point at nothing — that is expected. */
19944
- sourceMediaKey: string(),
19994
+ sourceMediaId: number().int(),
19945
19995
  sourceKind: MediaFileKindEnum,
19946
19996
  sizeBytes: number().int(),
19947
19997
  width: number().int(),
@@ -19957,7 +20007,7 @@ var RetrainCopyRefusalSchema = _enum([
19957
20007
  var RetrainFrameSelectionSchema = object({
19958
20008
  copied: array(RetrainFrameSchema).readonly(),
19959
20009
  refused: array(object({
19960
- sourceMediaKey: string(),
20010
+ sourceMediaId: number().int(),
19961
20011
  reason: RetrainCopyRefusalSchema
19962
20012
  })).readonly()
19963
20013
  });
@@ -19965,7 +20015,7 @@ var RetrainFrameListSchema = object({
19965
20015
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19966
20016
  copies: array(RetrainFrameSchema).readonly(),
19967
20017
  /** What the page pre-selects — the native key frame when one survives. */
19968
- autoPickMediaKey: string().optional()
20018
+ autoPickMediaId: number().int().optional()
19969
20019
  });
19970
20020
  /** What the operator asked the assist to look for. */
19971
20021
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20060,9 +20110,11 @@ var RecentTracksQueryInput = object({
20060
20110
  });
20061
20111
  /**
20062
20112
  * A Summary (D359): the per-camera session envelope that references tracks.
20063
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20064
- * served by the event-media plane by key), null until the first frame with a
20065
- * member visible has been materialised.
20113
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20114
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20115
+ * first frame with a member visible has been materialised (D482 — it was the
20116
+ * row's composite key, which embedded the owner and could name another table's
20117
+ * row once event ids became per-table rowids).
20066
20118
  */
20067
20119
  var SummarySchema = object({
20068
20120
  id: string(),
@@ -20071,7 +20123,7 @@ var SummarySchema = object({
20071
20123
  lastActiveAt: number(),
20072
20124
  sealedAt: number().nullable(),
20073
20125
  memberCount: number().int().nonnegative(),
20074
- groupShotKey: string().nullable()
20126
+ groupShotMediaId: number().int().nullable()
20075
20127
  });
20076
20128
  var SummariesQueryInput = object({
20077
20129
  deviceIds: array(number()).min(1),
@@ -20625,7 +20677,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20625
20677
  }), method(object({
20626
20678
  deviceId: number(),
20627
20679
  trackId: string(),
20628
- mediaKeys: array(string()).min(1)
20680
+ mediaIds: array(number().int()).min(1)
20629
20681
  }), RetrainFrameSelectionSchema, {
20630
20682
  kind: "mutation",
20631
20683
  auth: "admin"
@@ -20693,7 +20745,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20693
20745
  kind: "query",
20694
20746
  auth: "admin"
20695
20747
  }), method(object({
20696
- eventId: string(),
20748
+ /** Which of the three event tables owns the row. */
20749
+ ownerType: EventOwnerTypeSchema,
20750
+ /** The event's rowid in that table (D474: an event id is a number). */
20751
+ eventId: number().int(),
20697
20752
  kind: MediaFileKindEnum.optional(),
20698
20753
  deviceId: number()
20699
20754
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20704,7 +20759,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20704
20759
  trackId: string(),
20705
20760
  deviceId: number()
20706
20761
  }), array(MediaFileInfoSchema).readonly()), method(object({
20707
- eventId: string(),
20762
+ ownerType: EventOwnerTypeSchema,
20763
+ eventId: number().int(),
20708
20764
  deviceId: number()
20709
20765
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20710
20766
  kind: "mutation",
@@ -25914,8 +25970,8 @@ var IdentitySchema = object({
25914
25970
  id: string(),
25915
25971
  name: string(),
25916
25972
  sampleCount: number().int().nonnegative(),
25917
- coverMediaKey: string().optional(),
25918
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25973
+ coverMediaId: number().int().optional(),
25974
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25919
25975
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25920
25976
  coverBase64: string().optional()
25921
25977
  });
@@ -25941,8 +25997,8 @@ var IdentitySampleAuditRowSchema = object({
25941
25997
  enrolledAt: number().int(),
25942
25998
  deviceId: number().int().optional(),
25943
25999
  source: _enum(["detected", "photo"]),
25944
- /** The enrolled crop's media key, so the panel can show the evidence. */
25945
- mediaKey: string().optional(),
26000
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
26001
+ mediaId: number().int().optional(),
25946
26002
  /**
25947
26003
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25948
26004
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -26011,15 +26067,15 @@ var FaceInfoSchema = object({
26011
26067
  */
26012
26068
  cropUrl: string().optional(),
26013
26069
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
26014
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26070
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
26015
26071
  * legacy rows written before design B. */
26016
26072
  faceBbox: BoundingBoxSchema.optional(),
26017
26073
  /** Design B: MediaStore key of the track's native-resolution key frame.
26018
26074
  * Fetch the native JPEG via the event-media data-plane
26019
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26075
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26020
26076
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26021
26077
  * back to the inline `base64` face crop. */
26022
- keyFrameMediaKey: string().optional(),
26078
+ keyFrameMediaId: number().int().optional(),
26023
26079
  /** Winning identity-match cosine (0..1) for this face's track, when an
26024
26080
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26025
26081
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26088,11 +26144,14 @@ var FaceClusterSchema = object({
26088
26144
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26089
26145
  * dialog already rendering its key FRAME from the `event-media` plane.
26090
26146
  *
26091
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26092
- * media key directly, so this needed no new plane and no new access decision.
26147
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26148
+ * resolves a media row by its own id, so this needed no new plane and no new
26149
+ * access decision.
26093
26150
  */
26094
26151
  var MediaFileLiteSchema$1 = object({
26095
- key: string(),
26152
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26153
+ * (D482). It was the row's composite key, which embedded the owner. */
26154
+ mediaId: number().int(),
26096
26155
  kind: string(),
26097
26156
  url: string(),
26098
26157
  sizeBytes: number(),
@@ -26109,7 +26168,7 @@ method(object({
26109
26168
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26110
26169
  * four times and the viewer holds at `staleTime: 30_000`.
26111
26170
  *
26112
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26171
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26113
26172
  * the `event-media` plane serves that key `immutable` with an ETag.
26114
26173
  *
26115
26174
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29472,8 +29531,8 @@ var VehicleSchema = object({
29472
29531
  id: string(),
29473
29532
  name: string(),
29474
29533
  sampleCount: number().int().nonnegative(),
29475
- coverMediaKey: string().optional(),
29476
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29534
+ coverMediaId: number().int().optional(),
29535
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29477
29536
  coverBase64: string().optional()
29478
29537
  });
29479
29538
  var VehicleSampleInfoSchema = object({
@@ -29506,7 +29565,7 @@ var PlateInfoSchema = object({
29506
29565
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29507
29566
  plateBbox: BoundingBoxSchema.optional(),
29508
29567
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29509
- keyFrameMediaKey: string().optional(),
29568
+ keyFrameMediaId: number().int().optional(),
29510
29569
  base64: string().optional(),
29511
29570
  /**
29512
29571
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29527,11 +29586,14 @@ var PlateInfoSchema = object({
29527
29586
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29528
29587
  * dialog already rendering its key FRAME from the `event-media` plane.
29529
29588
  *
29530
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29531
- * media key directly, so this needed no new plane and no new access decision.
29589
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29590
+ * resolves a media row by its own id, so this needed no new plane and no new
29591
+ * access decision.
29532
29592
  */
29533
29593
  var MediaFileLiteSchema = object({
29534
- key: string(),
29594
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29595
+ * (D482). It was the row's composite key, which embedded the owner. */
29596
+ mediaId: number().int(),
29535
29597
  kind: string(),
29536
29598
  url: string(),
29537
29599
  sizeBytes: number(),
@@ -29590,7 +29652,7 @@ method(object({
29590
29652
  }), method(object({
29591
29653
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29592
29654
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29593
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29655
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29594
29656
  * INPUT field: stripped by the hub router until the train ships, which
29595
29657
  * resolves to `false` and is exactly the intended default. */
29596
29658
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31159,7 +31221,14 @@ var SceneReferenceSchema = object({
31159
31221
  modelId: string(),
31160
31222
  condition: SceneConditionSchema,
31161
31223
  capturedAt: number(),
31162
- thumbnailMediaId: string().optional(),
31224
+ /**
31225
+ * The reference thumbnail's media ROW id (D482).
31226
+ *
31227
+ * The name already said `mediaId` while the value was the row's composite
31228
+ * key — a leftover that read as done and was not. It is the row's own id
31229
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31230
+ */
31231
+ thumbnailMediaId: number().int().optional(),
31163
31232
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31164
31233
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31165
31234
  * normalized rect frame a different piece of world, and the scene would
@@ -33143,7 +33212,7 @@ var StationaryObjectSchema = object({
33143
33212
  /** Enrichment label carried from the source track (identity / plate). */
33144
33213
  label: string().optional(),
33145
33214
  /** Native-resolution key-frame media key for the parked object's best image. */
33146
- keyFrameMediaKey: string().optional()
33215
+ keyFrameMediaId: number().int().optional()
33147
33216
  });
33148
33217
  var CameraOccupancySnapshotSchema = object({
33149
33218
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35881,6 +35950,12 @@ Object.freeze({
35881
35950
  addonId: null,
35882
35951
  access: "create"
35883
35952
  },
35953
+ "dataStoreProvider.upsert": {
35954
+ capName: "data-store-provider",
35955
+ capScope: "system",
35956
+ addonId: null,
35957
+ access: "create"
35958
+ },
35884
35959
  "dayNight.getOptions": {
35885
35960
  capName: "day-night",
35886
35961
  capScope: "device",
@@ -39745,6 +39820,12 @@ Object.freeze({
39745
39820
  addonId: null,
39746
39821
  access: "create"
39747
39822
  },
39823
+ "settingsStore.upsert": {
39824
+ capName: "settings-store",
39825
+ capScope: "system",
39826
+ addonId: null,
39827
+ access: "create"
39828
+ },
39748
39829
  "smtpProvider.getStatus": {
39749
39830
  capName: "smtp-provider",
39750
39831
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-unifi",
3
- "version": "0.2.98",
3
+ "version": "0.2.100",
4
4
  "description": "UniFi Network controller device-provider addon for CamStack — local-controller infra switches/APs (as containers) + network-client presence. NO cameras/Protect.",
5
5
  "keywords": [
6
6
  "camstack",