@camstack/addon-provider-rtsp 1.2.48 → 1.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.mjs CHANGED
@@ -8050,6 +8050,20 @@ var RelocateJobSchema = object({
8050
8050
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8051
8051
  */
8052
8052
  rowsReconciled: number().int().nonnegative().optional(),
8053
+ /**
8054
+ * Rows this run FORGOT because the file they name is not on disk.
8055
+ *
8056
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8057
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8058
+ * and the durable row is dropped through the same channel eviction uses. It
8059
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8060
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8061
+ * the same failure as one that quietly skips them (D295).
8062
+ *
8063
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8064
+ * ledger claimed 5.65 GB of footage that no longer existed.
8065
+ */
8066
+ rowsForgotten: number().int().nonnegative().optional(),
8053
8067
  startedAt: number(),
8054
8068
  finishedAt: number().nullable(),
8055
8069
  error: string().nullable()
@@ -8114,6 +8128,13 @@ var MediaRelocateModeSchema = _enum([
8114
8128
  ]);
8115
8129
  var RelocateMediaInputSchema = object({
8116
8130
  toLocationId: string(),
8131
+ /**
8132
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8133
+ * every row that is not already on `toLocationId` (the historical
8134
+ * behaviour). A named source is what a from→to migration needs: without it
8135
+ * "move events off disk 2" also emptied disk 1.
8136
+ */
8137
+ fromLocationId: string().optional(),
8117
8138
  throttleMbps: number().min(1).max(1e3).optional(),
8118
8139
  /** Omitted = `move`, the pre-existing behaviour. */
8119
8140
  mode: MediaRelocateModeSchema.optional()
@@ -8181,6 +8202,19 @@ var StorageMigrationDestinationsSchema = object({
8181
8202
  galleryMedia: string().min(1).optional()
8182
8203
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8183
8204
  /**
8205
+ * Optional named source per class. Omitted = the class's current default
8206
+ * (the historical behaviour). A named source that is NOT the default is a
8207
+ * drain of that disk: bytes move, the default stays, and the source is
8208
+ * disabled when the move finishes.
8209
+ */
8210
+ var StorageMigrationSourcesSchema = object({
8211
+ recordings: string().min(1).optional(),
8212
+ recordingsLow: string().min(1).optional(),
8213
+ eventMedia: string().min(1).optional(),
8214
+ backups: string().min(1).optional(),
8215
+ galleryMedia: string().min(1).optional()
8216
+ }).optional();
8217
+ /**
8184
8218
  * How a migration sequences the cutover against the byte move.
8185
8219
  *
8186
8220
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8202,6 +8236,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8202
8236
  /** Shared input for planning and starting an orchestrated storage migration. */
8203
8237
  var StorageMigrationInputSchema = object({
8204
8238
  destinations: StorageMigrationDestinationsSchema,
8239
+ /** Omitted = each class's current default. */
8240
+ sources: StorageMigrationSourcesSchema,
8205
8241
  throttleMbps: number().min(1).max(1e3).optional(),
8206
8242
  /** Omitted = `blocking`, which stays the default. */
8207
8243
  mode: StorageMigrationModeSchema.optional()
@@ -8281,6 +8317,13 @@ var StorageMigrationMoveSchema = object({
8281
8317
  storageClass: StorageMigrationClassSchema,
8282
8318
  fromLocationId: string(),
8283
8319
  toLocationId: string(),
8320
+ /**
8321
+ * True when `from` was NOT the class default at plan time. The move still
8322
+ * copies bytes, but the default is left alone and the source is disabled
8323
+ * once the copy verifies. Absent on jobs planned before this field existed
8324
+ * — those jobs always repointed, which is `false`.
8325
+ */
8326
+ freezeSource: boolean().optional(),
8284
8327
  moverJobId: string().nullable(),
8285
8328
  state: RelocateJobStateSchema.nullable(),
8286
8329
  error: string().nullable(),
@@ -8294,6 +8337,7 @@ var StorageMigrationJobSchema = object({
8294
8337
  * can tell a seconds-long cutover from a thirty-hour one. */
8295
8338
  mode: StorageMigrationModeSchema,
8296
8339
  destinations: StorageMigrationDestinationsSchema,
8340
+ sources: StorageMigrationSourcesSchema,
8297
8341
  throttleMbps: number(),
8298
8342
  moves: array(StorageMigrationMoveSchema),
8299
8343
  pauseLeaseId: string().nullable(),
@@ -8319,6 +8363,7 @@ var StorageMigrationFindingSchema = object({
8319
8363
  });
8320
8364
  var StorageMigrationPlanSchema = object({
8321
8365
  destinations: StorageMigrationDestinationsSchema,
8366
+ sources: StorageMigrationSourcesSchema,
8322
8367
  /** The mode this plan was built for. A plan is only valid for its mode: the
8323
8368
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8324
8369
  * it. */
@@ -8326,7 +8371,8 @@ var StorageMigrationPlanSchema = object({
8326
8371
  moves: array(object({
8327
8372
  storageClass: StorageMigrationClassSchema,
8328
8373
  fromLocationId: string(),
8329
- toLocationId: string()
8374
+ toLocationId: string(),
8375
+ freezeSource: boolean().optional()
8330
8376
  })),
8331
8377
  findings: array(StorageMigrationFindingSchema)
8332
8378
  });
@@ -8413,16 +8459,142 @@ var RelocateResidueSchema = object({
8413
8459
  segments: number().int().nonnegative(),
8414
8460
  bytes: number().int().nonnegative()
8415
8461
  }).nullable();
8462
+ /**
8463
+ * Ask one location whether its durable hour rows describe the disk — the walk
8464
+ * (D319).
8465
+ *
8466
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8467
+ * missing tool is the question, and the dry run is how they sanity-check the
8468
+ * destructive run before authorising it.
8469
+ */
8470
+ var LedgerWalkInputSchema = object({
8471
+ locationId: string().min(1),
8472
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8473
+ apply: boolean().optional(),
8474
+ /** Narrow to one camera. */
8475
+ deviceId: number().int().positive().optional(),
8476
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8477
+ profiles: array(string().min(1)).optional()
8478
+ });
8479
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8480
+ var LedgerWalkRefusalSchema = _enum([
8481
+ "location-unknown",
8482
+ "source-writable",
8483
+ "no-ledger",
8484
+ "archive-unreadable",
8485
+ "anchor-absent",
8486
+ "anchor-unreadable",
8487
+ "anchor-moved"
8488
+ ]);
8489
+ _enum([
8490
+ "live-tail",
8491
+ "listing-error",
8492
+ "path-mismatch",
8493
+ "durable-refused"
8494
+ ]);
8495
+ /** Every skip reason, always present, always a number — so a reason that never
8496
+ * fired reports as zero rather than absent and the report shape is constant
8497
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8498
+ var LedgerWalkSkipCountsSchema = object({
8499
+ "live-tail": number().int().nonnegative(),
8500
+ "listing-error": number().int().nonnegative(),
8501
+ "path-mismatch": number().int().nonnegative(),
8502
+ "durable-refused": number().int().nonnegative()
8503
+ });
8504
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8505
+ var LedgerWalkDeviceReportSchema = object({
8506
+ deviceId: number().int(),
8507
+ hoursWalked: number().int().nonnegative(),
8508
+ hoursMissing: number().int().nonnegative(),
8509
+ ghostSegments: number().int().nonnegative(),
8510
+ ghostBytes: number().int().nonnegative(),
8511
+ forgottenSegments: number().int().nonnegative(),
8512
+ orphanFiles: number().int().nonnegative()
8513
+ });
8514
+ /**
8515
+ * What one walk claimed, listed, found and (only when armed) forgot.
8516
+ *
8517
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8518
+ * walk that saw a fraction of the location is visible in its own report rather
8519
+ * than in the absence of one.
8520
+ */
8521
+ var LedgerWalkReportSchema = object({
8522
+ locationId: string(),
8523
+ applied: boolean(),
8524
+ refused: LedgerWalkRefusalSchema.nullable(),
8525
+ archiveSegments: number().int().nonnegative().nullable(),
8526
+ archiveBytes: number().int().nonnegative().nullable(),
8527
+ hoursClaimed: number().int().nonnegative(),
8528
+ hoursWalked: number().int().nonnegative(),
8529
+ hoursMissing: number().int().nonnegative(),
8530
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8531
+ listings: number().int().nonnegative(),
8532
+ segmentsClaimed: number().int().nonnegative(),
8533
+ ghostSegments: number().int().nonnegative(),
8534
+ ghostBytes: number().int().nonnegative(),
8535
+ ghostHoursWhole: number().int().nonnegative(),
8536
+ forgottenSegments: number().int().nonnegative(),
8537
+ forgottenBytes: number().int().nonnegative(),
8538
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8539
+ orphanFiles: number().int().nonnegative(),
8540
+ orphanSample: array(string()).readonly(),
8541
+ hoursSkipped: number().int().nonnegative(),
8542
+ skippedByReason: LedgerWalkSkipCountsSchema,
8543
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8544
+ bounded: boolean(),
8545
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8546
+ });
8416
8547
  /** How many rows a media pass would still act on against a given target — the
8417
8548
  * media lane's denominator AND its residue, from ONE derivation so the two can
8418
8549
  * never disagree. `null` = the count could not be taken. */
8419
8550
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8420
8551
  var RelocatableMediaCountInputSchema = object({
8421
8552
  toLocationId: string().min(1),
8553
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8554
+ fromLocationId: string().optional(),
8422
8555
  /** Omitted = `move`. */
8423
8556
  mode: MediaRelocateModeSchema.optional()
8424
8557
  });
8425
8558
  /**
8559
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8560
+ * ghost ledger entries on frozen footage locations.
8561
+ *
8562
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8563
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8564
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8565
+ * with no operator-visible status.
8566
+ */
8567
+ var StorageCleanupPhaseSchema = _enum([
8568
+ "orphans",
8569
+ "debug-media",
8570
+ "ghost-ledger",
8571
+ "done",
8572
+ "failed",
8573
+ "cancelled"
8574
+ ]);
8575
+ var StorageCleanupInputSchema = object({
8576
+ /** Also walk motion stills / track filmstrips. Off by default. */
8577
+ includeDebugMedia: boolean().optional() });
8578
+ var StorageCleanupJobSchema = object({
8579
+ jobId: string(),
8580
+ phase: StorageCleanupPhaseSchema,
8581
+ includeDebugMedia: boolean(),
8582
+ orphansReclaimed: number().int().nonnegative(),
8583
+ orphanBytesReclaimed: number().int().nonnegative(),
8584
+ debugMediaReclaimed: number().int().nonnegative(),
8585
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8586
+ ghostsForgotten: number().int().nonnegative(),
8587
+ ghostBytesForgotten: number().int().nonnegative(),
8588
+ /** Short operator-facing line: current collection, pass, or location. */
8589
+ detail: string().nullable(),
8590
+ cancelRequested: boolean(),
8591
+ startedAt: number(),
8592
+ updatedAt: number(),
8593
+ finishedAt: number().nullable(),
8594
+ error: string().nullable()
8595
+ });
8596
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8597
+ /**
8426
8598
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8427
8599
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8428
8600
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8450,11 +8622,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8450
8622
  * The default location for a type uses `id === <type>:default` by
8451
8623
  * convention (the bare type ref like `'backups'` resolves to it).
8452
8624
  *
8453
- * `isSystem: true` marks a location as orchestrator-seeded and
8454
- * undeletable. The bootstrap-installed defaults (one per type) carry
8455
- * this flag; operator-added locations don't. Editing the config of
8456
- * a system location is allowed (path migration, provider swap) but
8457
- * deleting it is rejected at the cap level.
8625
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8626
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8627
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8628
+ * / last-enabled, not on this bit.
8458
8629
  */
8459
8630
  var StorageLocationSchema = object({
8460
8631
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12980,6 +13151,12 @@ method(object({
12980
13151
  }), _void(), {
12981
13152
  kind: "mutation",
12982
13153
  auth: "admin"
13154
+ }), method(object({
13155
+ from: string(),
13156
+ to: string()
13157
+ }), object({ moved: number() }), {
13158
+ kind: "mutation",
13159
+ auth: "admin"
12983
13160
  }), method(object({
12984
13161
  deviceId: number(),
12985
13162
  disabled: boolean()
@@ -18988,53 +19165,15 @@ var RecentTracksPageSchema = object({
18988
19165
  /** Cursor for the next page, or null when this page is the last. */
18989
19166
  nextCursor: string().nullable()
18990
19167
  });
18991
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18992
- var LIST_GROUPS_MAX_LIMIT = 100;
18993
- var AnalyticsGroupRecordSchema = object({
18994
- id: string(),
18995
- deviceId: number().int(),
18996
- openedAt: number().int(),
18997
- closedAt: number().int(),
18998
- timestamp: number().int(),
18999
- memberCount: number().int(),
19000
- memberTrackIds: array(string()).readonly(),
19001
- className: string(),
19002
- classes: array(string()).readonly(),
19003
- /** Relative event-media path, or null when the group has no picture yet. */
19004
- mediaUrl: string().nullable(),
19005
- singleton: boolean()
19006
- });
19007
- var AnalyticsGroupMemberSchema = object({
19008
- trackId: string(),
19009
- deviceId: number().int(),
19010
- className: string(),
19011
- firstSeen: number().int(),
19012
- lastSeen: number().int(),
19013
- mediaUrl: string().nullable()
19014
- });
19015
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19016
- var ListGroupsQueryInput = object({
19017
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19018
- deviceIds: array(number()),
19019
- /** Window lower bound on `closedAt` (inclusive). */
19020
- since: number().optional(),
19021
- /** Window upper bound on `openedAt` (inclusive). */
19022
- until: number().optional(),
19023
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19024
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19025
- cursor: string().optional()
19026
- });
19027
- var ListGroupsPageSchema = object({
19028
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19029
- nextCursor: string().nullable()
19030
- });
19168
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19169
+ var KEY_EVENTS_MAX_LIMIT = 200;
19031
19170
  var KeyEventQueryInput = object({
19032
19171
  deviceId: number(),
19033
19172
  /** Window lower bound (track firstSeen ≥ since). */
19034
19173
  since: number(),
19035
19174
  /** Window upper bound (track firstSeen ≤ until). */
19036
19175
  until: number(),
19037
- limit: number().int().min(1).max(200).default(50),
19176
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19038
19177
  /** Drop tracks scoring below this importance. */
19039
19178
  minImportance: number().min(0).max(1).optional(),
19040
19179
  /** Restrict to a single class (e.g. 'person'). */
@@ -19056,6 +19195,32 @@ var KeyEventSchema = object({
19056
19195
  ...TrackFlagFields,
19057
19196
  ...TrackRetrainFields
19058
19197
  });
19198
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19199
+ var KeyEventBatchQueryInput = object({
19200
+ deviceIds: array(number()).min(1).max(200),
19201
+ since: number(),
19202
+ until: number(),
19203
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19204
+ * across the set, which would let a busy camera starve a quiet one of its
19205
+ * rows and change what the merged feed contains. */
19206
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19207
+ minImportance: number().min(0).max(1).optional(),
19208
+ classFilter: string().optional()
19209
+ });
19210
+ /**
19211
+ * One camera's key events in a batch answer.
19212
+ *
19213
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19214
+ * error rather than throwing, so a camera whose store read failed and one with
19215
+ * no events in the window were ALREADY indistinguishable per camera — the
19216
+ * batch does not make that worse, and the row keeps the deviceId the single
19217
+ * method's output never carried (the caller used to stamp it from the fan-out
19218
+ * key, which only worked because there was one query per camera).
19219
+ */
19220
+ var KeyEventsForDeviceSchema = object({
19221
+ deviceId: number(),
19222
+ events: array(KeyEventSchema).readonly()
19223
+ });
19059
19224
  object({
19060
19225
  trackId: string(),
19061
19226
  className: string(),
@@ -19103,9 +19268,7 @@ var TrackCascadeCountsSchema = object({
19103
19268
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19104
19269
  plates: number().int(),
19105
19270
  /** Per-track CLIP search vectors removed (best-effort). */
19106
- embeddings: number().int(),
19107
- /** Group membership + group rows removed with their last member (best-effort). */
19108
- groups: number().int()
19271
+ embeddings: number().int()
19109
19272
  });
19110
19273
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19111
19274
  var DiskReconcileCountsSchema = object({
@@ -19275,6 +19438,47 @@ var RebuildStatusSchema = object({
19275
19438
  /** Present when the pass ended by throwing. */
19276
19439
  error: string().nullable()
19277
19440
  });
19441
+ /**
19442
+ * Acknowledgement that a debug-media reclaim STARTED.
19443
+ *
19444
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19445
+ * it runs detached and this returns immediately. Awaiting it is how the
19446
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19447
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19448
+ */
19449
+ var MediaReclaimStartResultSchema = object({
19450
+ started: boolean(),
19451
+ /** True when a pass was already running; the new request is ignored. */
19452
+ alreadyRunning: boolean()
19453
+ });
19454
+ var MediaReclaimInputSchema = object({
19455
+ mode: _enum(["report", "reclaim"]).default("report"),
19456
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19457
+ deviceIds: array(number().int()).min(1).optional(),
19458
+ restart: boolean().optional(),
19459
+ pageSize: number().int().min(50).max(5e3).optional(),
19460
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19461
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19462
+ maxBytesPerRun: number().int().min(1).optional(),
19463
+ budgetMinutes: number().int().min(1).max(720).optional(),
19464
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19465
+ graceMinutes: number().int().min(1).max(10080).optional()
19466
+ });
19467
+ var MediaReclaimStatusSchema = object({
19468
+ running: boolean(),
19469
+ mode: _enum(["report", "reclaim"]).nullable(),
19470
+ totalExamined: number(),
19471
+ totalEligible: number(),
19472
+ totalReclaimed: number(),
19473
+ totalBytesReclaimed: number(),
19474
+ totalRefused: number(),
19475
+ /** Device+scope windows finished in this pass. */
19476
+ devicesDone: number(),
19477
+ complete: boolean().nullable(),
19478
+ startedAtMs: number().nullable(),
19479
+ finishedAtMs: number().nullable(),
19480
+ error: string().nullable()
19481
+ });
19278
19482
  var ReplayFrameInputSchema = object({
19279
19483
  timestamp: number(),
19280
19484
  frame: PipelineRunResultBridge
@@ -19313,10 +19517,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19313
19517
  * stationary registry). Default false: the timeline lists passages,
19314
19518
  * not parking records (operator decision, 2026-08-15). */
19315
19519
  includeStationary: boolean().optional()
19316
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19317
- deviceId: number(),
19318
- groupId: string().min(1)
19319
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19520
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19320
19521
  kind: "mutation",
19321
19522
  auth: "admin"
19322
19523
  }), 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({
@@ -19325,7 +19526,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19325
19526
  until: number().optional(),
19326
19527
  kinds: array(string()).optional(),
19327
19528
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19328
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19529
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19329
19530
  deviceId: number(),
19330
19531
  since: number(),
19331
19532
  until: number(),
@@ -19416,6 +19617,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19416
19617
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19417
19618
  kind: "query",
19418
19619
  auth: "admin"
19620
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19621
+ kind: "mutation",
19622
+ auth: "admin"
19623
+ }), method(object({}), MediaReclaimStatusSchema, {
19624
+ kind: "query",
19625
+ auth: "admin"
19419
19626
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19420
19627
  kind: "query",
19421
19628
  auth: "admin"
@@ -21490,7 +21697,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21490
21697
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21491
21698
  kind: "mutation",
21492
21699
  auth: "admin"
21493
- });
21700
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21701
+ kind: "mutation",
21702
+ auth: "admin"
21703
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21704
+ kind: "mutation",
21705
+ auth: "admin"
21706
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21494
21707
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21495
21708
  providerId: string().min(1),
21496
21709
  displayName: string().min(1),
@@ -28591,6 +28804,33 @@ var RecordingRebalanceInputSchema = object({
28591
28804
  minMoveGb: number().min(0).optional()
28592
28805
  });
28593
28806
  /**
28807
+ * Operator-facing placement of one camera onto a recordings location.
28808
+ *
28809
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
28810
+ * Auto (the planner may move this camera). `locationId` is where high/mid
28811
+ * currently write — the plan, which may disagree with the pin when Auto.
28812
+ */
28813
+ var RecordingDevicePlacementSchema = object({
28814
+ deviceId: number().int(),
28815
+ profile: string(),
28816
+ locationId: string()
28817
+ });
28818
+ var RecordingDevicePinSchema = object({
28819
+ deviceId: number().int(),
28820
+ /** Recordings-class location this camera is pinned to. */
28821
+ locationId: string()
28822
+ });
28823
+ var RecordingPlacementViewSchema = object({
28824
+ assignments: array(RecordingDevicePlacementSchema),
28825
+ pins: array(RecordingDevicePinSchema),
28826
+ defaultLocations: record(string(), string())
28827
+ });
28828
+ var RecordingSetDevicePlacementInputSchema = object({
28829
+ deviceId: number().int(),
28830
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
28831
+ locationId: string().nullable()
28832
+ });
28833
+ /**
28594
28834
  * Result of locating footage at a wall-clock instant for one device/profile.
28595
28835
  * `segment` carries the covering segment's window; `gap` reports the forward
28596
28836
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -28804,6 +29044,9 @@ method(object({
28804
29044
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
28805
29045
  kind: "query",
28806
29046
  auth: "admin"
29047
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
29048
+ kind: "mutation",
29049
+ auth: "admin"
28807
29050
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
28808
29051
  kind: "mutation",
28809
29052
  auth: "admin"
@@ -28813,6 +29056,12 @@ method(object({
28813
29056
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
28814
29057
  kind: "mutation",
28815
29058
  auth: "admin"
29059
+ }), method(object({}), RecordingPlacementViewSchema, {
29060
+ kind: "query",
29061
+ auth: "admin"
29062
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
29063
+ kind: "mutation",
29064
+ auth: "admin"
28816
29065
  });
28817
29066
  /**
28818
29067
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -29195,6 +29444,25 @@ var SceneMonitorStatusSchema = object({
29195
29444
  monitors: array(SceneMonitorSchema),
29196
29445
  lastFetchedAt: number()
29197
29446
  });
29447
+ /**
29448
+ * One camera's row in a `listScenesBatch` answer.
29449
+ *
29450
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
29451
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
29452
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
29453
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
29454
+ * configured. Fanned out per camera the difference was visible — one query
29455
+ * errored while the others resolved — and a batch that returned only the rows
29456
+ * it managed would have destroyed it, silently, by making an unreachable camera
29457
+ * indistinguishable from one that answered `monitors: []`.
29458
+ *
29459
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
29460
+ * could not be read"; `status.monitors: []` means "read, and it has none".
29461
+ */
29462
+ var SceneMonitorStatusForDeviceSchema = object({
29463
+ deviceId: number(),
29464
+ status: SceneMonitorStatusSchema.nullable()
29465
+ });
29198
29466
  var sceneMonitorCapability = {
29199
29467
  name: "scene-monitor",
29200
29468
  scope: "device",
@@ -29204,6 +29472,22 @@ var sceneMonitorCapability = {
29204
29472
  deviceTypes: [DeviceType.Camera],
29205
29473
  methods: {
29206
29474
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
29475
+ /**
29476
+ * The same answer, for a SET of cameras, in one round trip.
29477
+ *
29478
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
29479
+ * poll behind the push slice. Fanned out client-side that was one query
29480
+ * per camera — 29 round trips through the browser, the hub and the
29481
+ * post-analysis runner every 30 seconds to read an in-memory map the
29482
+ * owner had already merged. The work is unchanged (`statusFor` per
29483
+ * device, all in-process at the owner); what collapses is the transport.
29484
+ *
29485
+ * A camera that cannot answer still gets a row, with `status: null` —
29486
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
29487
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
29488
+ * tell which two are missing, or that any are.
29489
+ */
29490
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
29207
29491
  createScene: method(object({
29208
29492
  deviceId: number(),
29209
29493
  label: string(),
@@ -31039,6 +31323,27 @@ var CameraOccupancySnapshotSchema = object({
31039
31323
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
31040
31324
  });
31041
31325
  /**
31326
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
31327
+ *
31328
+ * THREE outcomes, and the single-camera method could only express two of them
31329
+ * because `snapshot: null` was already spoken for:
31330
+ *
31331
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
31332
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
31333
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
31334
+ * - `read: 'unreadable'` — the owner could not answer for this
31335
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
31336
+ *
31337
+ * Collapsing the last two is the failure this field exists to prevent: a
31338
+ * hydration that threw would otherwise render as an empty Stationary section,
31339
+ * which is a definite claim about a camera nobody could read.
31340
+ */
31341
+ var CameraOccupancySnapshotForDeviceSchema = object({
31342
+ deviceId: number(),
31343
+ read: _enum(["read", "unreadable"]),
31344
+ snapshot: CameraOccupancySnapshotSchema.nullable()
31345
+ });
31346
+ /**
31042
31347
  * Time-series resolution. The history methods return one bucket per
31043
31348
  * step over the requested range. Smaller resolutions cost more
31044
31349
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -31095,6 +31400,20 @@ var zoneAnalyticsCapability = {
31095
31400
  * (no inference result emitted since boot or since binding was
31096
31401
  * activated). */
31097
31402
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
31403
+ /**
31404
+ * The same snapshot, for a SET of cameras, in one round trip.
31405
+ *
31406
+ * The Events page's Stationary section polls this every 15s for every
31407
+ * selected camera. Fanned out client-side that is one query per camera to
31408
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
31409
+ * costs everything. Batched, N transports become one and the per-device
31410
+ * work is unchanged.
31411
+ *
31412
+ * Every requested deviceId gets a row, tagged `read` — see
31413
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
31414
+ * not answer for is `'unreadable'`, never an empty reading.
31415
+ */
31416
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
31098
31417
  /** Time-series object count inside one zone. `className` optional —
31099
31418
  * omit to count every class in the zone. */
31100
31419
  getZoneHistory: method(object({
@@ -33878,6 +34197,12 @@ Object.freeze({
33878
34197
  addonId: null,
33879
34198
  access: "delete"
33880
34199
  },
34200
+ "deviceManager.renameLocation": {
34201
+ capName: "device-manager",
34202
+ capScope: "system",
34203
+ addonId: null,
34204
+ access: "create"
34205
+ },
33881
34206
  "deviceManager.runDeviceAction": {
33882
34207
  capName: "device-manager",
33883
34208
  capScope: "system",
@@ -35570,13 +35895,19 @@ Object.freeze({
35570
35895
  addonId: null,
35571
35896
  access: "view"
35572
35897
  },
35573
- "pipelineAnalytics.getGroup": {
35898
+ "pipelineAnalytics.getKeyEvents": {
35574
35899
  capName: "pipeline-analytics",
35575
35900
  capScope: "device",
35576
35901
  addonId: null,
35577
35902
  access: "view"
35578
35903
  },
35579
- "pipelineAnalytics.getKeyEvents": {
35904
+ "pipelineAnalytics.getKeyEventsBatch": {
35905
+ capName: "pipeline-analytics",
35906
+ capScope: "device",
35907
+ addonId: null,
35908
+ access: "view"
35909
+ },
35910
+ "pipelineAnalytics.getMediaReclaimStatus": {
35580
35911
  capName: "pipeline-analytics",
35581
35912
  capScope: "device",
35582
35913
  addonId: null,
@@ -35666,12 +35997,6 @@ Object.freeze({
35666
35997
  addonId: null,
35667
35998
  access: "view"
35668
35999
  },
35669
- "pipelineAnalytics.listGroups": {
35670
- capName: "pipeline-analytics",
35671
- capScope: "device",
35672
- addonId: null,
35673
- access: "view"
35674
- },
35675
36000
  "pipelineAnalytics.listOpsLog": {
35676
36001
  capName: "pipeline-analytics",
35677
36002
  capScope: "device",
@@ -35756,6 +36081,12 @@ Object.freeze({
35756
36081
  addonId: null,
35757
36082
  access: "create"
35758
36083
  },
36084
+ "pipelineAnalytics.reclaimDebugMedia": {
36085
+ capName: "pipeline-analytics",
36086
+ capScope: "device",
36087
+ addonId: null,
36088
+ access: "create"
36089
+ },
35759
36090
  "pipelineAnalytics.reconcileFromDisk": {
35760
36091
  capName: "pipeline-analytics",
35761
36092
  capScope: "device",
@@ -36680,6 +37011,12 @@ Object.freeze({
36680
37011
  addonId: null,
36681
37012
  access: "view"
36682
37013
  },
37014
+ "recording.getPlacement": {
37015
+ capName: "recording",
37016
+ capScope: "system",
37017
+ addonId: null,
37018
+ access: "view"
37019
+ },
36683
37020
  "recording.getPlaybackManifest": {
36684
37021
  capName: "recording",
36685
37022
  capScope: "system",
@@ -36758,6 +37095,12 @@ Object.freeze({
36758
37095
  addonId: null,
36759
37096
  access: "view"
36760
37097
  },
37098
+ "recording.reconcileLedgerAgainstDisk": {
37099
+ capName: "recording",
37100
+ capScope: "system",
37101
+ addonId: null,
37102
+ access: "create"
37103
+ },
36761
37104
  "recording.refreshStorageLocationsForMigration": {
36762
37105
  capName: "recording",
36763
37106
  capScope: "system",
@@ -36800,6 +37143,12 @@ Object.freeze({
36800
37143
  addonId: null,
36801
37144
  access: "create"
36802
37145
  },
37146
+ "recording.setDevicePlacement": {
37147
+ capName: "recording",
37148
+ capScope: "system",
37149
+ addonId: null,
37150
+ access: "create"
37151
+ },
36803
37152
  "recording.startStorageMigrationMove": {
36804
37153
  capName: "recording",
36805
37154
  capScope: "system",
@@ -36884,6 +37233,12 @@ Object.freeze({
36884
37233
  addonId: null,
36885
37234
  access: "view"
36886
37235
  },
37236
+ "sceneMonitor.listScenesBatch": {
37237
+ capName: "scene-monitor",
37238
+ capScope: "device",
37239
+ addonId: null,
37240
+ access: "view"
37241
+ },
36887
37242
  "sceneMonitor.recheckNow": {
36888
37243
  capName: "scene-monitor",
36889
37244
  capScope: "device",
@@ -37238,12 +37593,36 @@ Object.freeze({
37238
37593
  addonId: null,
37239
37594
  access: "create"
37240
37595
  },
37596
+ "storageMigration.cleanupCancel": {
37597
+ capName: "storage-migration",
37598
+ capScope: "system",
37599
+ addonId: null,
37600
+ access: "create"
37601
+ },
37602
+ "storageMigration.cleanupStart": {
37603
+ capName: "storage-migration",
37604
+ capScope: "system",
37605
+ addonId: null,
37606
+ access: "create"
37607
+ },
37608
+ "storageMigration.cleanupStatus": {
37609
+ capName: "storage-migration",
37610
+ capScope: "system",
37611
+ addonId: null,
37612
+ access: "view"
37613
+ },
37241
37614
  "storageMigration.drain": {
37242
37615
  capName: "storage-migration",
37243
37616
  capScope: "system",
37244
37617
  addonId: null,
37245
37618
  access: "create"
37246
37619
  },
37620
+ "storageMigration.history": {
37621
+ capName: "storage-migration",
37622
+ capScope: "system",
37623
+ addonId: null,
37624
+ access: "view"
37625
+ },
37247
37626
  "storageMigration.movers": {
37248
37627
  capName: "storage-migration",
37249
37628
  capScope: "system",
@@ -38240,6 +38619,12 @@ Object.freeze({
38240
38619
  addonId: null,
38241
38620
  access: "view"
38242
38621
  },
38622
+ "zoneAnalytics.getCurrentSnapshotBatch": {
38623
+ capName: "zone-analytics",
38624
+ capScope: "device",
38625
+ addonId: null,
38626
+ access: "view"
38627
+ },
38243
38628
  "zoneAnalytics.getUnzonedHistory": {
38244
38629
  capName: "zone-analytics",
38245
38630
  capScope: "device",
@@ -39234,14 +39619,14 @@ Object.freeze({
39234
39619
  form: "single",
39235
39620
  optional: true
39236
39621
  }],
39237
- "pipelineAnalytics.getGroup": [{
39622
+ "pipelineAnalytics.getKeyEvents": [{
39238
39623
  name: "deviceId",
39239
39624
  form: "single",
39240
39625
  optional: false
39241
39626
  }],
39242
- "pipelineAnalytics.getKeyEvents": [{
39243
- name: "deviceId",
39244
- form: "single",
39627
+ "pipelineAnalytics.getKeyEventsBatch": [{
39628
+ name: "deviceIds",
39629
+ form: "array",
39245
39630
  optional: false
39246
39631
  }],
39247
39632
  "pipelineAnalytics.getMotionEvents": [{
@@ -39299,11 +39684,6 @@ Object.freeze({
39299
39684
  form: "single",
39300
39685
  optional: false
39301
39686
  }],
39302
- "pipelineAnalytics.listGroups": [{
39303
- name: "deviceIds",
39304
- form: "array",
39305
- optional: false
39306
- }],
39307
39687
  "pipelineAnalytics.listOpsLog": [{
39308
39688
  name: "deviceId",
39309
39689
  form: "single",
@@ -39349,6 +39729,11 @@ Object.freeze({
39349
39729
  form: "single",
39350
39730
  optional: true
39351
39731
  }],
39732
+ "pipelineAnalytics.reclaimDebugMedia": [{
39733
+ name: "deviceIds",
39734
+ form: "array",
39735
+ optional: true
39736
+ }],
39352
39737
  "pipelineAnalytics.reconcileFromDisk": [{
39353
39738
  name: "deviceId",
39354
39739
  form: "single",
@@ -39684,6 +40069,11 @@ Object.freeze({
39684
40069
  form: "single",
39685
40070
  optional: false
39686
40071
  }],
40072
+ "recording.reconcileLedgerAgainstDisk": [{
40073
+ name: "deviceId",
40074
+ form: "single",
40075
+ optional: true
40076
+ }],
39687
40077
  "recording.relocateFootage": [{
39688
40078
  name: "deviceId",
39689
40079
  form: "single",
@@ -39709,6 +40099,11 @@ Object.freeze({
39709
40099
  form: "single",
39710
40100
  optional: false
39711
40101
  }],
40102
+ "recording.setDevicePlacement": [{
40103
+ name: "deviceId",
40104
+ form: "single",
40105
+ optional: false
40106
+ }],
39712
40107
  "recording.startStorageMigrationMove": [{
39713
40108
  name: "deviceId",
39714
40109
  form: "single",
@@ -39749,6 +40144,11 @@ Object.freeze({
39749
40144
  form: "single",
39750
40145
  optional: false
39751
40146
  }],
40147
+ "sceneMonitor.listScenesBatch": [{
40148
+ name: "deviceIds",
40149
+ form: "array",
40150
+ optional: false
40151
+ }],
39752
40152
  "sceneMonitor.recheckNow": [{
39753
40153
  name: "deviceId",
39754
40154
  form: "single",
@@ -40010,6 +40410,11 @@ Object.freeze({
40010
40410
  form: "single",
40011
40411
  optional: false
40012
40412
  }],
40413
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
40414
+ name: "deviceIds",
40415
+ form: "array",
40416
+ optional: false
40417
+ }],
40013
40418
  "zoneAnalytics.getUnzonedHistory": [{
40014
40419
  name: "deviceId",
40015
40420
  form: "single",