@camstack/addon-provider-dreo 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
@@ -8075,18 +8075,61 @@ var RelocateFootageInputSchema = object({
8075
8075
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8076
8076
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8077
8077
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8078
+ /**
8079
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8080
+ * mover (the engine already walks both collections with a timestamp cursor and
8081
+ * already has a stamp-without-copy path).
8082
+ *
8083
+ * - `move` — the default and the historical behaviour: event-media and
8084
+ * retrain blobs move to `toLocationId` and their rows are
8085
+ * stamped. The enrolled gallery is skipped (D197).
8086
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8087
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8088
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8089
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8090
+ * instant a repoint moves that pointer the row reads from the
8091
+ * new disk while its bytes are on the old one.
8092
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8093
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8094
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8095
+ * never run beside a live second location: it is stop-the-world
8096
+ * by construction, which is acceptable only because the gallery
8097
+ * is a few KB per enrolled sample.
8098
+ */
8099
+ var MediaRelocateModeSchema = _enum([
8100
+ "move",
8101
+ "seal",
8102
+ "gallery"
8103
+ ]);
8078
8104
  var RelocateMediaInputSchema = object({
8079
8105
  toLocationId: string(),
8080
- throttleMbps: number().min(1).max(1e3).optional()
8106
+ throttleMbps: number().min(1).max(1e3).optional(),
8107
+ /** Omitted = `move`, the pre-existing behaviour. */
8108
+ mode: MediaRelocateModeSchema.optional()
8109
+ });
8110
+ /** How many rows still carry NO `locationId` — the population a repoint would
8111
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8112
+ * value that permits a non-blocking `eventMedia` cutover. */
8113
+ var UnstampedEventMediaCountSchema = object({
8114
+ media: number().int().nonnegative(),
8115
+ retrainFrames: number().int().nonnegative(),
8116
+ total: number().int().nonnegative()
8081
8117
  });
8082
8118
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8083
- /** The independently selectable logical storage classes. `recordings`
8084
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8085
- * segments; `eventMedia` is post-analysis blobs. */
8119
+ /** The independently selectable logical storage classes — every class
8120
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8121
+ * Zod enum error where they should meet an explanation.
8122
+ *
8123
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8124
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8125
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8126
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8086
8127
  var StorageMigrationClassSchema = _enum([
8087
8128
  "recordings",
8088
8129
  "recordingsLow",
8089
- "eventMedia"
8130
+ "eventMedia",
8131
+ "backups",
8132
+ "galleryMedia"
8090
8133
  ]);
8091
8134
  /** A destination is always an existing, fully-qualified location id. The
8092
8135
  * migration API intentionally never changes a source location's `basePath`:
@@ -8094,20 +8137,56 @@ var StorageMigrationClassSchema = _enum([
8094
8137
  var StorageMigrationDestinationsSchema = object({
8095
8138
  recordings: string().min(1).optional(),
8096
8139
  recordingsLow: string().min(1).optional(),
8097
- eventMedia: string().min(1).optional()
8140
+ eventMedia: string().min(1).optional(),
8141
+ backups: string().min(1).optional(),
8142
+ galleryMedia: string().min(1).optional()
8098
8143
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8144
+ /**
8145
+ * How a migration sequences the cutover against the byte move.
8146
+ *
8147
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8148
+ * resume. Recording is stopped for the whole move. Right
8149
+ * for a small or a cold class, and the only legal mode for
8150
+ * a `cardinality: 'single'` class.
8151
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8152
+ * refresh, resume, then move the past with everything
8153
+ * running. The pause is three bounded instants (a detach +
8154
+ * attach round, a write-gate drain, a lease) instead of one
8155
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8156
+ * stopped recording under `blocking`; the same move is
8157
+ * seconds of stopped recording under `nonBlocking`.
8158
+ *
8159
+ * The mode is on the JOB, not only on the input, because `status` is where an
8160
+ * operator finds out which one is running.
8161
+ */
8162
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8099
8163
  /** Shared input for planning and starting an orchestrated storage migration. */
8100
8164
  var StorageMigrationInputSchema = object({
8101
8165
  destinations: StorageMigrationDestinationsSchema,
8102
- throttleMbps: number().min(1).max(1e3).optional()
8166
+ throttleMbps: number().min(1).max(1e3).optional(),
8167
+ /** Omitted = `blocking`, which stays the default. */
8168
+ mode: StorageMigrationModeSchema.optional()
8103
8169
  });
8104
- /** The durable coordinator state machine. The only phase that changes default
8105
- * locations is `repointing`, after every selected mover has completed and been
8106
- * verified. */
8170
+ /**
8171
+ * The durable coordinator state machine.
8172
+ *
8173
+ * `blocking`:
8174
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8175
+ *
8176
+ * `nonBlocking`:
8177
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8178
+ *
8179
+ * Same phases, different order plus two new ones — not a second mover.
8180
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8181
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8182
+ * `repointing` is still the only phase that changes a default location.
8183
+ */
8107
8184
  var StorageMigrationPhaseSchema = _enum([
8108
8185
  "planning",
8186
+ "sealing",
8109
8187
  "pausing",
8110
8188
  "moving",
8189
+ "draining",
8111
8190
  "verifying",
8112
8191
  "repointing",
8113
8192
  "refreshing",
@@ -8132,6 +8211,9 @@ var StorageMigrationMoveSchema = object({
8132
8211
  var StorageMigrationJobSchema = object({
8133
8212
  jobId: string(),
8134
8213
  phase: StorageMigrationPhaseSchema,
8214
+ /** Which order this job is running. `status` is the only place an operator
8215
+ * can tell a seconds-long cutover from a thirty-hour one. */
8216
+ mode: StorageMigrationModeSchema,
8135
8217
  destinations: StorageMigrationDestinationsSchema,
8136
8218
  throttleMbps: number(),
8137
8219
  moves: array(StorageMigrationMoveSchema),
@@ -8144,13 +8226,30 @@ var StorageMigrationJobSchema = object({
8144
8226
  finishedAt: number().nullable(),
8145
8227
  error: string().nullable()
8146
8228
  });
8229
+ var StorageMigrationFindingSchema = object({
8230
+ code: _enum([
8231
+ "sharesDeviceWithSource",
8232
+ "deviceIdentityUnknown",
8233
+ "unstampedEventMediaRows",
8234
+ "blockingOnly",
8235
+ "noMover"
8236
+ ]),
8237
+ storageClass: StorageMigrationClassSchema,
8238
+ /** Human-readable, already carrying the ids and counts. */
8239
+ message: string()
8240
+ });
8147
8241
  var StorageMigrationPlanSchema = object({
8148
8242
  destinations: StorageMigrationDestinationsSchema,
8243
+ /** The mode this plan was built for. A plan is only valid for its mode: the
8244
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
8245
+ * it. */
8246
+ mode: StorageMigrationModeSchema,
8149
8247
  moves: array(object({
8150
8248
  storageClass: StorageMigrationClassSchema,
8151
8249
  fromLocationId: string(),
8152
8250
  toLocationId: string()
8153
- }))
8251
+ })),
8252
+ findings: array(StorageMigrationFindingSchema)
8154
8253
  });
8155
8254
  /**
8156
8255
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -19020,7 +19119,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19020
19119
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
19021
19120
  kind: "mutation",
19022
19121
  auth: "admin"
19023
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19122
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19024
19123
  kind: "query",
19025
19124
  auth: "admin"
19026
19125
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20927,7 +21026,10 @@ method(object({
20927
21026
  }), StorageLocationSchema, {
20928
21027
  kind: "mutation",
20929
21028
  auth: "admin"
20930
- }), method(object({ id: string() }), _void(), {
21029
+ }), method(object({
21030
+ id: string(),
21031
+ force: boolean().optional()
21032
+ }), _void(), {
20931
21033
  kind: "mutation",
20932
21034
  auth: "admin"
20933
21035
  }), method(object({ id: string() }), object({
@@ -25003,6 +25105,33 @@ var intercomCapability = {
25003
25105
  deviceNative: true,
25004
25106
  mode: "singleton",
25005
25107
  deviceTypes: [DeviceType.Camera],
25108
+ /**
25109
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25110
+ *
25111
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25112
+ * cap has no configuration surface at all: all six methods open, feed and
25113
+ * close one live audio session against one `deviceId`. That is the same
25114
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25115
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25116
+ * `admin` because they change what the device IS.
25117
+ *
25118
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25119
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25120
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25121
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25122
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25123
+ * camera 7.
25124
+ *
25125
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25126
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25127
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25128
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25129
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25130
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25131
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25132
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25133
+ * preset promises a cap no row of that preset can reach.
25134
+ */
25006
25135
  methods: {
25007
25136
  /**
25008
25137
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -25015,7 +25144,7 @@ var intercomCapability = {
25015
25144
  sdpOffer: string()
25016
25145
  }), {
25017
25146
  kind: "mutation",
25018
- auth: "admin"
25147
+ auth: "protected"
25019
25148
  }),
25020
25149
  handleAnswer: method(object({
25021
25150
  deviceId: number(),
@@ -25023,7 +25152,7 @@ var intercomCapability = {
25023
25152
  sdpAnswer: string()
25024
25153
  }), _void(), {
25025
25154
  kind: "mutation",
25026
- auth: "admin"
25155
+ auth: "protected"
25027
25156
  }),
25028
25157
  /** Close explicitly. Server also auto-closes on 30s idle. */
25029
25158
  stopSession: method(object({
@@ -25031,7 +25160,7 @@ var intercomCapability = {
25031
25160
  sessionId: string()
25032
25161
  }), _void(), {
25033
25162
  kind: "mutation",
25034
- auth: "admin"
25163
+ auth: "protected"
25035
25164
  }),
25036
25165
  /**
25037
25166
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -25044,7 +25173,7 @@ var intercomCapability = {
25044
25173
  */
25045
25174
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
25046
25175
  kind: "mutation",
25047
- auth: "admin"
25176
+ auth: "protected"
25048
25177
  }),
25049
25178
  /**
25050
25179
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25079,12 +25208,12 @@ var intercomCapability = {
25079
25208
  sequenceNumber: number().int()
25080
25209
  }), object({ accepted: boolean() }), {
25081
25210
  kind: "mutation",
25082
- auth: "admin"
25211
+ auth: "protected"
25083
25212
  }),
25084
25213
  /** Close the raw-PCM talk session. Idempotent. */
25085
25214
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25086
25215
  kind: "mutation",
25087
- auth: "admin"
25216
+ auth: "protected"
25088
25217
  })
25089
25218
  },
25090
25219
  events: { onStatusChanged: { data: object({
@@ -34765,6 +34894,12 @@ Object.freeze({
34765
34894
  addonId: null,
34766
34895
  access: "create"
34767
34896
  },
34897
+ "pipelineAnalytics.countUnstampedEventMedia": {
34898
+ capName: "pipeline-analytics",
34899
+ capScope: "device",
34900
+ addonId: null,
34901
+ access: "view"
34902
+ },
34768
34903
  "pipelineAnalytics.deleteDeviceEvents": {
34769
34904
  capName: "pipeline-analytics",
34770
34905
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -8076,18 +8076,61 @@ var RelocateFootageInputSchema = object({
8076
8076
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8077
8077
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8078
8078
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8079
+ /**
8080
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8081
+ * mover (the engine already walks both collections with a timestamp cursor and
8082
+ * already has a stamp-without-copy path).
8083
+ *
8084
+ * - `move` — the default and the historical behaviour: event-media and
8085
+ * retrain blobs move to `toLocationId` and their rows are
8086
+ * stamped. The enrolled gallery is skipped (D197).
8087
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8088
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8089
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8090
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8091
+ * instant a repoint moves that pointer the row reads from the
8092
+ * new disk while its bytes are on the old one.
8093
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8094
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8095
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8096
+ * never run beside a live second location: it is stop-the-world
8097
+ * by construction, which is acceptable only because the gallery
8098
+ * is a few KB per enrolled sample.
8099
+ */
8100
+ var MediaRelocateModeSchema = _enum([
8101
+ "move",
8102
+ "seal",
8103
+ "gallery"
8104
+ ]);
8079
8105
  var RelocateMediaInputSchema = object({
8080
8106
  toLocationId: string(),
8081
- throttleMbps: number().min(1).max(1e3).optional()
8107
+ throttleMbps: number().min(1).max(1e3).optional(),
8108
+ /** Omitted = `move`, the pre-existing behaviour. */
8109
+ mode: MediaRelocateModeSchema.optional()
8110
+ });
8111
+ /** How many rows still carry NO `locationId` — the population a repoint would
8112
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8113
+ * value that permits a non-blocking `eventMedia` cutover. */
8114
+ var UnstampedEventMediaCountSchema = object({
8115
+ media: number().int().nonnegative(),
8116
+ retrainFrames: number().int().nonnegative(),
8117
+ total: number().int().nonnegative()
8082
8118
  });
8083
8119
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8084
- /** The independently selectable logical storage classes. `recordings`
8085
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8086
- * segments; `eventMedia` is post-analysis blobs. */
8120
+ /** The independently selectable logical storage classes — every class
8121
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8122
+ * Zod enum error where they should meet an explanation.
8123
+ *
8124
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8125
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8126
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8127
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8087
8128
  var StorageMigrationClassSchema = _enum([
8088
8129
  "recordings",
8089
8130
  "recordingsLow",
8090
- "eventMedia"
8131
+ "eventMedia",
8132
+ "backups",
8133
+ "galleryMedia"
8091
8134
  ]);
8092
8135
  /** A destination is always an existing, fully-qualified location id. The
8093
8136
  * migration API intentionally never changes a source location's `basePath`:
@@ -8095,20 +8138,56 @@ var StorageMigrationClassSchema = _enum([
8095
8138
  var StorageMigrationDestinationsSchema = object({
8096
8139
  recordings: string().min(1).optional(),
8097
8140
  recordingsLow: string().min(1).optional(),
8098
- eventMedia: string().min(1).optional()
8141
+ eventMedia: string().min(1).optional(),
8142
+ backups: string().min(1).optional(),
8143
+ galleryMedia: string().min(1).optional()
8099
8144
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8145
+ /**
8146
+ * How a migration sequences the cutover against the byte move.
8147
+ *
8148
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8149
+ * resume. Recording is stopped for the whole move. Right
8150
+ * for a small or a cold class, and the only legal mode for
8151
+ * a `cardinality: 'single'` class.
8152
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8153
+ * refresh, resume, then move the past with everything
8154
+ * running. The pause is three bounded instants (a detach +
8155
+ * attach round, a write-gate drain, a lease) instead of one
8156
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8157
+ * stopped recording under `blocking`; the same move is
8158
+ * seconds of stopped recording under `nonBlocking`.
8159
+ *
8160
+ * The mode is on the JOB, not only on the input, because `status` is where an
8161
+ * operator finds out which one is running.
8162
+ */
8163
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8100
8164
  /** Shared input for planning and starting an orchestrated storage migration. */
8101
8165
  var StorageMigrationInputSchema = object({
8102
8166
  destinations: StorageMigrationDestinationsSchema,
8103
- throttleMbps: number().min(1).max(1e3).optional()
8167
+ throttleMbps: number().min(1).max(1e3).optional(),
8168
+ /** Omitted = `blocking`, which stays the default. */
8169
+ mode: StorageMigrationModeSchema.optional()
8104
8170
  });
8105
- /** The durable coordinator state machine. The only phase that changes default
8106
- * locations is `repointing`, after every selected mover has completed and been
8107
- * verified. */
8171
+ /**
8172
+ * The durable coordinator state machine.
8173
+ *
8174
+ * `blocking`:
8175
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8176
+ *
8177
+ * `nonBlocking`:
8178
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8179
+ *
8180
+ * Same phases, different order plus two new ones — not a second mover.
8181
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8182
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8183
+ * `repointing` is still the only phase that changes a default location.
8184
+ */
8108
8185
  var StorageMigrationPhaseSchema = _enum([
8109
8186
  "planning",
8187
+ "sealing",
8110
8188
  "pausing",
8111
8189
  "moving",
8190
+ "draining",
8112
8191
  "verifying",
8113
8192
  "repointing",
8114
8193
  "refreshing",
@@ -8133,6 +8212,9 @@ var StorageMigrationMoveSchema = object({
8133
8212
  var StorageMigrationJobSchema = object({
8134
8213
  jobId: string(),
8135
8214
  phase: StorageMigrationPhaseSchema,
8215
+ /** Which order this job is running. `status` is the only place an operator
8216
+ * can tell a seconds-long cutover from a thirty-hour one. */
8217
+ mode: StorageMigrationModeSchema,
8136
8218
  destinations: StorageMigrationDestinationsSchema,
8137
8219
  throttleMbps: number(),
8138
8220
  moves: array(StorageMigrationMoveSchema),
@@ -8145,13 +8227,30 @@ var StorageMigrationJobSchema = object({
8145
8227
  finishedAt: number().nullable(),
8146
8228
  error: string().nullable()
8147
8229
  });
8230
+ var StorageMigrationFindingSchema = object({
8231
+ code: _enum([
8232
+ "sharesDeviceWithSource",
8233
+ "deviceIdentityUnknown",
8234
+ "unstampedEventMediaRows",
8235
+ "blockingOnly",
8236
+ "noMover"
8237
+ ]),
8238
+ storageClass: StorageMigrationClassSchema,
8239
+ /** Human-readable, already carrying the ids and counts. */
8240
+ message: string()
8241
+ });
8148
8242
  var StorageMigrationPlanSchema = object({
8149
8243
  destinations: StorageMigrationDestinationsSchema,
8244
+ /** The mode this plan was built for. A plan is only valid for its mode: the
8245
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
8246
+ * it. */
8247
+ mode: StorageMigrationModeSchema,
8150
8248
  moves: array(object({
8151
8249
  storageClass: StorageMigrationClassSchema,
8152
8250
  fromLocationId: string(),
8153
8251
  toLocationId: string()
8154
- }))
8252
+ })),
8253
+ findings: array(StorageMigrationFindingSchema)
8155
8254
  });
8156
8255
  /**
8157
8256
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -19021,7 +19120,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19021
19120
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
19022
19121
  kind: "mutation",
19023
19122
  auth: "admin"
19024
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19123
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19025
19124
  kind: "query",
19026
19125
  auth: "admin"
19027
19126
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20928,7 +21027,10 @@ method(object({
20928
21027
  }), StorageLocationSchema, {
20929
21028
  kind: "mutation",
20930
21029
  auth: "admin"
20931
- }), method(object({ id: string() }), _void(), {
21030
+ }), method(object({
21031
+ id: string(),
21032
+ force: boolean().optional()
21033
+ }), _void(), {
20932
21034
  kind: "mutation",
20933
21035
  auth: "admin"
20934
21036
  }), method(object({ id: string() }), object({
@@ -25004,6 +25106,33 @@ var intercomCapability = {
25004
25106
  deviceNative: true,
25005
25107
  mode: "singleton",
25006
25108
  deviceTypes: [DeviceType.Camera],
25109
+ /**
25110
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25111
+ *
25112
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25113
+ * cap has no configuration surface at all: all six methods open, feed and
25114
+ * close one live audio session against one `deviceId`. That is the same
25115
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25116
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25117
+ * `admin` because they change what the device IS.
25118
+ *
25119
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25120
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25121
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25122
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25123
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25124
+ * camera 7.
25125
+ *
25126
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25127
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25128
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25129
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25130
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25131
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25132
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25133
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25134
+ * preset promises a cap no row of that preset can reach.
25135
+ */
25007
25136
  methods: {
25008
25137
  /**
25009
25138
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -25016,7 +25145,7 @@ var intercomCapability = {
25016
25145
  sdpOffer: string()
25017
25146
  }), {
25018
25147
  kind: "mutation",
25019
- auth: "admin"
25148
+ auth: "protected"
25020
25149
  }),
25021
25150
  handleAnswer: method(object({
25022
25151
  deviceId: number(),
@@ -25024,7 +25153,7 @@ var intercomCapability = {
25024
25153
  sdpAnswer: string()
25025
25154
  }), _void(), {
25026
25155
  kind: "mutation",
25027
- auth: "admin"
25156
+ auth: "protected"
25028
25157
  }),
25029
25158
  /** Close explicitly. Server also auto-closes on 30s idle. */
25030
25159
  stopSession: method(object({
@@ -25032,7 +25161,7 @@ var intercomCapability = {
25032
25161
  sessionId: string()
25033
25162
  }), _void(), {
25034
25163
  kind: "mutation",
25035
- auth: "admin"
25164
+ auth: "protected"
25036
25165
  }),
25037
25166
  /**
25038
25167
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -25045,7 +25174,7 @@ var intercomCapability = {
25045
25174
  */
25046
25175
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
25047
25176
  kind: "mutation",
25048
- auth: "admin"
25177
+ auth: "protected"
25049
25178
  }),
25050
25179
  /**
25051
25180
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25080,12 +25209,12 @@ var intercomCapability = {
25080
25209
  sequenceNumber: number().int()
25081
25210
  }), object({ accepted: boolean() }), {
25082
25211
  kind: "mutation",
25083
- auth: "admin"
25212
+ auth: "protected"
25084
25213
  }),
25085
25214
  /** Close the raw-PCM talk session. Idempotent. */
25086
25215
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25087
25216
  kind: "mutation",
25088
- auth: "admin"
25217
+ auth: "protected"
25089
25218
  })
25090
25219
  },
25091
25220
  events: { onStatusChanged: { data: object({
@@ -34766,6 +34895,12 @@ Object.freeze({
34766
34895
  addonId: null,
34767
34896
  access: "create"
34768
34897
  },
34898
+ "pipelineAnalytics.countUnstampedEventMedia": {
34899
+ capName: "pipeline-analytics",
34900
+ capScope: "device",
34901
+ addonId: null,
34902
+ access: "view"
34903
+ },
34769
34904
  "pipelineAnalytics.deleteDeviceEvents": {
34770
34905
  capName: "pipeline-analytics",
34771
34906
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreo",
3
- "version": "0.2.41",
3
+ "version": "0.2.43",
4
4
  "description": "Dreo smart-device (fan / air-circulator / purifier / heater / humidifier) device-provider addon for CamStack — wraps the @apocaliss92/nodedreo Dreo cloud client (REST + WebSocket)",
5
5
  "keywords": [
6
6
  "camstack",