@camstack/addon-provider-velux 0.2.41 → 0.2.43

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 +154 -19
  2. package/dist/addon.mjs +154 -19
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -8039,18 +8039,61 @@ var RelocateFootageInputSchema = object({
8039
8039
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8040
8040
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8041
8041
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8042
+ /**
8043
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8044
+ * mover (the engine already walks both collections with a timestamp cursor and
8045
+ * already has a stamp-without-copy path).
8046
+ *
8047
+ * - `move` — the default and the historical behaviour: event-media and
8048
+ * retrain blobs move to `toLocationId` and their rows are
8049
+ * stamped. The enrolled gallery is skipped (D197).
8050
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8051
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8052
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8053
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8054
+ * instant a repoint moves that pointer the row reads from the
8055
+ * new disk while its bytes are on the old one.
8056
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8057
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8058
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8059
+ * never run beside a live second location: it is stop-the-world
8060
+ * by construction, which is acceptable only because the gallery
8061
+ * is a few KB per enrolled sample.
8062
+ */
8063
+ var MediaRelocateModeSchema = _enum([
8064
+ "move",
8065
+ "seal",
8066
+ "gallery"
8067
+ ]);
8042
8068
  var RelocateMediaInputSchema = object({
8043
8069
  toLocationId: string(),
8044
- throttleMbps: number().min(1).max(1e3).optional()
8070
+ throttleMbps: number().min(1).max(1e3).optional(),
8071
+ /** Omitted = `move`, the pre-existing behaviour. */
8072
+ mode: MediaRelocateModeSchema.optional()
8073
+ });
8074
+ /** How many rows still carry NO `locationId` — the population a repoint would
8075
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8076
+ * value that permits a non-blocking `eventMedia` cutover. */
8077
+ var UnstampedEventMediaCountSchema = object({
8078
+ media: number().int().nonnegative(),
8079
+ retrainFrames: number().int().nonnegative(),
8080
+ total: number().int().nonnegative()
8045
8081
  });
8046
8082
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8047
- /** The independently selectable logical storage classes. `recordings`
8048
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8049
- * segments; `eventMedia` is post-analysis blobs. */
8083
+ /** The independently selectable logical storage classes — every class
8084
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8085
+ * Zod enum error where they should meet an explanation.
8086
+ *
8087
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8088
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8089
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8090
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8050
8091
  var StorageMigrationClassSchema = _enum([
8051
8092
  "recordings",
8052
8093
  "recordingsLow",
8053
- "eventMedia"
8094
+ "eventMedia",
8095
+ "backups",
8096
+ "galleryMedia"
8054
8097
  ]);
8055
8098
  /** A destination is always an existing, fully-qualified location id. The
8056
8099
  * migration API intentionally never changes a source location's `basePath`:
@@ -8058,20 +8101,56 @@ var StorageMigrationClassSchema = _enum([
8058
8101
  var StorageMigrationDestinationsSchema = object({
8059
8102
  recordings: string().min(1).optional(),
8060
8103
  recordingsLow: string().min(1).optional(),
8061
- eventMedia: string().min(1).optional()
8104
+ eventMedia: string().min(1).optional(),
8105
+ backups: string().min(1).optional(),
8106
+ galleryMedia: string().min(1).optional()
8062
8107
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8108
+ /**
8109
+ * How a migration sequences the cutover against the byte move.
8110
+ *
8111
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8112
+ * resume. Recording is stopped for the whole move. Right
8113
+ * for a small or a cold class, and the only legal mode for
8114
+ * a `cardinality: 'single'` class.
8115
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8116
+ * refresh, resume, then move the past with everything
8117
+ * running. The pause is three bounded instants (a detach +
8118
+ * attach round, a write-gate drain, a lease) instead of one
8119
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8120
+ * stopped recording under `blocking`; the same move is
8121
+ * seconds of stopped recording under `nonBlocking`.
8122
+ *
8123
+ * The mode is on the JOB, not only on the input, because `status` is where an
8124
+ * operator finds out which one is running.
8125
+ */
8126
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8063
8127
  /** Shared input for planning and starting an orchestrated storage migration. */
8064
8128
  var StorageMigrationInputSchema = object({
8065
8129
  destinations: StorageMigrationDestinationsSchema,
8066
- throttleMbps: number().min(1).max(1e3).optional()
8130
+ throttleMbps: number().min(1).max(1e3).optional(),
8131
+ /** Omitted = `blocking`, which stays the default. */
8132
+ mode: StorageMigrationModeSchema.optional()
8067
8133
  });
8068
- /** The durable coordinator state machine. The only phase that changes default
8069
- * locations is `repointing`, after every selected mover has completed and been
8070
- * verified. */
8134
+ /**
8135
+ * The durable coordinator state machine.
8136
+ *
8137
+ * `blocking`:
8138
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8139
+ *
8140
+ * `nonBlocking`:
8141
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8142
+ *
8143
+ * Same phases, different order plus two new ones — not a second mover.
8144
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8145
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8146
+ * `repointing` is still the only phase that changes a default location.
8147
+ */
8071
8148
  var StorageMigrationPhaseSchema = _enum([
8072
8149
  "planning",
8150
+ "sealing",
8073
8151
  "pausing",
8074
8152
  "moving",
8153
+ "draining",
8075
8154
  "verifying",
8076
8155
  "repointing",
8077
8156
  "refreshing",
@@ -8096,6 +8175,9 @@ var StorageMigrationMoveSchema = object({
8096
8175
  var StorageMigrationJobSchema = object({
8097
8176
  jobId: string(),
8098
8177
  phase: StorageMigrationPhaseSchema,
8178
+ /** Which order this job is running. `status` is the only place an operator
8179
+ * can tell a seconds-long cutover from a thirty-hour one. */
8180
+ mode: StorageMigrationModeSchema,
8099
8181
  destinations: StorageMigrationDestinationsSchema,
8100
8182
  throttleMbps: number(),
8101
8183
  moves: array(StorageMigrationMoveSchema),
@@ -8108,13 +8190,30 @@ var StorageMigrationJobSchema = object({
8108
8190
  finishedAt: number().nullable(),
8109
8191
  error: string().nullable()
8110
8192
  });
8193
+ var StorageMigrationFindingSchema = object({
8194
+ code: _enum([
8195
+ "sharesDeviceWithSource",
8196
+ "deviceIdentityUnknown",
8197
+ "unstampedEventMediaRows",
8198
+ "blockingOnly",
8199
+ "noMover"
8200
+ ]),
8201
+ storageClass: StorageMigrationClassSchema,
8202
+ /** Human-readable, already carrying the ids and counts. */
8203
+ message: string()
8204
+ });
8111
8205
  var StorageMigrationPlanSchema = object({
8112
8206
  destinations: StorageMigrationDestinationsSchema,
8207
+ /** The mode this plan was built for. A plan is only valid for its mode: the
8208
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
8209
+ * it. */
8210
+ mode: StorageMigrationModeSchema,
8113
8211
  moves: array(object({
8114
8212
  storageClass: StorageMigrationClassSchema,
8115
8213
  fromLocationId: string(),
8116
8214
  toLocationId: string()
8117
- }))
8215
+ })),
8216
+ findings: array(StorageMigrationFindingSchema)
8118
8217
  });
8119
8218
  /**
8120
8219
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -18967,7 +19066,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18967
19066
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18968
19067
  kind: "mutation",
18969
19068
  auth: "admin"
18970
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19069
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
18971
19070
  kind: "query",
18972
19071
  auth: "admin"
18973
19072
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20874,7 +20973,10 @@ method(object({
20874
20973
  }), StorageLocationSchema, {
20875
20974
  kind: "mutation",
20876
20975
  auth: "admin"
20877
- }), method(object({ id: string() }), _void(), {
20976
+ }), method(object({
20977
+ id: string(),
20978
+ force: boolean().optional()
20979
+ }), _void(), {
20878
20980
  kind: "mutation",
20879
20981
  auth: "admin"
20880
20982
  }), method(object({ id: string() }), object({
@@ -24942,6 +25044,33 @@ var intercomCapability = {
24942
25044
  deviceNative: true,
24943
25045
  mode: "singleton",
24944
25046
  deviceTypes: [DeviceType.Camera],
25047
+ /**
25048
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25049
+ *
25050
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25051
+ * cap has no configuration surface at all: all six methods open, feed and
25052
+ * close one live audio session against one `deviceId`. That is the same
25053
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25054
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25055
+ * `admin` because they change what the device IS.
25056
+ *
25057
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25058
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25059
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25060
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25061
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25062
+ * camera 7.
25063
+ *
25064
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25065
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25066
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25067
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25068
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25069
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25070
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25071
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25072
+ * preset promises a cap no row of that preset can reach.
25073
+ */
24945
25074
  methods: {
24946
25075
  /**
24947
25076
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -24954,7 +25083,7 @@ var intercomCapability = {
24954
25083
  sdpOffer: string()
24955
25084
  }), {
24956
25085
  kind: "mutation",
24957
- auth: "admin"
25086
+ auth: "protected"
24958
25087
  }),
24959
25088
  handleAnswer: method(object({
24960
25089
  deviceId: number(),
@@ -24962,7 +25091,7 @@ var intercomCapability = {
24962
25091
  sdpAnswer: string()
24963
25092
  }), _void(), {
24964
25093
  kind: "mutation",
24965
- auth: "admin"
25094
+ auth: "protected"
24966
25095
  }),
24967
25096
  /** Close explicitly. Server also auto-closes on 30s idle. */
24968
25097
  stopSession: method(object({
@@ -24970,7 +25099,7 @@ var intercomCapability = {
24970
25099
  sessionId: string()
24971
25100
  }), _void(), {
24972
25101
  kind: "mutation",
24973
- auth: "admin"
25102
+ auth: "protected"
24974
25103
  }),
24975
25104
  /**
24976
25105
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -24983,7 +25112,7 @@ var intercomCapability = {
24983
25112
  */
24984
25113
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
24985
25114
  kind: "mutation",
24986
- auth: "admin"
25115
+ auth: "protected"
24987
25116
  }),
24988
25117
  /**
24989
25118
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25018,12 +25147,12 @@ var intercomCapability = {
25018
25147
  sequenceNumber: number().int()
25019
25148
  }), object({ accepted: boolean() }), {
25020
25149
  kind: "mutation",
25021
- auth: "admin"
25150
+ auth: "protected"
25022
25151
  }),
25023
25152
  /** Close the raw-PCM talk session. Idempotent. */
25024
25153
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25025
25154
  kind: "mutation",
25026
- auth: "admin"
25155
+ auth: "protected"
25027
25156
  })
25028
25157
  },
25029
25158
  events: { onStatusChanged: { data: object({
@@ -34704,6 +34833,12 @@ Object.freeze({
34704
34833
  addonId: null,
34705
34834
  access: "create"
34706
34835
  },
34836
+ "pipelineAnalytics.countUnstampedEventMedia": {
34837
+ capName: "pipeline-analytics",
34838
+ capScope: "device",
34839
+ addonId: null,
34840
+ access: "view"
34841
+ },
34707
34842
  "pipelineAnalytics.deleteDeviceEvents": {
34708
34843
  capName: "pipeline-analytics",
34709
34844
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -8038,18 +8038,61 @@ var RelocateFootageInputSchema = object({
8038
8038
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8039
8039
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8040
8040
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8041
+ /**
8042
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8043
+ * mover (the engine already walks both collections with a timestamp cursor and
8044
+ * already has a stamp-without-copy path).
8045
+ *
8046
+ * - `move` — the default and the historical behaviour: event-media and
8047
+ * retrain blobs move to `toLocationId` and their rows are
8048
+ * stamped. The enrolled gallery is skipped (D197).
8049
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8050
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8051
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8052
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8053
+ * instant a repoint moves that pointer the row reads from the
8054
+ * new disk while its bytes are on the old one.
8055
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8056
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8057
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8058
+ * never run beside a live second location: it is stop-the-world
8059
+ * by construction, which is acceptable only because the gallery
8060
+ * is a few KB per enrolled sample.
8061
+ */
8062
+ var MediaRelocateModeSchema = _enum([
8063
+ "move",
8064
+ "seal",
8065
+ "gallery"
8066
+ ]);
8041
8067
  var RelocateMediaInputSchema = object({
8042
8068
  toLocationId: string(),
8043
- throttleMbps: number().min(1).max(1e3).optional()
8069
+ throttleMbps: number().min(1).max(1e3).optional(),
8070
+ /** Omitted = `move`, the pre-existing behaviour. */
8071
+ mode: MediaRelocateModeSchema.optional()
8072
+ });
8073
+ /** How many rows still carry NO `locationId` — the population a repoint would
8074
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8075
+ * value that permits a non-blocking `eventMedia` cutover. */
8076
+ var UnstampedEventMediaCountSchema = object({
8077
+ media: number().int().nonnegative(),
8078
+ retrainFrames: number().int().nonnegative(),
8079
+ total: number().int().nonnegative()
8044
8080
  });
8045
8081
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8046
- /** The independently selectable logical storage classes. `recordings`
8047
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8048
- * segments; `eventMedia` is post-analysis blobs. */
8082
+ /** The independently selectable logical storage classes — every class
8083
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8084
+ * Zod enum error where they should meet an explanation.
8085
+ *
8086
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8087
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8088
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8089
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8049
8090
  var StorageMigrationClassSchema = _enum([
8050
8091
  "recordings",
8051
8092
  "recordingsLow",
8052
- "eventMedia"
8093
+ "eventMedia",
8094
+ "backups",
8095
+ "galleryMedia"
8053
8096
  ]);
8054
8097
  /** A destination is always an existing, fully-qualified location id. The
8055
8098
  * migration API intentionally never changes a source location's `basePath`:
@@ -8057,20 +8100,56 @@ var StorageMigrationClassSchema = _enum([
8057
8100
  var StorageMigrationDestinationsSchema = object({
8058
8101
  recordings: string().min(1).optional(),
8059
8102
  recordingsLow: string().min(1).optional(),
8060
- eventMedia: string().min(1).optional()
8103
+ eventMedia: string().min(1).optional(),
8104
+ backups: string().min(1).optional(),
8105
+ galleryMedia: string().min(1).optional()
8061
8106
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8107
+ /**
8108
+ * How a migration sequences the cutover against the byte move.
8109
+ *
8110
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8111
+ * resume. Recording is stopped for the whole move. Right
8112
+ * for a small or a cold class, and the only legal mode for
8113
+ * a `cardinality: 'single'` class.
8114
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8115
+ * refresh, resume, then move the past with everything
8116
+ * running. The pause is three bounded instants (a detach +
8117
+ * attach round, a write-gate drain, a lease) instead of one
8118
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8119
+ * stopped recording under `blocking`; the same move is
8120
+ * seconds of stopped recording under `nonBlocking`.
8121
+ *
8122
+ * The mode is on the JOB, not only on the input, because `status` is where an
8123
+ * operator finds out which one is running.
8124
+ */
8125
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8062
8126
  /** Shared input for planning and starting an orchestrated storage migration. */
8063
8127
  var StorageMigrationInputSchema = object({
8064
8128
  destinations: StorageMigrationDestinationsSchema,
8065
- throttleMbps: number().min(1).max(1e3).optional()
8129
+ throttleMbps: number().min(1).max(1e3).optional(),
8130
+ /** Omitted = `blocking`, which stays the default. */
8131
+ mode: StorageMigrationModeSchema.optional()
8066
8132
  });
8067
- /** The durable coordinator state machine. The only phase that changes default
8068
- * locations is `repointing`, after every selected mover has completed and been
8069
- * verified. */
8133
+ /**
8134
+ * The durable coordinator state machine.
8135
+ *
8136
+ * `blocking`:
8137
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8138
+ *
8139
+ * `nonBlocking`:
8140
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8141
+ *
8142
+ * Same phases, different order plus two new ones — not a second mover.
8143
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8144
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8145
+ * `repointing` is still the only phase that changes a default location.
8146
+ */
8070
8147
  var StorageMigrationPhaseSchema = _enum([
8071
8148
  "planning",
8149
+ "sealing",
8072
8150
  "pausing",
8073
8151
  "moving",
8152
+ "draining",
8074
8153
  "verifying",
8075
8154
  "repointing",
8076
8155
  "refreshing",
@@ -8095,6 +8174,9 @@ var StorageMigrationMoveSchema = object({
8095
8174
  var StorageMigrationJobSchema = object({
8096
8175
  jobId: string(),
8097
8176
  phase: StorageMigrationPhaseSchema,
8177
+ /** Which order this job is running. `status` is the only place an operator
8178
+ * can tell a seconds-long cutover from a thirty-hour one. */
8179
+ mode: StorageMigrationModeSchema,
8098
8180
  destinations: StorageMigrationDestinationsSchema,
8099
8181
  throttleMbps: number(),
8100
8182
  moves: array(StorageMigrationMoveSchema),
@@ -8107,13 +8189,30 @@ var StorageMigrationJobSchema = object({
8107
8189
  finishedAt: number().nullable(),
8108
8190
  error: string().nullable()
8109
8191
  });
8192
+ var StorageMigrationFindingSchema = object({
8193
+ code: _enum([
8194
+ "sharesDeviceWithSource",
8195
+ "deviceIdentityUnknown",
8196
+ "unstampedEventMediaRows",
8197
+ "blockingOnly",
8198
+ "noMover"
8199
+ ]),
8200
+ storageClass: StorageMigrationClassSchema,
8201
+ /** Human-readable, already carrying the ids and counts. */
8202
+ message: string()
8203
+ });
8110
8204
  var StorageMigrationPlanSchema = object({
8111
8205
  destinations: StorageMigrationDestinationsSchema,
8206
+ /** The mode this plan was built for. A plan is only valid for its mode: the
8207
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
8208
+ * it. */
8209
+ mode: StorageMigrationModeSchema,
8112
8210
  moves: array(object({
8113
8211
  storageClass: StorageMigrationClassSchema,
8114
8212
  fromLocationId: string(),
8115
8213
  toLocationId: string()
8116
- }))
8214
+ })),
8215
+ findings: array(StorageMigrationFindingSchema)
8117
8216
  });
8118
8217
  /**
8119
8218
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -18966,7 +19065,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18966
19065
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18967
19066
  kind: "mutation",
18968
19067
  auth: "admin"
18969
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19068
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
18970
19069
  kind: "query",
18971
19070
  auth: "admin"
18972
19071
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20873,7 +20972,10 @@ method(object({
20873
20972
  }), StorageLocationSchema, {
20874
20973
  kind: "mutation",
20875
20974
  auth: "admin"
20876
- }), method(object({ id: string() }), _void(), {
20975
+ }), method(object({
20976
+ id: string(),
20977
+ force: boolean().optional()
20978
+ }), _void(), {
20877
20979
  kind: "mutation",
20878
20980
  auth: "admin"
20879
20981
  }), method(object({ id: string() }), object({
@@ -24941,6 +25043,33 @@ var intercomCapability = {
24941
25043
  deviceNative: true,
24942
25044
  mode: "singleton",
24943
25045
  deviceTypes: [DeviceType.Camera],
25046
+ /**
25047
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25048
+ *
25049
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25050
+ * cap has no configuration surface at all: all six methods open, feed and
25051
+ * close one live audio session against one `deviceId`. That is the same
25052
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25053
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25054
+ * `admin` because they change what the device IS.
25055
+ *
25056
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25057
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25058
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25059
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25060
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25061
+ * camera 7.
25062
+ *
25063
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25064
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25065
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25066
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25067
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25068
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25069
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25070
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25071
+ * preset promises a cap no row of that preset can reach.
25072
+ */
24944
25073
  methods: {
24945
25074
  /**
24946
25075
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -24953,7 +25082,7 @@ var intercomCapability = {
24953
25082
  sdpOffer: string()
24954
25083
  }), {
24955
25084
  kind: "mutation",
24956
- auth: "admin"
25085
+ auth: "protected"
24957
25086
  }),
24958
25087
  handleAnswer: method(object({
24959
25088
  deviceId: number(),
@@ -24961,7 +25090,7 @@ var intercomCapability = {
24961
25090
  sdpAnswer: string()
24962
25091
  }), _void(), {
24963
25092
  kind: "mutation",
24964
- auth: "admin"
25093
+ auth: "protected"
24965
25094
  }),
24966
25095
  /** Close explicitly. Server also auto-closes on 30s idle. */
24967
25096
  stopSession: method(object({
@@ -24969,7 +25098,7 @@ var intercomCapability = {
24969
25098
  sessionId: string()
24970
25099
  }), _void(), {
24971
25100
  kind: "mutation",
24972
- auth: "admin"
25101
+ auth: "protected"
24973
25102
  }),
24974
25103
  /**
24975
25104
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -24982,7 +25111,7 @@ var intercomCapability = {
24982
25111
  */
24983
25112
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
24984
25113
  kind: "mutation",
24985
- auth: "admin"
25114
+ auth: "protected"
24986
25115
  }),
24987
25116
  /**
24988
25117
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25017,12 +25146,12 @@ var intercomCapability = {
25017
25146
  sequenceNumber: number().int()
25018
25147
  }), object({ accepted: boolean() }), {
25019
25148
  kind: "mutation",
25020
- auth: "admin"
25149
+ auth: "protected"
25021
25150
  }),
25022
25151
  /** Close the raw-PCM talk session. Idempotent. */
25023
25152
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25024
25153
  kind: "mutation",
25025
- auth: "admin"
25154
+ auth: "protected"
25026
25155
  })
25027
25156
  },
25028
25157
  events: { onStatusChanged: { data: object({
@@ -34703,6 +34832,12 @@ Object.freeze({
34703
34832
  addonId: null,
34704
34833
  access: "create"
34705
34834
  },
34835
+ "pipelineAnalytics.countUnstampedEventMedia": {
34836
+ capName: "pipeline-analytics",
34837
+ capScope: "device",
34838
+ addonId: null,
34839
+ access: "view"
34840
+ },
34706
34841
  "pipelineAnalytics.deleteDeviceEvents": {
34707
34842
  capName: "pipeline-analytics",
34708
34843
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-velux",
3
- "version": "0.2.41",
3
+ "version": "0.2.43",
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",