@camstack/addon-provider-tuya 0.2.48 → 0.2.50

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 +478 -73
  2. package/dist/addon.mjs +478 -73
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -8838,6 +8838,20 @@ var RelocateJobSchema = object({
8838
8838
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8839
8839
  */
8840
8840
  rowsReconciled: number().int().nonnegative().optional(),
8841
+ /**
8842
+ * Rows this run FORGOT because the file they name is not on disk.
8843
+ *
8844
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8845
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8846
+ * and the durable row is dropped through the same channel eviction uses. It
8847
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8848
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8849
+ * the same failure as one that quietly skips them (D295).
8850
+ *
8851
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8852
+ * ledger claimed 5.65 GB of footage that no longer existed.
8853
+ */
8854
+ rowsForgotten: number().int().nonnegative().optional(),
8841
8855
  startedAt: number(),
8842
8856
  finishedAt: number().nullable(),
8843
8857
  error: string().nullable()
@@ -8902,6 +8916,13 @@ var MediaRelocateModeSchema = _enum([
8902
8916
  ]);
8903
8917
  var RelocateMediaInputSchema = object({
8904
8918
  toLocationId: string(),
8919
+ /**
8920
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8921
+ * every row that is not already on `toLocationId` (the historical
8922
+ * behaviour). A named source is what a from→to migration needs: without it
8923
+ * "move events off disk 2" also emptied disk 1.
8924
+ */
8925
+ fromLocationId: string().optional(),
8905
8926
  throttleMbps: number().min(1).max(1e3).optional(),
8906
8927
  /** Omitted = `move`, the pre-existing behaviour. */
8907
8928
  mode: MediaRelocateModeSchema.optional()
@@ -8969,6 +8990,19 @@ var StorageMigrationDestinationsSchema = object({
8969
8990
  galleryMedia: string().min(1).optional()
8970
8991
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8971
8992
  /**
8993
+ * Optional named source per class. Omitted = the class's current default
8994
+ * (the historical behaviour). A named source that is NOT the default is a
8995
+ * drain of that disk: bytes move, the default stays, and the source is
8996
+ * disabled when the move finishes.
8997
+ */
8998
+ var StorageMigrationSourcesSchema = object({
8999
+ recordings: string().min(1).optional(),
9000
+ recordingsLow: string().min(1).optional(),
9001
+ eventMedia: string().min(1).optional(),
9002
+ backups: string().min(1).optional(),
9003
+ galleryMedia: string().min(1).optional()
9004
+ }).optional();
9005
+ /**
8972
9006
  * How a migration sequences the cutover against the byte move.
8973
9007
  *
8974
9008
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8990,6 +9024,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8990
9024
  /** Shared input for planning and starting an orchestrated storage migration. */
8991
9025
  var StorageMigrationInputSchema = object({
8992
9026
  destinations: StorageMigrationDestinationsSchema,
9027
+ /** Omitted = each class's current default. */
9028
+ sources: StorageMigrationSourcesSchema,
8993
9029
  throttleMbps: number().min(1).max(1e3).optional(),
8994
9030
  /** Omitted = `blocking`, which stays the default. */
8995
9031
  mode: StorageMigrationModeSchema.optional()
@@ -9069,6 +9105,13 @@ var StorageMigrationMoveSchema = object({
9069
9105
  storageClass: StorageMigrationClassSchema,
9070
9106
  fromLocationId: string(),
9071
9107
  toLocationId: string(),
9108
+ /**
9109
+ * True when `from` was NOT the class default at plan time. The move still
9110
+ * copies bytes, but the default is left alone and the source is disabled
9111
+ * once the copy verifies. Absent on jobs planned before this field existed
9112
+ * — those jobs always repointed, which is `false`.
9113
+ */
9114
+ freezeSource: boolean().optional(),
9072
9115
  moverJobId: string().nullable(),
9073
9116
  state: RelocateJobStateSchema.nullable(),
9074
9117
  error: string().nullable(),
@@ -9082,6 +9125,7 @@ var StorageMigrationJobSchema = object({
9082
9125
  * can tell a seconds-long cutover from a thirty-hour one. */
9083
9126
  mode: StorageMigrationModeSchema,
9084
9127
  destinations: StorageMigrationDestinationsSchema,
9128
+ sources: StorageMigrationSourcesSchema,
9085
9129
  throttleMbps: number(),
9086
9130
  moves: array(StorageMigrationMoveSchema),
9087
9131
  pauseLeaseId: string().nullable(),
@@ -9107,6 +9151,7 @@ var StorageMigrationFindingSchema = object({
9107
9151
  });
9108
9152
  var StorageMigrationPlanSchema = object({
9109
9153
  destinations: StorageMigrationDestinationsSchema,
9154
+ sources: StorageMigrationSourcesSchema,
9110
9155
  /** The mode this plan was built for. A plan is only valid for its mode: the
9111
9156
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
9112
9157
  * it. */
@@ -9114,7 +9159,8 @@ var StorageMigrationPlanSchema = object({
9114
9159
  moves: array(object({
9115
9160
  storageClass: StorageMigrationClassSchema,
9116
9161
  fromLocationId: string(),
9117
- toLocationId: string()
9162
+ toLocationId: string(),
9163
+ freezeSource: boolean().optional()
9118
9164
  })),
9119
9165
  findings: array(StorageMigrationFindingSchema)
9120
9166
  });
@@ -9201,16 +9247,142 @@ var RelocateResidueSchema = object({
9201
9247
  segments: number().int().nonnegative(),
9202
9248
  bytes: number().int().nonnegative()
9203
9249
  }).nullable();
9250
+ /**
9251
+ * Ask one location whether its durable hour rows describe the disk — the walk
9252
+ * (D319).
9253
+ *
9254
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
9255
+ * missing tool is the question, and the dry run is how they sanity-check the
9256
+ * destructive run before authorising it.
9257
+ */
9258
+ var LedgerWalkInputSchema = object({
9259
+ locationId: string().min(1),
9260
+ /** Forget the confirmed-absent rows, rather than only counting them. */
9261
+ apply: boolean().optional(),
9262
+ /** Narrow to one camera. */
9263
+ deviceId: number().int().positive().optional(),
9264
+ /** Narrow to these recording profiles; empty/absent = every profile. */
9265
+ profiles: array(string().min(1)).optional()
9266
+ });
9267
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
9268
+ var LedgerWalkRefusalSchema = _enum([
9269
+ "location-unknown",
9270
+ "source-writable",
9271
+ "no-ledger",
9272
+ "archive-unreadable",
9273
+ "anchor-absent",
9274
+ "anchor-unreadable",
9275
+ "anchor-moved"
9276
+ ]);
9277
+ _enum([
9278
+ "live-tail",
9279
+ "listing-error",
9280
+ "path-mismatch",
9281
+ "durable-refused"
9282
+ ]);
9283
+ /** Every skip reason, always present, always a number — so a reason that never
9284
+ * fired reports as zero rather than absent and the report shape is constant
9285
+ * between passes. Spelled out rather than `z.record` for exactly that. */
9286
+ var LedgerWalkSkipCountsSchema = object({
9287
+ "live-tail": number().int().nonnegative(),
9288
+ "listing-error": number().int().nonnegative(),
9289
+ "path-mismatch": number().int().nonnegative(),
9290
+ "durable-refused": number().int().nonnegative()
9291
+ });
9292
+ /** One camera's share of a walk, so a report names cameras and not rows. */
9293
+ var LedgerWalkDeviceReportSchema = object({
9294
+ deviceId: number().int(),
9295
+ hoursWalked: number().int().nonnegative(),
9296
+ hoursMissing: number().int().nonnegative(),
9297
+ ghostSegments: number().int().nonnegative(),
9298
+ ghostBytes: number().int().nonnegative(),
9299
+ forgottenSegments: number().int().nonnegative(),
9300
+ orphanFiles: number().int().nonnegative()
9301
+ });
9302
+ /**
9303
+ * What one walk claimed, listed, found and (only when armed) forgot.
9304
+ *
9305
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
9306
+ * walk that saw a fraction of the location is visible in its own report rather
9307
+ * than in the absence of one.
9308
+ */
9309
+ var LedgerWalkReportSchema = object({
9310
+ locationId: string(),
9311
+ applied: boolean(),
9312
+ refused: LedgerWalkRefusalSchema.nullable(),
9313
+ archiveSegments: number().int().nonnegative().nullable(),
9314
+ archiveBytes: number().int().nonnegative().nullable(),
9315
+ hoursClaimed: number().int().nonnegative(),
9316
+ hoursWalked: number().int().nonnegative(),
9317
+ hoursMissing: number().int().nonnegative(),
9318
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
9319
+ listings: number().int().nonnegative(),
9320
+ segmentsClaimed: number().int().nonnegative(),
9321
+ ghostSegments: number().int().nonnegative(),
9322
+ ghostBytes: number().int().nonnegative(),
9323
+ ghostHoursWhole: number().int().nonnegative(),
9324
+ forgottenSegments: number().int().nonnegative(),
9325
+ forgottenBytes: number().int().nonnegative(),
9326
+ /** Files under a claimed hour that no durable row names. Never deleted. */
9327
+ orphanFiles: number().int().nonnegative(),
9328
+ orphanSample: array(string()).readonly(),
9329
+ hoursSkipped: number().int().nonnegative(),
9330
+ skippedByReason: LedgerWalkSkipCountsSchema,
9331
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
9332
+ bounded: boolean(),
9333
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
9334
+ });
9204
9335
  /** How many rows a media pass would still act on against a given target — the
9205
9336
  * media lane's denominator AND its residue, from ONE derivation so the two can
9206
9337
  * never disagree. `null` = the count could not be taken. */
9207
9338
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9208
9339
  var RelocatableMediaCountInputSchema = object({
9209
9340
  toLocationId: string().min(1),
9341
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
9342
+ fromLocationId: string().optional(),
9210
9343
  /** Omitted = `move`. */
9211
9344
  mode: MediaRelocateModeSchema.optional()
9212
9345
  });
9213
9346
  /**
9347
+ * Operator cleanup of leftover analytics rows, optional debug media, and
9348
+ * ghost ledger entries on frozen footage locations.
9349
+ *
9350
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
9351
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
9352
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
9353
+ * with no operator-visible status.
9354
+ */
9355
+ var StorageCleanupPhaseSchema = _enum([
9356
+ "orphans",
9357
+ "debug-media",
9358
+ "ghost-ledger",
9359
+ "done",
9360
+ "failed",
9361
+ "cancelled"
9362
+ ]);
9363
+ var StorageCleanupInputSchema = object({
9364
+ /** Also walk motion stills / track filmstrips. Off by default. */
9365
+ includeDebugMedia: boolean().optional() });
9366
+ var StorageCleanupJobSchema = object({
9367
+ jobId: string(),
9368
+ phase: StorageCleanupPhaseSchema,
9369
+ includeDebugMedia: boolean(),
9370
+ orphansReclaimed: number().int().nonnegative(),
9371
+ orphanBytesReclaimed: number().int().nonnegative(),
9372
+ debugMediaReclaimed: number().int().nonnegative(),
9373
+ debugMediaBytesReclaimed: number().int().nonnegative(),
9374
+ ghostsForgotten: number().int().nonnegative(),
9375
+ ghostBytesForgotten: number().int().nonnegative(),
9376
+ /** Short operator-facing line: current collection, pass, or location. */
9377
+ detail: string().nullable(),
9378
+ cancelRequested: boolean(),
9379
+ startedAt: number(),
9380
+ updatedAt: number(),
9381
+ finishedAt: number().nullable(),
9382
+ error: string().nullable()
9383
+ });
9384
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
9385
+ /**
9214
9386
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9215
9387
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9216
9388
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9238,11 +9410,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
9238
9410
  * The default location for a type uses `id === <type>:default` by
9239
9411
  * convention (the bare type ref like `'backups'` resolves to it).
9240
9412
  *
9241
- * `isSystem: true` marks a location as orchestrator-seeded and
9242
- * undeletable. The bootstrap-installed defaults (one per type) carry
9243
- * this flag; operator-added locations don't. Editing the config of
9244
- * a system location is allowed (path migration, provider swap) but
9245
- * deleting it is rejected at the cap level.
9413
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
9414
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
9415
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
9416
+ * / last-enabled, not on this bit.
9246
9417
  */
9247
9418
  var StorageLocationSchema = object({
9248
9419
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13785,6 +13956,12 @@ method(object({
13785
13956
  }), _void(), {
13786
13957
  kind: "mutation",
13787
13958
  auth: "admin"
13959
+ }), method(object({
13960
+ from: string(),
13961
+ to: string()
13962
+ }), object({ moved: number() }), {
13963
+ kind: "mutation",
13964
+ auth: "admin"
13788
13965
  }), method(object({
13789
13966
  deviceId: number(),
13790
13967
  disabled: boolean()
@@ -19793,53 +19970,15 @@ var RecentTracksPageSchema = object({
19793
19970
  /** Cursor for the next page, or null when this page is the last. */
19794
19971
  nextCursor: string().nullable()
19795
19972
  });
19796
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19797
- var LIST_GROUPS_MAX_LIMIT = 100;
19798
- var AnalyticsGroupRecordSchema = object({
19799
- id: string(),
19800
- deviceId: number().int(),
19801
- openedAt: number().int(),
19802
- closedAt: number().int(),
19803
- timestamp: number().int(),
19804
- memberCount: number().int(),
19805
- memberTrackIds: array(string()).readonly(),
19806
- className: string(),
19807
- classes: array(string()).readonly(),
19808
- /** Relative event-media path, or null when the group has no picture yet. */
19809
- mediaUrl: string().nullable(),
19810
- singleton: boolean()
19811
- });
19812
- var AnalyticsGroupMemberSchema = object({
19813
- trackId: string(),
19814
- deviceId: number().int(),
19815
- className: string(),
19816
- firstSeen: number().int(),
19817
- lastSeen: number().int(),
19818
- mediaUrl: string().nullable()
19819
- });
19820
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19821
- var ListGroupsQueryInput = object({
19822
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19823
- deviceIds: array(number()),
19824
- /** Window lower bound on `closedAt` (inclusive). */
19825
- since: number().optional(),
19826
- /** Window upper bound on `openedAt` (inclusive). */
19827
- until: number().optional(),
19828
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19829
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19830
- cursor: string().optional()
19831
- });
19832
- var ListGroupsPageSchema = object({
19833
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19834
- nextCursor: string().nullable()
19835
- });
19973
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19974
+ var KEY_EVENTS_MAX_LIMIT = 200;
19836
19975
  var KeyEventQueryInput = object({
19837
19976
  deviceId: number(),
19838
19977
  /** Window lower bound (track firstSeen ≥ since). */
19839
19978
  since: number(),
19840
19979
  /** Window upper bound (track firstSeen ≤ until). */
19841
19980
  until: number(),
19842
- limit: number().int().min(1).max(200).default(50),
19981
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19843
19982
  /** Drop tracks scoring below this importance. */
19844
19983
  minImportance: number().min(0).max(1).optional(),
19845
19984
  /** Restrict to a single class (e.g. 'person'). */
@@ -19861,6 +20000,32 @@ var KeyEventSchema = object({
19861
20000
  ...TrackFlagFields,
19862
20001
  ...TrackRetrainFields
19863
20002
  });
20003
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
20004
+ var KeyEventBatchQueryInput = object({
20005
+ deviceIds: array(number()).min(1).max(200),
20006
+ since: number(),
20007
+ until: number(),
20008
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
20009
+ * across the set, which would let a busy camera starve a quiet one of its
20010
+ * rows and change what the merged feed contains. */
20011
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
20012
+ minImportance: number().min(0).max(1).optional(),
20013
+ classFilter: string().optional()
20014
+ });
20015
+ /**
20016
+ * One camera's key events in a batch answer.
20017
+ *
20018
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
20019
+ * error rather than throwing, so a camera whose store read failed and one with
20020
+ * no events in the window were ALREADY indistinguishable per camera — the
20021
+ * batch does not make that worse, and the row keeps the deviceId the single
20022
+ * method's output never carried (the caller used to stamp it from the fan-out
20023
+ * key, which only worked because there was one query per camera).
20024
+ */
20025
+ var KeyEventsForDeviceSchema = object({
20026
+ deviceId: number(),
20027
+ events: array(KeyEventSchema).readonly()
20028
+ });
19864
20029
  object({
19865
20030
  trackId: string(),
19866
20031
  className: string(),
@@ -19908,9 +20073,7 @@ var TrackCascadeCountsSchema = object({
19908
20073
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19909
20074
  plates: number().int(),
19910
20075
  /** Per-track CLIP search vectors removed (best-effort). */
19911
- embeddings: number().int(),
19912
- /** Group membership + group rows removed with their last member (best-effort). */
19913
- groups: number().int()
20076
+ embeddings: number().int()
19914
20077
  });
19915
20078
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19916
20079
  var DiskReconcileCountsSchema = object({
@@ -20080,6 +20243,47 @@ var RebuildStatusSchema = object({
20080
20243
  /** Present when the pass ended by throwing. */
20081
20244
  error: string().nullable()
20082
20245
  });
20246
+ /**
20247
+ * Acknowledgement that a debug-media reclaim STARTED.
20248
+ *
20249
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
20250
+ * it runs detached and this returns immediately. Awaiting it is how the
20251
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
20252
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
20253
+ */
20254
+ var MediaReclaimStartResultSchema = object({
20255
+ started: boolean(),
20256
+ /** True when a pass was already running; the new request is ignored. */
20257
+ alreadyRunning: boolean()
20258
+ });
20259
+ var MediaReclaimInputSchema = object({
20260
+ mode: _enum(["report", "reclaim"]).default("report"),
20261
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
20262
+ deviceIds: array(number().int()).min(1).optional(),
20263
+ restart: boolean().optional(),
20264
+ pageSize: number().int().min(50).max(5e3).optional(),
20265
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
20266
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
20267
+ maxBytesPerRun: number().int().min(1).optional(),
20268
+ budgetMinutes: number().int().min(1).max(720).optional(),
20269
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
20270
+ graceMinutes: number().int().min(1).max(10080).optional()
20271
+ });
20272
+ var MediaReclaimStatusSchema = object({
20273
+ running: boolean(),
20274
+ mode: _enum(["report", "reclaim"]).nullable(),
20275
+ totalExamined: number(),
20276
+ totalEligible: number(),
20277
+ totalReclaimed: number(),
20278
+ totalBytesReclaimed: number(),
20279
+ totalRefused: number(),
20280
+ /** Device+scope windows finished in this pass. */
20281
+ devicesDone: number(),
20282
+ complete: boolean().nullable(),
20283
+ startedAtMs: number().nullable(),
20284
+ finishedAtMs: number().nullable(),
20285
+ error: string().nullable()
20286
+ });
20083
20287
  var ReplayFrameInputSchema = object({
20084
20288
  timestamp: number(),
20085
20289
  frame: PipelineRunResultBridge
@@ -20118,10 +20322,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20118
20322
  * stationary registry). Default false: the timeline lists passages,
20119
20323
  * not parking records (operator decision, 2026-08-15). */
20120
20324
  includeStationary: boolean().optional()
20121
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
20122
- deviceId: number(),
20123
- groupId: string().min(1)
20124
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20325
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
20125
20326
  kind: "mutation",
20126
20327
  auth: "admin"
20127
20328
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -20130,7 +20331,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20130
20331
  until: number().optional(),
20131
20332
  kinds: array(string()).optional(),
20132
20333
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20133
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
20334
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20134
20335
  deviceId: number(),
20135
20336
  since: number(),
20136
20337
  until: number(),
@@ -20221,6 +20422,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20221
20422
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20222
20423
  kind: "query",
20223
20424
  auth: "admin"
20425
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
20426
+ kind: "mutation",
20427
+ auth: "admin"
20428
+ }), method(object({}), MediaReclaimStatusSchema, {
20429
+ kind: "query",
20430
+ auth: "admin"
20224
20431
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
20225
20432
  kind: "query",
20226
20433
  auth: "admin"
@@ -22191,7 +22398,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
22191
22398
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
22192
22399
  kind: "mutation",
22193
22400
  auth: "admin"
22194
- });
22401
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
22402
+ kind: "mutation",
22403
+ auth: "admin"
22404
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22405
+ kind: "mutation",
22406
+ auth: "admin"
22407
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
22195
22408
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
22196
22409
  providerId: string().min(1),
22197
22410
  displayName: string().min(1),
@@ -29292,6 +29505,33 @@ var RecordingRebalanceInputSchema = object({
29292
29505
  minMoveGb: number().min(0).optional()
29293
29506
  });
29294
29507
  /**
29508
+ * Operator-facing placement of one camera onto a recordings location.
29509
+ *
29510
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
29511
+ * Auto (the planner may move this camera). `locationId` is where high/mid
29512
+ * currently write — the plan, which may disagree with the pin when Auto.
29513
+ */
29514
+ var RecordingDevicePlacementSchema = object({
29515
+ deviceId: number().int(),
29516
+ profile: string(),
29517
+ locationId: string()
29518
+ });
29519
+ var RecordingDevicePinSchema = object({
29520
+ deviceId: number().int(),
29521
+ /** Recordings-class location this camera is pinned to. */
29522
+ locationId: string()
29523
+ });
29524
+ var RecordingPlacementViewSchema = object({
29525
+ assignments: array(RecordingDevicePlacementSchema),
29526
+ pins: array(RecordingDevicePinSchema),
29527
+ defaultLocations: record(string(), string())
29528
+ });
29529
+ var RecordingSetDevicePlacementInputSchema = object({
29530
+ deviceId: number().int(),
29531
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
29532
+ locationId: string().nullable()
29533
+ });
29534
+ /**
29295
29535
  * Result of locating footage at a wall-clock instant for one device/profile.
29296
29536
  * `segment` carries the covering segment's window; `gap` reports the forward
29297
29537
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -29505,6 +29745,9 @@ method(object({
29505
29745
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29506
29746
  kind: "query",
29507
29747
  auth: "admin"
29748
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
29749
+ kind: "mutation",
29750
+ auth: "admin"
29508
29751
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29509
29752
  kind: "mutation",
29510
29753
  auth: "admin"
@@ -29514,6 +29757,12 @@ method(object({
29514
29757
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
29515
29758
  kind: "mutation",
29516
29759
  auth: "admin"
29760
+ }), method(object({}), RecordingPlacementViewSchema, {
29761
+ kind: "query",
29762
+ auth: "admin"
29763
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
29764
+ kind: "mutation",
29765
+ auth: "admin"
29517
29766
  });
29518
29767
  /**
29519
29768
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -29896,6 +30145,25 @@ var SceneMonitorStatusSchema = object({
29896
30145
  monitors: array(SceneMonitorSchema),
29897
30146
  lastFetchedAt: number()
29898
30147
  });
30148
+ /**
30149
+ * One camera's row in a `listScenesBatch` answer.
30150
+ *
30151
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
30152
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
30153
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
30154
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
30155
+ * configured. Fanned out per camera the difference was visible — one query
30156
+ * errored while the others resolved — and a batch that returned only the rows
30157
+ * it managed would have destroyed it, silently, by making an unreachable camera
30158
+ * indistinguishable from one that answered `monitors: []`.
30159
+ *
30160
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
30161
+ * could not be read"; `status.monitors: []` means "read, and it has none".
30162
+ */
30163
+ var SceneMonitorStatusForDeviceSchema = object({
30164
+ deviceId: number(),
30165
+ status: SceneMonitorStatusSchema.nullable()
30166
+ });
29899
30167
  var sceneMonitorCapability = {
29900
30168
  name: "scene-monitor",
29901
30169
  scope: "device",
@@ -29905,6 +30173,22 @@ var sceneMonitorCapability = {
29905
30173
  deviceTypes: [DeviceType.Camera],
29906
30174
  methods: {
29907
30175
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
30176
+ /**
30177
+ * The same answer, for a SET of cameras, in one round trip.
30178
+ *
30179
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
30180
+ * poll behind the push slice. Fanned out client-side that was one query
30181
+ * per camera — 29 round trips through the browser, the hub and the
30182
+ * post-analysis runner every 30 seconds to read an in-memory map the
30183
+ * owner had already merged. The work is unchanged (`statusFor` per
30184
+ * device, all in-process at the owner); what collapses is the transport.
30185
+ *
30186
+ * A camera that cannot answer still gets a row, with `status: null` —
30187
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
30188
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
30189
+ * tell which two are missing, or that any are.
30190
+ */
30191
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
29908
30192
  createScene: method(object({
29909
30193
  deviceId: number(),
29910
30194
  label: string(),
@@ -31740,6 +32024,27 @@ var CameraOccupancySnapshotSchema = object({
31740
32024
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
31741
32025
  });
31742
32026
  /**
32027
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
32028
+ *
32029
+ * THREE outcomes, and the single-camera method could only express two of them
32030
+ * because `snapshot: null` was already spoken for:
32031
+ *
32032
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
32033
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
32034
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
32035
+ * - `read: 'unreadable'` — the owner could not answer for this
32036
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
32037
+ *
32038
+ * Collapsing the last two is the failure this field exists to prevent: a
32039
+ * hydration that threw would otherwise render as an empty Stationary section,
32040
+ * which is a definite claim about a camera nobody could read.
32041
+ */
32042
+ var CameraOccupancySnapshotForDeviceSchema = object({
32043
+ deviceId: number(),
32044
+ read: _enum(["read", "unreadable"]),
32045
+ snapshot: CameraOccupancySnapshotSchema.nullable()
32046
+ });
32047
+ /**
31743
32048
  * Time-series resolution. The history methods return one bucket per
31744
32049
  * step over the requested range. Smaller resolutions cost more
31745
32050
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -31796,6 +32101,20 @@ var zoneAnalyticsCapability = {
31796
32101
  * (no inference result emitted since boot or since binding was
31797
32102
  * activated). */
31798
32103
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
32104
+ /**
32105
+ * The same snapshot, for a SET of cameras, in one round trip.
32106
+ *
32107
+ * The Events page's Stationary section polls this every 15s for every
32108
+ * selected camera. Fanned out client-side that is one query per camera to
32109
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
32110
+ * costs everything. Batched, N transports become one and the per-device
32111
+ * work is unchanged.
32112
+ *
32113
+ * Every requested deviceId gets a row, tagged `read` — see
32114
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
32115
+ * not answer for is `'unreadable'`, never an empty reading.
32116
+ */
32117
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
31799
32118
  /** Time-series object count inside one zone. `className` optional —
31800
32119
  * omit to count every class in the zone. */
31801
32120
  getZoneHistory: method(object({
@@ -34513,6 +34832,12 @@ Object.freeze({
34513
34832
  addonId: null,
34514
34833
  access: "delete"
34515
34834
  },
34835
+ "deviceManager.renameLocation": {
34836
+ capName: "device-manager",
34837
+ capScope: "system",
34838
+ addonId: null,
34839
+ access: "create"
34840
+ },
34516
34841
  "deviceManager.runDeviceAction": {
34517
34842
  capName: "device-manager",
34518
34843
  capScope: "system",
@@ -36205,13 +36530,19 @@ Object.freeze({
36205
36530
  addonId: null,
36206
36531
  access: "view"
36207
36532
  },
36208
- "pipelineAnalytics.getGroup": {
36533
+ "pipelineAnalytics.getKeyEvents": {
36209
36534
  capName: "pipeline-analytics",
36210
36535
  capScope: "device",
36211
36536
  addonId: null,
36212
36537
  access: "view"
36213
36538
  },
36214
- "pipelineAnalytics.getKeyEvents": {
36539
+ "pipelineAnalytics.getKeyEventsBatch": {
36540
+ capName: "pipeline-analytics",
36541
+ capScope: "device",
36542
+ addonId: null,
36543
+ access: "view"
36544
+ },
36545
+ "pipelineAnalytics.getMediaReclaimStatus": {
36215
36546
  capName: "pipeline-analytics",
36216
36547
  capScope: "device",
36217
36548
  addonId: null,
@@ -36301,12 +36632,6 @@ Object.freeze({
36301
36632
  addonId: null,
36302
36633
  access: "view"
36303
36634
  },
36304
- "pipelineAnalytics.listGroups": {
36305
- capName: "pipeline-analytics",
36306
- capScope: "device",
36307
- addonId: null,
36308
- access: "view"
36309
- },
36310
36635
  "pipelineAnalytics.listOpsLog": {
36311
36636
  capName: "pipeline-analytics",
36312
36637
  capScope: "device",
@@ -36391,6 +36716,12 @@ Object.freeze({
36391
36716
  addonId: null,
36392
36717
  access: "create"
36393
36718
  },
36719
+ "pipelineAnalytics.reclaimDebugMedia": {
36720
+ capName: "pipeline-analytics",
36721
+ capScope: "device",
36722
+ addonId: null,
36723
+ access: "create"
36724
+ },
36394
36725
  "pipelineAnalytics.reconcileFromDisk": {
36395
36726
  capName: "pipeline-analytics",
36396
36727
  capScope: "device",
@@ -37315,6 +37646,12 @@ Object.freeze({
37315
37646
  addonId: null,
37316
37647
  access: "view"
37317
37648
  },
37649
+ "recording.getPlacement": {
37650
+ capName: "recording",
37651
+ capScope: "system",
37652
+ addonId: null,
37653
+ access: "view"
37654
+ },
37318
37655
  "recording.getPlaybackManifest": {
37319
37656
  capName: "recording",
37320
37657
  capScope: "system",
@@ -37393,6 +37730,12 @@ Object.freeze({
37393
37730
  addonId: null,
37394
37731
  access: "view"
37395
37732
  },
37733
+ "recording.reconcileLedgerAgainstDisk": {
37734
+ capName: "recording",
37735
+ capScope: "system",
37736
+ addonId: null,
37737
+ access: "create"
37738
+ },
37396
37739
  "recording.refreshStorageLocationsForMigration": {
37397
37740
  capName: "recording",
37398
37741
  capScope: "system",
@@ -37435,6 +37778,12 @@ Object.freeze({
37435
37778
  addonId: null,
37436
37779
  access: "create"
37437
37780
  },
37781
+ "recording.setDevicePlacement": {
37782
+ capName: "recording",
37783
+ capScope: "system",
37784
+ addonId: null,
37785
+ access: "create"
37786
+ },
37438
37787
  "recording.startStorageMigrationMove": {
37439
37788
  capName: "recording",
37440
37789
  capScope: "system",
@@ -37519,6 +37868,12 @@ Object.freeze({
37519
37868
  addonId: null,
37520
37869
  access: "view"
37521
37870
  },
37871
+ "sceneMonitor.listScenesBatch": {
37872
+ capName: "scene-monitor",
37873
+ capScope: "device",
37874
+ addonId: null,
37875
+ access: "view"
37876
+ },
37522
37877
  "sceneMonitor.recheckNow": {
37523
37878
  capName: "scene-monitor",
37524
37879
  capScope: "device",
@@ -37873,12 +38228,36 @@ Object.freeze({
37873
38228
  addonId: null,
37874
38229
  access: "create"
37875
38230
  },
38231
+ "storageMigration.cleanupCancel": {
38232
+ capName: "storage-migration",
38233
+ capScope: "system",
38234
+ addonId: null,
38235
+ access: "create"
38236
+ },
38237
+ "storageMigration.cleanupStart": {
38238
+ capName: "storage-migration",
38239
+ capScope: "system",
38240
+ addonId: null,
38241
+ access: "create"
38242
+ },
38243
+ "storageMigration.cleanupStatus": {
38244
+ capName: "storage-migration",
38245
+ capScope: "system",
38246
+ addonId: null,
38247
+ access: "view"
38248
+ },
37876
38249
  "storageMigration.drain": {
37877
38250
  capName: "storage-migration",
37878
38251
  capScope: "system",
37879
38252
  addonId: null,
37880
38253
  access: "create"
37881
38254
  },
38255
+ "storageMigration.history": {
38256
+ capName: "storage-migration",
38257
+ capScope: "system",
38258
+ addonId: null,
38259
+ access: "view"
38260
+ },
37882
38261
  "storageMigration.movers": {
37883
38262
  capName: "storage-migration",
37884
38263
  capScope: "system",
@@ -38875,6 +39254,12 @@ Object.freeze({
38875
39254
  addonId: null,
38876
39255
  access: "view"
38877
39256
  },
39257
+ "zoneAnalytics.getCurrentSnapshotBatch": {
39258
+ capName: "zone-analytics",
39259
+ capScope: "device",
39260
+ addonId: null,
39261
+ access: "view"
39262
+ },
38878
39263
  "zoneAnalytics.getUnzonedHistory": {
38879
39264
  capName: "zone-analytics",
38880
39265
  capScope: "device",
@@ -39869,14 +40254,14 @@ Object.freeze({
39869
40254
  form: "single",
39870
40255
  optional: true
39871
40256
  }],
39872
- "pipelineAnalytics.getGroup": [{
40257
+ "pipelineAnalytics.getKeyEvents": [{
39873
40258
  name: "deviceId",
39874
40259
  form: "single",
39875
40260
  optional: false
39876
40261
  }],
39877
- "pipelineAnalytics.getKeyEvents": [{
39878
- name: "deviceId",
39879
- form: "single",
40262
+ "pipelineAnalytics.getKeyEventsBatch": [{
40263
+ name: "deviceIds",
40264
+ form: "array",
39880
40265
  optional: false
39881
40266
  }],
39882
40267
  "pipelineAnalytics.getMotionEvents": [{
@@ -39934,11 +40319,6 @@ Object.freeze({
39934
40319
  form: "single",
39935
40320
  optional: false
39936
40321
  }],
39937
- "pipelineAnalytics.listGroups": [{
39938
- name: "deviceIds",
39939
- form: "array",
39940
- optional: false
39941
- }],
39942
40322
  "pipelineAnalytics.listOpsLog": [{
39943
40323
  name: "deviceId",
39944
40324
  form: "single",
@@ -39984,6 +40364,11 @@ Object.freeze({
39984
40364
  form: "single",
39985
40365
  optional: true
39986
40366
  }],
40367
+ "pipelineAnalytics.reclaimDebugMedia": [{
40368
+ name: "deviceIds",
40369
+ form: "array",
40370
+ optional: true
40371
+ }],
39987
40372
  "pipelineAnalytics.reconcileFromDisk": [{
39988
40373
  name: "deviceId",
39989
40374
  form: "single",
@@ -40319,6 +40704,11 @@ Object.freeze({
40319
40704
  form: "single",
40320
40705
  optional: false
40321
40706
  }],
40707
+ "recording.reconcileLedgerAgainstDisk": [{
40708
+ name: "deviceId",
40709
+ form: "single",
40710
+ optional: true
40711
+ }],
40322
40712
  "recording.relocateFootage": [{
40323
40713
  name: "deviceId",
40324
40714
  form: "single",
@@ -40344,6 +40734,11 @@ Object.freeze({
40344
40734
  form: "single",
40345
40735
  optional: false
40346
40736
  }],
40737
+ "recording.setDevicePlacement": [{
40738
+ name: "deviceId",
40739
+ form: "single",
40740
+ optional: false
40741
+ }],
40347
40742
  "recording.startStorageMigrationMove": [{
40348
40743
  name: "deviceId",
40349
40744
  form: "single",
@@ -40384,6 +40779,11 @@ Object.freeze({
40384
40779
  form: "single",
40385
40780
  optional: false
40386
40781
  }],
40782
+ "sceneMonitor.listScenesBatch": [{
40783
+ name: "deviceIds",
40784
+ form: "array",
40785
+ optional: false
40786
+ }],
40387
40787
  "sceneMonitor.recheckNow": [{
40388
40788
  name: "deviceId",
40389
40789
  form: "single",
@@ -40645,6 +41045,11 @@ Object.freeze({
40645
41045
  form: "single",
40646
41046
  optional: false
40647
41047
  }],
41048
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
41049
+ name: "deviceIds",
41050
+ form: "array",
41051
+ optional: false
41052
+ }],
40648
41053
  "zoneAnalytics.getUnzonedHistory": [{
40649
41054
  name: "deviceId",
40650
41055
  form: "single",