@camstack/addon-provider-tuya 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
@@ -8859,18 +8859,61 @@ var RelocateFootageInputSchema = object({
8859
8859
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8860
8860
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8861
8861
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8862
+ /**
8863
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8864
+ * mover (the engine already walks both collections with a timestamp cursor and
8865
+ * already has a stamp-without-copy path).
8866
+ *
8867
+ * - `move` — the default and the historical behaviour: event-media and
8868
+ * retrain blobs move to `toLocationId` and their rows are
8869
+ * stamped. The enrolled gallery is skipped (D197).
8870
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8871
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8872
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8873
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8874
+ * instant a repoint moves that pointer the row reads from the
8875
+ * new disk while its bytes are on the old one.
8876
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8877
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8878
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8879
+ * never run beside a live second location: it is stop-the-world
8880
+ * by construction, which is acceptable only because the gallery
8881
+ * is a few KB per enrolled sample.
8882
+ */
8883
+ var MediaRelocateModeSchema = _enum([
8884
+ "move",
8885
+ "seal",
8886
+ "gallery"
8887
+ ]);
8862
8888
  var RelocateMediaInputSchema = object({
8863
8889
  toLocationId: string(),
8864
- throttleMbps: number().min(1).max(1e3).optional()
8890
+ throttleMbps: number().min(1).max(1e3).optional(),
8891
+ /** Omitted = `move`, the pre-existing behaviour. */
8892
+ mode: MediaRelocateModeSchema.optional()
8893
+ });
8894
+ /** How many rows still carry NO `locationId` — the population a repoint would
8895
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8896
+ * value that permits a non-blocking `eventMedia` cutover. */
8897
+ var UnstampedEventMediaCountSchema = object({
8898
+ media: number().int().nonnegative(),
8899
+ retrainFrames: number().int().nonnegative(),
8900
+ total: number().int().nonnegative()
8865
8901
  });
8866
8902
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8867
- /** The independently selectable logical storage classes. `recordings`
8868
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8869
- * segments; `eventMedia` is post-analysis blobs. */
8903
+ /** The independently selectable logical storage classes — every class
8904
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8905
+ * Zod enum error where they should meet an explanation.
8906
+ *
8907
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8908
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8909
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8910
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8870
8911
  var StorageMigrationClassSchema = _enum([
8871
8912
  "recordings",
8872
8913
  "recordingsLow",
8873
- "eventMedia"
8914
+ "eventMedia",
8915
+ "backups",
8916
+ "galleryMedia"
8874
8917
  ]);
8875
8918
  /** A destination is always an existing, fully-qualified location id. The
8876
8919
  * migration API intentionally never changes a source location's `basePath`:
@@ -8878,20 +8921,56 @@ var StorageMigrationClassSchema = _enum([
8878
8921
  var StorageMigrationDestinationsSchema = object({
8879
8922
  recordings: string().min(1).optional(),
8880
8923
  recordingsLow: string().min(1).optional(),
8881
- eventMedia: string().min(1).optional()
8924
+ eventMedia: string().min(1).optional(),
8925
+ backups: string().min(1).optional(),
8926
+ galleryMedia: string().min(1).optional()
8882
8927
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8928
+ /**
8929
+ * How a migration sequences the cutover against the byte move.
8930
+ *
8931
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8932
+ * resume. Recording is stopped for the whole move. Right
8933
+ * for a small or a cold class, and the only legal mode for
8934
+ * a `cardinality: 'single'` class.
8935
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8936
+ * refresh, resume, then move the past with everything
8937
+ * running. The pause is three bounded instants (a detach +
8938
+ * attach round, a write-gate drain, a lease) instead of one
8939
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8940
+ * stopped recording under `blocking`; the same move is
8941
+ * seconds of stopped recording under `nonBlocking`.
8942
+ *
8943
+ * The mode is on the JOB, not only on the input, because `status` is where an
8944
+ * operator finds out which one is running.
8945
+ */
8946
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8883
8947
  /** Shared input for planning and starting an orchestrated storage migration. */
8884
8948
  var StorageMigrationInputSchema = object({
8885
8949
  destinations: StorageMigrationDestinationsSchema,
8886
- throttleMbps: number().min(1).max(1e3).optional()
8950
+ throttleMbps: number().min(1).max(1e3).optional(),
8951
+ /** Omitted = `blocking`, which stays the default. */
8952
+ mode: StorageMigrationModeSchema.optional()
8887
8953
  });
8888
- /** The durable coordinator state machine. The only phase that changes default
8889
- * locations is `repointing`, after every selected mover has completed and been
8890
- * verified. */
8954
+ /**
8955
+ * The durable coordinator state machine.
8956
+ *
8957
+ * `blocking`:
8958
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8959
+ *
8960
+ * `nonBlocking`:
8961
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8962
+ *
8963
+ * Same phases, different order plus two new ones — not a second mover.
8964
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8965
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8966
+ * `repointing` is still the only phase that changes a default location.
8967
+ */
8891
8968
  var StorageMigrationPhaseSchema = _enum([
8892
8969
  "planning",
8970
+ "sealing",
8893
8971
  "pausing",
8894
8972
  "moving",
8973
+ "draining",
8895
8974
  "verifying",
8896
8975
  "repointing",
8897
8976
  "refreshing",
@@ -8916,6 +8995,9 @@ var StorageMigrationMoveSchema = object({
8916
8995
  var StorageMigrationJobSchema = object({
8917
8996
  jobId: string(),
8918
8997
  phase: StorageMigrationPhaseSchema,
8998
+ /** Which order this job is running. `status` is the only place an operator
8999
+ * can tell a seconds-long cutover from a thirty-hour one. */
9000
+ mode: StorageMigrationModeSchema,
8919
9001
  destinations: StorageMigrationDestinationsSchema,
8920
9002
  throttleMbps: number(),
8921
9003
  moves: array(StorageMigrationMoveSchema),
@@ -8928,13 +9010,30 @@ var StorageMigrationJobSchema = object({
8928
9010
  finishedAt: number().nullable(),
8929
9011
  error: string().nullable()
8930
9012
  });
9013
+ var StorageMigrationFindingSchema = object({
9014
+ code: _enum([
9015
+ "sharesDeviceWithSource",
9016
+ "deviceIdentityUnknown",
9017
+ "unstampedEventMediaRows",
9018
+ "blockingOnly",
9019
+ "noMover"
9020
+ ]),
9021
+ storageClass: StorageMigrationClassSchema,
9022
+ /** Human-readable, already carrying the ids and counts. */
9023
+ message: string()
9024
+ });
8931
9025
  var StorageMigrationPlanSchema = object({
8932
9026
  destinations: StorageMigrationDestinationsSchema,
9027
+ /** The mode this plan was built for. A plan is only valid for its mode: the
9028
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
9029
+ * it. */
9030
+ mode: StorageMigrationModeSchema,
8933
9031
  moves: array(object({
8934
9032
  storageClass: StorageMigrationClassSchema,
8935
9033
  fromLocationId: string(),
8936
9034
  toLocationId: string()
8937
- }))
9035
+ })),
9036
+ findings: array(StorageMigrationFindingSchema)
8938
9037
  });
8939
9038
  /**
8940
9039
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -19804,7 +19903,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19804
19903
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
19805
19904
  kind: "mutation",
19806
19905
  auth: "admin"
19807
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19906
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19808
19907
  kind: "query",
19809
19908
  auth: "admin"
19810
19909
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -21711,7 +21810,10 @@ method(object({
21711
21810
  }), StorageLocationSchema, {
21712
21811
  kind: "mutation",
21713
21812
  auth: "admin"
21714
- }), method(object({ id: string() }), _void(), {
21813
+ }), method(object({
21814
+ id: string(),
21815
+ force: boolean().optional()
21816
+ }), _void(), {
21715
21817
  kind: "mutation",
21716
21818
  auth: "admin"
21717
21819
  }), method(object({ id: string() }), object({
@@ -25787,6 +25889,33 @@ var intercomCapability = {
25787
25889
  deviceNative: true,
25788
25890
  mode: "singleton",
25789
25891
  deviceTypes: [DeviceType.Camera],
25892
+ /**
25893
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25894
+ *
25895
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25896
+ * cap has no configuration surface at all: all six methods open, feed and
25897
+ * close one live audio session against one `deviceId`. That is the same
25898
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25899
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25900
+ * `admin` because they change what the device IS.
25901
+ *
25902
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25903
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25904
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25905
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25906
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25907
+ * camera 7.
25908
+ *
25909
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25910
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25911
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25912
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25913
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25914
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25915
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25916
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25917
+ * preset promises a cap no row of that preset can reach.
25918
+ */
25790
25919
  methods: {
25791
25920
  /**
25792
25921
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -25799,7 +25928,7 @@ var intercomCapability = {
25799
25928
  sdpOffer: string()
25800
25929
  }), {
25801
25930
  kind: "mutation",
25802
- auth: "admin"
25931
+ auth: "protected"
25803
25932
  }),
25804
25933
  handleAnswer: method(object({
25805
25934
  deviceId: number(),
@@ -25807,7 +25936,7 @@ var intercomCapability = {
25807
25936
  sdpAnswer: string()
25808
25937
  }), _void(), {
25809
25938
  kind: "mutation",
25810
- auth: "admin"
25939
+ auth: "protected"
25811
25940
  }),
25812
25941
  /** Close explicitly. Server also auto-closes on 30s idle. */
25813
25942
  stopSession: method(object({
@@ -25815,7 +25944,7 @@ var intercomCapability = {
25815
25944
  sessionId: string()
25816
25945
  }), _void(), {
25817
25946
  kind: "mutation",
25818
- auth: "admin"
25947
+ auth: "protected"
25819
25948
  }),
25820
25949
  /**
25821
25950
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -25828,7 +25957,7 @@ var intercomCapability = {
25828
25957
  */
25829
25958
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
25830
25959
  kind: "mutation",
25831
- auth: "admin"
25960
+ auth: "protected"
25832
25961
  }),
25833
25962
  /**
25834
25963
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25863,12 +25992,12 @@ var intercomCapability = {
25863
25992
  sequenceNumber: number().int()
25864
25993
  }), object({ accepted: boolean() }), {
25865
25994
  kind: "mutation",
25866
- auth: "admin"
25995
+ auth: "protected"
25867
25996
  }),
25868
25997
  /** Close the raw-PCM talk session. Idempotent. */
25869
25998
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25870
25999
  kind: "mutation",
25871
- auth: "admin"
26000
+ auth: "protected"
25872
26001
  })
25873
26002
  },
25874
26003
  events: { onStatusChanged: { data: object({
@@ -35549,6 +35678,12 @@ Object.freeze({
35549
35678
  addonId: null,
35550
35679
  access: "create"
35551
35680
  },
35681
+ "pipelineAnalytics.countUnstampedEventMedia": {
35682
+ capName: "pipeline-analytics",
35683
+ capScope: "device",
35684
+ addonId: null,
35685
+ access: "view"
35686
+ },
35552
35687
  "pipelineAnalytics.deleteDeviceEvents": {
35553
35688
  capName: "pipeline-analytics",
35554
35689
  capScope: "device",
package/dist/addon.mjs CHANGED
@@ -8858,18 +8858,61 @@ var RelocateFootageInputSchema = object({
8858
8858
  * `RecordingConfig.enabled` or camera wrapper bindings. */
8859
8859
  var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
8860
8860
  var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
8861
+ /**
8862
+ * What a `relocateMedia` pass DOES. One engine, three passes — never a second
8863
+ * mover (the engine already walks both collections with a timestamp cursor and
8864
+ * already has a stamp-without-copy path).
8865
+ *
8866
+ * - `move` — the default and the historical behaviour: event-media and
8867
+ * retrain blobs move to `toLocationId` and their rows are
8868
+ * stamped. The enrolled gallery is skipped (D197).
8869
+ * - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
8870
+ * stamped with `toLocationId`. `toLocationId` here is the id the
8871
+ * bytes ALREADY sit on — today's `eventMedia` default — because
8872
+ * a NULL row means "wherever `eventMedia` points *now*", and the
8873
+ * instant a repoint moves that pointer the row reads from the
8874
+ * new disk while its bytes are on the old one.
8875
+ * - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
8876
+ * (enrolled-gallery) rows, which `move` deliberately skips.
8877
+ * `galleryMedia` is `cardinality: 'single'`, so this pass can
8878
+ * never run beside a live second location: it is stop-the-world
8879
+ * by construction, which is acceptable only because the gallery
8880
+ * is a few KB per enrolled sample.
8881
+ */
8882
+ var MediaRelocateModeSchema = _enum([
8883
+ "move",
8884
+ "seal",
8885
+ "gallery"
8886
+ ]);
8861
8887
  var RelocateMediaInputSchema = object({
8862
8888
  toLocationId: string(),
8863
- throttleMbps: number().min(1).max(1e3).optional()
8889
+ throttleMbps: number().min(1).max(1e3).optional(),
8890
+ /** Omitted = `move`, the pre-existing behaviour. */
8891
+ mode: MediaRelocateModeSchema.optional()
8892
+ });
8893
+ /** How many rows still carry NO `locationId` — the population a repoint would
8894
+ * silently re-aim at a disk that does not hold their bytes. Zero is the only
8895
+ * value that permits a non-blocking `eventMedia` cutover. */
8896
+ var UnstampedEventMediaCountSchema = object({
8897
+ media: number().int().nonnegative(),
8898
+ retrainFrames: number().int().nonnegative(),
8899
+ total: number().int().nonnegative()
8864
8900
  });
8865
8901
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
8866
- /** The independently selectable logical storage classes. `recordings`
8867
- * encompasses the high and mid segment profiles; `recordingsLow` is low
8868
- * segments; `eventMedia` is post-analysis blobs. */
8902
+ /** The independently selectable logical storage classes — every class
8903
+ * `storage.listLocationDeclarations` reports, so an operator never meets a
8904
+ * Zod enum error where they should meet an explanation.
8905
+ *
8906
+ * `recordings` encompasses the high and mid segment profiles; `recordingsLow`
8907
+ * is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
8908
+ * enrolled gallery; `backups` is the system backup archive. The last two have
8909
+ * their own rules — see {@link StorageMigrationFindingCodeSchema}. */
8869
8910
  var StorageMigrationClassSchema = _enum([
8870
8911
  "recordings",
8871
8912
  "recordingsLow",
8872
- "eventMedia"
8913
+ "eventMedia",
8914
+ "backups",
8915
+ "galleryMedia"
8873
8916
  ]);
8874
8917
  /** A destination is always an existing, fully-qualified location id. The
8875
8918
  * migration API intentionally never changes a source location's `basePath`:
@@ -8877,20 +8920,56 @@ var StorageMigrationClassSchema = _enum([
8877
8920
  var StorageMigrationDestinationsSchema = object({
8878
8921
  recordings: string().min(1).optional(),
8879
8922
  recordingsLow: string().min(1).optional(),
8880
- eventMedia: string().min(1).optional()
8923
+ eventMedia: string().min(1).optional(),
8924
+ backups: string().min(1).optional(),
8925
+ galleryMedia: string().min(1).optional()
8881
8926
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8927
+ /**
8928
+ * How a migration sequences the cutover against the byte move.
8929
+ *
8930
+ * - `blocking` — the historical order: pause, move every byte, repoint,
8931
+ * resume. Recording is stopped for the whole move. Right
8932
+ * for a small or a cold class, and the only legal mode for
8933
+ * a `cardinality: 'single'` class.
8934
+ * - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
8935
+ * refresh, resume, then move the past with everything
8936
+ * running. The pause is three bounded instants (a detach +
8937
+ * attach round, a write-gate drain, a lease) instead of one
8938
+ * bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
8939
+ * stopped recording under `blocking`; the same move is
8940
+ * seconds of stopped recording under `nonBlocking`.
8941
+ *
8942
+ * The mode is on the JOB, not only on the input, because `status` is where an
8943
+ * operator finds out which one is running.
8944
+ */
8945
+ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8882
8946
  /** Shared input for planning and starting an orchestrated storage migration. */
8883
8947
  var StorageMigrationInputSchema = object({
8884
8948
  destinations: StorageMigrationDestinationsSchema,
8885
- throttleMbps: number().min(1).max(1e3).optional()
8949
+ throttleMbps: number().min(1).max(1e3).optional(),
8950
+ /** Omitted = `blocking`, which stays the default. */
8951
+ mode: StorageMigrationModeSchema.optional()
8886
8952
  });
8887
- /** The durable coordinator state machine. The only phase that changes default
8888
- * locations is `repointing`, after every selected mover has completed and been
8889
- * verified. */
8953
+ /**
8954
+ * The durable coordinator state machine.
8955
+ *
8956
+ * `blocking`:
8957
+ * planning → pausing → moving → verifying → repointing → refreshing → resuming → done
8958
+ *
8959
+ * `nonBlocking`:
8960
+ * planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
8961
+ *
8962
+ * Same phases, different order plus two new ones — not a second mover.
8963
+ * `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
8964
+ * `draining` runs the same movers UNLEASED, after every writer is back up.
8965
+ * `repointing` is still the only phase that changes a default location.
8966
+ */
8890
8967
  var StorageMigrationPhaseSchema = _enum([
8891
8968
  "planning",
8969
+ "sealing",
8892
8970
  "pausing",
8893
8971
  "moving",
8972
+ "draining",
8894
8973
  "verifying",
8895
8974
  "repointing",
8896
8975
  "refreshing",
@@ -8915,6 +8994,9 @@ var StorageMigrationMoveSchema = object({
8915
8994
  var StorageMigrationJobSchema = object({
8916
8995
  jobId: string(),
8917
8996
  phase: StorageMigrationPhaseSchema,
8997
+ /** Which order this job is running. `status` is the only place an operator
8998
+ * can tell a seconds-long cutover from a thirty-hour one. */
8999
+ mode: StorageMigrationModeSchema,
8918
9000
  destinations: StorageMigrationDestinationsSchema,
8919
9001
  throttleMbps: number(),
8920
9002
  moves: array(StorageMigrationMoveSchema),
@@ -8927,13 +9009,30 @@ var StorageMigrationJobSchema = object({
8927
9009
  finishedAt: number().nullable(),
8928
9010
  error: string().nullable()
8929
9011
  });
9012
+ var StorageMigrationFindingSchema = object({
9013
+ code: _enum([
9014
+ "sharesDeviceWithSource",
9015
+ "deviceIdentityUnknown",
9016
+ "unstampedEventMediaRows",
9017
+ "blockingOnly",
9018
+ "noMover"
9019
+ ]),
9020
+ storageClass: StorageMigrationClassSchema,
9021
+ /** Human-readable, already carrying the ids and counts. */
9022
+ message: string()
9023
+ });
8930
9024
  var StorageMigrationPlanSchema = object({
8931
9025
  destinations: StorageMigrationDestinationsSchema,
9026
+ /** The mode this plan was built for. A plan is only valid for its mode: the
9027
+ * `eventMedia` seal gate and the single-cardinality refusal both depend on
9028
+ * it. */
9029
+ mode: StorageMigrationModeSchema,
8932
9030
  moves: array(object({
8933
9031
  storageClass: StorageMigrationClassSchema,
8934
9032
  fromLocationId: string(),
8935
9033
  toLocationId: string()
8936
- }))
9034
+ })),
9035
+ findings: array(StorageMigrationFindingSchema)
8937
9036
  });
8938
9037
  /**
8939
9038
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -19803,7 +19902,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19803
19902
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
19804
19903
  kind: "mutation",
19805
19904
  auth: "admin"
19806
- }), method(object({}), array(RelocateJobSchema).readonly(), {
19905
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19807
19906
  kind: "query",
19808
19907
  auth: "admin"
19809
19908
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -21710,7 +21809,10 @@ method(object({
21710
21809
  }), StorageLocationSchema, {
21711
21810
  kind: "mutation",
21712
21811
  auth: "admin"
21713
- }), method(object({ id: string() }), _void(), {
21812
+ }), method(object({
21813
+ id: string(),
21814
+ force: boolean().optional()
21815
+ }), _void(), {
21714
21816
  kind: "mutation",
21715
21817
  auth: "admin"
21716
21818
  }), method(object({ id: string() }), object({
@@ -25786,6 +25888,33 @@ var intercomCapability = {
25786
25888
  deviceNative: true,
25787
25889
  mode: "singleton",
25788
25890
  deviceTypes: [DeviceType.Camera],
25891
+ /**
25892
+ * **Auth tier: `protected` on every method — deliberate, and load-bearing.**
25893
+ *
25894
+ * Talking through a camera is an OPERATE action, not a CONFIGURE one. This
25895
+ * cap has no configuration surface at all: all six methods open, feed and
25896
+ * close one live audio session against one `deviceId`. That is the same
25897
+ * authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
25898
+ * the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
25899
+ * `admin` because they change what the device IS.
25900
+ *
25901
+ * `protected` does not mean ungated: `protectedProcedure` runs the
25902
+ * `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
25903
+ * with `access: 'create'` and a `deviceId` in its input. So a caller needs a
25904
+ * grant that covers THAT camera at `create` — a `camera-viewer` (`view`
25905
+ * only) still cannot talk, and a grant on camera 5 cannot talk through
25906
+ * camera 7.
25907
+ *
25908
+ * Every method was `auth: 'admin'` from the initial commit, which made the
25909
+ * cap unreachable by every non-admin principal — `adminProcedure` throws
25910
+ * `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
25911
+ * machinery generated for this cap (`METHOD_ACCESS_MAP`,
25912
+ * `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
25913
+ * `camera-operator` scope preset has promised "PTZ control, intercom,
25914
+ * snapshots" since that same commit; the promise could not be kept. Recorded
25915
+ * as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
25916
+ * preset promises a cap no row of that preset can reach.
25917
+ */
25789
25918
  methods: {
25790
25919
  /**
25791
25920
  * Open a server-side WebRTC audio-only session. Returns an SDP
@@ -25798,7 +25927,7 @@ var intercomCapability = {
25798
25927
  sdpOffer: string()
25799
25928
  }), {
25800
25929
  kind: "mutation",
25801
- auth: "admin"
25930
+ auth: "protected"
25802
25931
  }),
25803
25932
  handleAnswer: method(object({
25804
25933
  deviceId: number(),
@@ -25806,7 +25935,7 @@ var intercomCapability = {
25806
25935
  sdpAnswer: string()
25807
25936
  }), _void(), {
25808
25937
  kind: "mutation",
25809
- auth: "admin"
25938
+ auth: "protected"
25810
25939
  }),
25811
25940
  /** Close explicitly. Server also auto-closes on 30s idle. */
25812
25941
  stopSession: method(object({
@@ -25814,7 +25943,7 @@ var intercomCapability = {
25814
25943
  sessionId: string()
25815
25944
  }), _void(), {
25816
25945
  kind: "mutation",
25817
- auth: "admin"
25946
+ auth: "protected"
25818
25947
  }),
25819
25948
  /**
25820
25949
  * Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
@@ -25827,7 +25956,7 @@ var intercomCapability = {
25827
25956
  */
25828
25957
  startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
25829
25958
  kind: "mutation",
25830
- auth: "admin"
25959
+ auth: "protected"
25831
25960
  }),
25832
25961
  /**
25833
25962
  * Push one chunk of talk-back audio onto the active talk session.
@@ -25862,12 +25991,12 @@ var intercomCapability = {
25862
25991
  sequenceNumber: number().int()
25863
25992
  }), object({ accepted: boolean() }), {
25864
25993
  kind: "mutation",
25865
- auth: "admin"
25994
+ auth: "protected"
25866
25995
  }),
25867
25996
  /** Close the raw-PCM talk session. Idempotent. */
25868
25997
  endTalkSession: method(object({ deviceId: number() }), _void(), {
25869
25998
  kind: "mutation",
25870
- auth: "admin"
25999
+ auth: "protected"
25871
26000
  })
25872
26001
  },
25873
26002
  events: { onStatusChanged: { data: object({
@@ -35548,6 +35677,12 @@ Object.freeze({
35548
35677
  addonId: null,
35549
35678
  access: "create"
35550
35679
  },
35680
+ "pipelineAnalytics.countUnstampedEventMedia": {
35681
+ capName: "pipeline-analytics",
35682
+ capScope: "device",
35683
+ addonId: null,
35684
+ access: "view"
35685
+ },
35551
35686
  "pipelineAnalytics.deleteDeviceEvents": {
35552
35687
  capName: "pipeline-analytics",
35553
35688
  capScope: "device",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-tuya",
3
- "version": "0.2.41",
3
+ "version": "0.2.43",
4
4
  "description": "Tuya / Smart Life device-provider addon for CamStack — account-onboarded (Tuya IoT cloud fetch of device localKeys) + LOCAL DP control via the @apocaliss92/nodetuya encrypted-LAN client, exposing switch / water-heater-family kettle entities",
5
5
  "keywords": [
6
6
  "camstack",