@camstack/addon-provider-velux 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",
@@ -25898,8 +25954,8 @@ var IdentitySchema = object({
25898
25954
  id: string(),
25899
25955
  name: string(),
25900
25956
  sampleCount: number().int().nonnegative(),
25901
- coverMediaKey: string().optional(),
25902
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25957
+ coverMediaId: number().int().optional(),
25958
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25903
25959
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25904
25960
  coverBase64: string().optional()
25905
25961
  });
@@ -25925,8 +25981,8 @@ var IdentitySampleAuditRowSchema = object({
25925
25981
  enrolledAt: number().int(),
25926
25982
  deviceId: number().int().optional(),
25927
25983
  source: _enum(["detected", "photo"]),
25928
- /** The enrolled crop's media key, so the panel can show the evidence. */
25929
- mediaKey: string().optional(),
25984
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25985
+ mediaId: number().int().optional(),
25930
25986
  /**
25931
25987
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25932
25988
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25995,15 +26051,15 @@ var FaceInfoSchema = object({
25995
26051
  */
25996
26052
  cropUrl: string().optional(),
25997
26053
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25998
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26054
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25999
26055
  * legacy rows written before design B. */
26000
26056
  faceBbox: BoundingBoxSchema.optional(),
26001
26057
  /** Design B: MediaStore key of the track's native-resolution key frame.
26002
26058
  * Fetch the native JPEG via the event-media data-plane
26003
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26059
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26004
26060
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26005
26061
  * back to the inline `base64` face crop. */
26006
- keyFrameMediaKey: string().optional(),
26062
+ keyFrameMediaId: number().int().optional(),
26007
26063
  /** Winning identity-match cosine (0..1) for this face's track, when an
26008
26064
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26009
26065
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26072,11 +26128,14 @@ var FaceClusterSchema = object({
26072
26128
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26073
26129
  * dialog already rendering its key FRAME from the `event-media` plane.
26074
26130
  *
26075
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26076
- * media key directly, so this needed no new plane and no new access decision.
26131
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26132
+ * resolves a media row by its own id, so this needed no new plane and no new
26133
+ * access decision.
26077
26134
  */
26078
26135
  var MediaFileLiteSchema$1 = object({
26079
- key: string(),
26136
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26137
+ * (D482). It was the row's composite key, which embedded the owner. */
26138
+ mediaId: number().int(),
26080
26139
  kind: string(),
26081
26140
  url: string(),
26082
26141
  sizeBytes: number(),
@@ -26093,7 +26152,7 @@ method(object({
26093
26152
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26094
26153
  * four times and the viewer holds at `staleTime: 30_000`.
26095
26154
  *
26096
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26155
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26097
26156
  * the `event-media` plane serves that key `immutable` with an ETag.
26098
26157
  *
26099
26158
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29456,8 +29515,8 @@ var VehicleSchema = object({
29456
29515
  id: string(),
29457
29516
  name: string(),
29458
29517
  sampleCount: number().int().nonnegative(),
29459
- coverMediaKey: string().optional(),
29460
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29518
+ coverMediaId: number().int().optional(),
29519
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29461
29520
  coverBase64: string().optional()
29462
29521
  });
29463
29522
  var VehicleSampleInfoSchema = object({
@@ -29490,7 +29549,7 @@ var PlateInfoSchema = object({
29490
29549
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29491
29550
  plateBbox: BoundingBoxSchema.optional(),
29492
29551
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29493
- keyFrameMediaKey: string().optional(),
29552
+ keyFrameMediaId: number().int().optional(),
29494
29553
  base64: string().optional(),
29495
29554
  /**
29496
29555
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29511,11 +29570,14 @@ var PlateInfoSchema = object({
29511
29570
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29512
29571
  * dialog already rendering its key FRAME from the `event-media` plane.
29513
29572
  *
29514
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29515
- * media key directly, so this needed no new plane and no new access decision.
29573
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29574
+ * resolves a media row by its own id, so this needed no new plane and no new
29575
+ * access decision.
29516
29576
  */
29517
29577
  var MediaFileLiteSchema = object({
29518
- key: string(),
29578
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29579
+ * (D482). It was the row's composite key, which embedded the owner. */
29580
+ mediaId: number().int(),
29519
29581
  kind: string(),
29520
29582
  url: string(),
29521
29583
  sizeBytes: number(),
@@ -29574,7 +29636,7 @@ method(object({
29574
29636
  }), method(object({
29575
29637
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29576
29638
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29577
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29639
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29578
29640
  * INPUT field: stripped by the hub router until the train ships, which
29579
29641
  * resolves to `false` and is exactly the intended default. */
29580
29642
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31143,7 +31205,14 @@ var SceneReferenceSchema = object({
31143
31205
  modelId: string(),
31144
31206
  condition: SceneConditionSchema,
31145
31207
  capturedAt: number(),
31146
- thumbnailMediaId: string().optional(),
31208
+ /**
31209
+ * The reference thumbnail's media ROW id (D482).
31210
+ *
31211
+ * The name already said `mediaId` while the value was the row's composite
31212
+ * key — a leftover that read as done and was not. It is the row's own id
31213
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31214
+ */
31215
+ thumbnailMediaId: number().int().optional(),
31147
31216
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31148
31217
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31149
31218
  * normalized rect frame a different piece of world, and the scene would
@@ -33127,7 +33196,7 @@ var StationaryObjectSchema = object({
33127
33196
  /** Enrichment label carried from the source track (identity / plate). */
33128
33197
  label: string().optional(),
33129
33198
  /** Native-resolution key-frame media key for the parked object's best image. */
33130
- keyFrameMediaKey: string().optional()
33199
+ keyFrameMediaId: number().int().optional()
33131
33200
  });
33132
33201
  var CameraOccupancySnapshotSchema = object({
33133
33202
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35865,6 +35934,12 @@ Object.freeze({
35865
35934
  addonId: null,
35866
35935
  access: "create"
35867
35936
  },
35937
+ "dataStoreProvider.upsert": {
35938
+ capName: "data-store-provider",
35939
+ capScope: "system",
35940
+ addonId: null,
35941
+ access: "create"
35942
+ },
35868
35943
  "dayNight.getOptions": {
35869
35944
  capName: "day-night",
35870
35945
  capScope: "device",
@@ -39729,6 +39804,12 @@ Object.freeze({
39729
39804
  addonId: null,
39730
39805
  access: "create"
39731
39806
  },
39807
+ "settingsStore.upsert": {
39808
+ capName: "settings-store",
39809
+ capScope: "system",
39810
+ addonId: null,
39811
+ access: "create"
39812
+ },
39732
39813
  "smtpProvider.getStatus": {
39733
39814
  capName: "smtp-provider",
39734
39815
  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",
@@ -25897,8 +25953,8 @@ var IdentitySchema = object({
25897
25953
  id: string(),
25898
25954
  name: string(),
25899
25955
  sampleCount: number().int().nonnegative(),
25900
- coverMediaKey: string().optional(),
25901
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25956
+ coverMediaId: number().int().optional(),
25957
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25902
25958
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25903
25959
  coverBase64: string().optional()
25904
25960
  });
@@ -25924,8 +25980,8 @@ var IdentitySampleAuditRowSchema = object({
25924
25980
  enrolledAt: number().int(),
25925
25981
  deviceId: number().int().optional(),
25926
25982
  source: _enum(["detected", "photo"]),
25927
- /** The enrolled crop's media key, so the panel can show the evidence. */
25928
- mediaKey: string().optional(),
25983
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25984
+ mediaId: number().int().optional(),
25929
25985
  /**
25930
25986
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25931
25987
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25994,15 +26050,15 @@ var FaceInfoSchema = object({
25994
26050
  */
25995
26051
  cropUrl: string().optional(),
25996
26052
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25997
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26053
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25998
26054
  * legacy rows written before design B. */
25999
26055
  faceBbox: BoundingBoxSchema.optional(),
26000
26056
  /** Design B: MediaStore key of the track's native-resolution key frame.
26001
26057
  * Fetch the native JPEG via the event-media data-plane
26002
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26058
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26003
26059
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26004
26060
  * back to the inline `base64` face crop. */
26005
- keyFrameMediaKey: string().optional(),
26061
+ keyFrameMediaId: number().int().optional(),
26006
26062
  /** Winning identity-match cosine (0..1) for this face's track, when an
26007
26063
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26008
26064
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26071,11 +26127,14 @@ var FaceClusterSchema = object({
26071
26127
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26072
26128
  * dialog already rendering its key FRAME from the `event-media` plane.
26073
26129
  *
26074
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26075
- * media key directly, so this needed no new plane and no new access decision.
26130
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26131
+ * resolves a media row by its own id, so this needed no new plane and no new
26132
+ * access decision.
26076
26133
  */
26077
26134
  var MediaFileLiteSchema$1 = object({
26078
- key: string(),
26135
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26136
+ * (D482). It was the row's composite key, which embedded the owner. */
26137
+ mediaId: number().int(),
26079
26138
  kind: string(),
26080
26139
  url: string(),
26081
26140
  sizeBytes: number(),
@@ -26092,7 +26151,7 @@ method(object({
26092
26151
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26093
26152
  * four times and the viewer holds at `staleTime: 30_000`.
26094
26153
  *
26095
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26154
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26096
26155
  * the `event-media` plane serves that key `immutable` with an ETag.
26097
26156
  *
26098
26157
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29455,8 +29514,8 @@ var VehicleSchema = object({
29455
29514
  id: string(),
29456
29515
  name: string(),
29457
29516
  sampleCount: number().int().nonnegative(),
29458
- coverMediaKey: string().optional(),
29459
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29517
+ coverMediaId: number().int().optional(),
29518
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29460
29519
  coverBase64: string().optional()
29461
29520
  });
29462
29521
  var VehicleSampleInfoSchema = object({
@@ -29489,7 +29548,7 @@ var PlateInfoSchema = object({
29489
29548
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29490
29549
  plateBbox: BoundingBoxSchema.optional(),
29491
29550
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29492
- keyFrameMediaKey: string().optional(),
29551
+ keyFrameMediaId: number().int().optional(),
29493
29552
  base64: string().optional(),
29494
29553
  /**
29495
29554
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29510,11 +29569,14 @@ var PlateInfoSchema = object({
29510
29569
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29511
29570
  * dialog already rendering its key FRAME from the `event-media` plane.
29512
29571
  *
29513
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29514
- * media key directly, so this needed no new plane and no new access decision.
29572
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29573
+ * resolves a media row by its own id, so this needed no new plane and no new
29574
+ * access decision.
29515
29575
  */
29516
29576
  var MediaFileLiteSchema = object({
29517
- key: string(),
29577
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29578
+ * (D482). It was the row's composite key, which embedded the owner. */
29579
+ mediaId: number().int(),
29518
29580
  kind: string(),
29519
29581
  url: string(),
29520
29582
  sizeBytes: number(),
@@ -29573,7 +29635,7 @@ method(object({
29573
29635
  }), method(object({
29574
29636
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29575
29637
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29576
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29638
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29577
29639
  * INPUT field: stripped by the hub router until the train ships, which
29578
29640
  * resolves to `false` and is exactly the intended default. */
29579
29641
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31142,7 +31204,14 @@ var SceneReferenceSchema = object({
31142
31204
  modelId: string(),
31143
31205
  condition: SceneConditionSchema,
31144
31206
  capturedAt: number(),
31145
- thumbnailMediaId: string().optional(),
31207
+ /**
31208
+ * The reference thumbnail's media ROW id (D482).
31209
+ *
31210
+ * The name already said `mediaId` while the value was the row's composite
31211
+ * key — a leftover that read as done and was not. It is the row's own id
31212
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31213
+ */
31214
+ thumbnailMediaId: number().int().optional(),
31146
31215
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31147
31216
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31148
31217
  * normalized rect frame a different piece of world, and the scene would
@@ -33126,7 +33195,7 @@ var StationaryObjectSchema = object({
33126
33195
  /** Enrichment label carried from the source track (identity / plate). */
33127
33196
  label: string().optional(),
33128
33197
  /** Native-resolution key-frame media key for the parked object's best image. */
33129
- keyFrameMediaKey: string().optional()
33198
+ keyFrameMediaId: number().int().optional()
33130
33199
  });
33131
33200
  var CameraOccupancySnapshotSchema = object({
33132
33201
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35864,6 +35933,12 @@ Object.freeze({
35864
35933
  addonId: null,
35865
35934
  access: "create"
35866
35935
  },
35936
+ "dataStoreProvider.upsert": {
35937
+ capName: "data-store-provider",
35938
+ capScope: "system",
35939
+ addonId: null,
35940
+ access: "create"
35941
+ },
35867
35942
  "dayNight.getOptions": {
35868
35943
  capName: "day-night",
35869
35944
  capScope: "device",
@@ -39728,6 +39803,12 @@ Object.freeze({
39728
39803
  addonId: null,
39729
39804
  access: "create"
39730
39805
  },
39806
+ "settingsStore.upsert": {
39807
+ capName: "settings-store",
39808
+ capScope: "system",
39809
+ addonId: null,
39810
+ access: "create"
39811
+ },
39731
39812
  "smtpProvider.getStatus": {
39732
39813
  capName: "smtp-provider",
39733
39814
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-velux",
3
- "version": "0.2.98",
3
+ "version": "0.2.100",
4
4
  "description": "Velux KLF-200 device-provider addon for CamStack — local-gateway window + shutter covers (positional, no tilt)",
5
5
  "keywords": [
6
6
  "camstack",