@camstack/addon-remote-storage 1.2.47 → 1.2.49

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.
@@ -8003,6 +8003,20 @@ var RelocateJobSchema = object({
8003
8003
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8004
8004
  */
8005
8005
  rowsReconciled: number().int().nonnegative().optional(),
8006
+ /**
8007
+ * Rows this run FORGOT because the file they name is not on disk.
8008
+ *
8009
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8010
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8011
+ * and the durable row is dropped through the same channel eviction uses. It
8012
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8013
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8014
+ * the same failure as one that quietly skips them (D295).
8015
+ *
8016
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8017
+ * ledger claimed 5.65 GB of footage that no longer existed.
8018
+ */
8019
+ rowsForgotten: number().int().nonnegative().optional(),
8006
8020
  startedAt: number(),
8007
8021
  finishedAt: number().nullable(),
8008
8022
  error: string().nullable()
@@ -8067,6 +8081,13 @@ var MediaRelocateModeSchema = _enum([
8067
8081
  ]);
8068
8082
  var RelocateMediaInputSchema = object({
8069
8083
  toLocationId: string(),
8084
+ /**
8085
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8086
+ * every row that is not already on `toLocationId` (the historical
8087
+ * behaviour). A named source is what a from→to migration needs: without it
8088
+ * "move events off disk 2" also emptied disk 1.
8089
+ */
8090
+ fromLocationId: string().optional(),
8070
8091
  throttleMbps: number().min(1).max(1e3).optional(),
8071
8092
  /** Omitted = `move`, the pre-existing behaviour. */
8072
8093
  mode: MediaRelocateModeSchema.optional()
@@ -8134,6 +8155,19 @@ var StorageMigrationDestinationsSchema = object({
8134
8155
  galleryMedia: string().min(1).optional()
8135
8156
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8136
8157
  /**
8158
+ * Optional named source per class. Omitted = the class's current default
8159
+ * (the historical behaviour). A named source that is NOT the default is a
8160
+ * drain of that disk: bytes move, the default stays, and the source is
8161
+ * disabled when the move finishes.
8162
+ */
8163
+ var StorageMigrationSourcesSchema = object({
8164
+ recordings: string().min(1).optional(),
8165
+ recordingsLow: string().min(1).optional(),
8166
+ eventMedia: string().min(1).optional(),
8167
+ backups: string().min(1).optional(),
8168
+ galleryMedia: string().min(1).optional()
8169
+ }).optional();
8170
+ /**
8137
8171
  * How a migration sequences the cutover against the byte move.
8138
8172
  *
8139
8173
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8155,6 +8189,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8155
8189
  /** Shared input for planning and starting an orchestrated storage migration. */
8156
8190
  var StorageMigrationInputSchema = object({
8157
8191
  destinations: StorageMigrationDestinationsSchema,
8192
+ /** Omitted = each class's current default. */
8193
+ sources: StorageMigrationSourcesSchema,
8158
8194
  throttleMbps: number().min(1).max(1e3).optional(),
8159
8195
  /** Omitted = `blocking`, which stays the default. */
8160
8196
  mode: StorageMigrationModeSchema.optional()
@@ -8234,6 +8270,13 @@ var StorageMigrationMoveSchema = object({
8234
8270
  storageClass: StorageMigrationClassSchema,
8235
8271
  fromLocationId: string(),
8236
8272
  toLocationId: string(),
8273
+ /**
8274
+ * True when `from` was NOT the class default at plan time. The move still
8275
+ * copies bytes, but the default is left alone and the source is disabled
8276
+ * once the copy verifies. Absent on jobs planned before this field existed
8277
+ * — those jobs always repointed, which is `false`.
8278
+ */
8279
+ freezeSource: boolean().optional(),
8237
8280
  moverJobId: string().nullable(),
8238
8281
  state: RelocateJobStateSchema.nullable(),
8239
8282
  error: string().nullable(),
@@ -8247,6 +8290,7 @@ var StorageMigrationJobSchema = object({
8247
8290
  * can tell a seconds-long cutover from a thirty-hour one. */
8248
8291
  mode: StorageMigrationModeSchema,
8249
8292
  destinations: StorageMigrationDestinationsSchema,
8293
+ sources: StorageMigrationSourcesSchema,
8250
8294
  throttleMbps: number(),
8251
8295
  moves: array(StorageMigrationMoveSchema),
8252
8296
  pauseLeaseId: string().nullable(),
@@ -8272,6 +8316,7 @@ var StorageMigrationFindingSchema = object({
8272
8316
  });
8273
8317
  var StorageMigrationPlanSchema = object({
8274
8318
  destinations: StorageMigrationDestinationsSchema,
8319
+ sources: StorageMigrationSourcesSchema,
8275
8320
  /** The mode this plan was built for. A plan is only valid for its mode: the
8276
8321
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8277
8322
  * it. */
@@ -8279,7 +8324,8 @@ var StorageMigrationPlanSchema = object({
8279
8324
  moves: array(object({
8280
8325
  storageClass: StorageMigrationClassSchema,
8281
8326
  fromLocationId: string(),
8282
- toLocationId: string()
8327
+ toLocationId: string(),
8328
+ freezeSource: boolean().optional()
8283
8329
  })),
8284
8330
  findings: array(StorageMigrationFindingSchema)
8285
8331
  });
@@ -8366,16 +8412,142 @@ var RelocateResidueSchema = object({
8366
8412
  segments: number().int().nonnegative(),
8367
8413
  bytes: number().int().nonnegative()
8368
8414
  }).nullable();
8415
+ /**
8416
+ * Ask one location whether its durable hour rows describe the disk — the walk
8417
+ * (D319).
8418
+ *
8419
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8420
+ * missing tool is the question, and the dry run is how they sanity-check the
8421
+ * destructive run before authorising it.
8422
+ */
8423
+ var LedgerWalkInputSchema = object({
8424
+ locationId: string().min(1),
8425
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8426
+ apply: boolean().optional(),
8427
+ /** Narrow to one camera. */
8428
+ deviceId: number().int().positive().optional(),
8429
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8430
+ profiles: array(string().min(1)).optional()
8431
+ });
8432
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8433
+ var LedgerWalkRefusalSchema = _enum([
8434
+ "location-unknown",
8435
+ "source-writable",
8436
+ "no-ledger",
8437
+ "archive-unreadable",
8438
+ "anchor-absent",
8439
+ "anchor-unreadable",
8440
+ "anchor-moved"
8441
+ ]);
8442
+ _enum([
8443
+ "live-tail",
8444
+ "listing-error",
8445
+ "path-mismatch",
8446
+ "durable-refused"
8447
+ ]);
8448
+ /** Every skip reason, always present, always a number — so a reason that never
8449
+ * fired reports as zero rather than absent and the report shape is constant
8450
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8451
+ var LedgerWalkSkipCountsSchema = object({
8452
+ "live-tail": number().int().nonnegative(),
8453
+ "listing-error": number().int().nonnegative(),
8454
+ "path-mismatch": number().int().nonnegative(),
8455
+ "durable-refused": number().int().nonnegative()
8456
+ });
8457
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8458
+ var LedgerWalkDeviceReportSchema = object({
8459
+ deviceId: number().int(),
8460
+ hoursWalked: number().int().nonnegative(),
8461
+ hoursMissing: number().int().nonnegative(),
8462
+ ghostSegments: number().int().nonnegative(),
8463
+ ghostBytes: number().int().nonnegative(),
8464
+ forgottenSegments: number().int().nonnegative(),
8465
+ orphanFiles: number().int().nonnegative()
8466
+ });
8467
+ /**
8468
+ * What one walk claimed, listed, found and (only when armed) forgot.
8469
+ *
8470
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8471
+ * walk that saw a fraction of the location is visible in its own report rather
8472
+ * than in the absence of one.
8473
+ */
8474
+ var LedgerWalkReportSchema = object({
8475
+ locationId: string(),
8476
+ applied: boolean(),
8477
+ refused: LedgerWalkRefusalSchema.nullable(),
8478
+ archiveSegments: number().int().nonnegative().nullable(),
8479
+ archiveBytes: number().int().nonnegative().nullable(),
8480
+ hoursClaimed: number().int().nonnegative(),
8481
+ hoursWalked: number().int().nonnegative(),
8482
+ hoursMissing: number().int().nonnegative(),
8483
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8484
+ listings: number().int().nonnegative(),
8485
+ segmentsClaimed: number().int().nonnegative(),
8486
+ ghostSegments: number().int().nonnegative(),
8487
+ ghostBytes: number().int().nonnegative(),
8488
+ ghostHoursWhole: number().int().nonnegative(),
8489
+ forgottenSegments: number().int().nonnegative(),
8490
+ forgottenBytes: number().int().nonnegative(),
8491
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8492
+ orphanFiles: number().int().nonnegative(),
8493
+ orphanSample: array(string()).readonly(),
8494
+ hoursSkipped: number().int().nonnegative(),
8495
+ skippedByReason: LedgerWalkSkipCountsSchema,
8496
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8497
+ bounded: boolean(),
8498
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8499
+ });
8369
8500
  /** How many rows a media pass would still act on against a given target — the
8370
8501
  * media lane's denominator AND its residue, from ONE derivation so the two can
8371
8502
  * never disagree. `null` = the count could not be taken. */
8372
8503
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8373
8504
  var RelocatableMediaCountInputSchema = object({
8374
8505
  toLocationId: string().min(1),
8506
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8507
+ fromLocationId: string().optional(),
8375
8508
  /** Omitted = `move`. */
8376
8509
  mode: MediaRelocateModeSchema.optional()
8377
8510
  });
8378
8511
  /**
8512
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8513
+ * ghost ledger entries on frozen footage locations.
8514
+ *
8515
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8516
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8517
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8518
+ * with no operator-visible status.
8519
+ */
8520
+ var StorageCleanupPhaseSchema = _enum([
8521
+ "orphans",
8522
+ "debug-media",
8523
+ "ghost-ledger",
8524
+ "done",
8525
+ "failed",
8526
+ "cancelled"
8527
+ ]);
8528
+ var StorageCleanupInputSchema = object({
8529
+ /** Also walk motion stills / track filmstrips. Off by default. */
8530
+ includeDebugMedia: boolean().optional() });
8531
+ var StorageCleanupJobSchema = object({
8532
+ jobId: string(),
8533
+ phase: StorageCleanupPhaseSchema,
8534
+ includeDebugMedia: boolean(),
8535
+ orphansReclaimed: number().int().nonnegative(),
8536
+ orphanBytesReclaimed: number().int().nonnegative(),
8537
+ debugMediaReclaimed: number().int().nonnegative(),
8538
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8539
+ ghostsForgotten: number().int().nonnegative(),
8540
+ ghostBytesForgotten: number().int().nonnegative(),
8541
+ /** Short operator-facing line: current collection, pass, or location. */
8542
+ detail: string().nullable(),
8543
+ cancelRequested: boolean(),
8544
+ startedAt: number(),
8545
+ updatedAt: number(),
8546
+ finishedAt: number().nullable(),
8547
+ error: string().nullable()
8548
+ });
8549
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8550
+ /**
8379
8551
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8380
8552
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8381
8553
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8403,11 +8575,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8403
8575
  * The default location for a type uses `id === <type>:default` by
8404
8576
  * convention (the bare type ref like `'backups'` resolves to it).
8405
8577
  *
8406
- * `isSystem: true` marks a location as orchestrator-seeded and
8407
- * undeletable. The bootstrap-installed defaults (one per type) carry
8408
- * this flag; operator-added locations don't. Editing the config of
8409
- * a system location is allowed (path migration, provider swap) but
8410
- * deleting it is rejected at the cap level.
8578
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8579
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8580
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8581
+ * / last-enabled, not on this bit.
8411
8582
  */
8412
8583
  var StorageLocationSchema = object({
8413
8584
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12669,6 +12840,12 @@ method(object({
12669
12840
  }), _void(), {
12670
12841
  kind: "mutation",
12671
12842
  auth: "admin"
12843
+ }), method(object({
12844
+ from: string(),
12845
+ to: string()
12846
+ }), object({ moved: number() }), {
12847
+ kind: "mutation",
12848
+ auth: "admin"
12672
12849
  }), method(object({
12673
12850
  deviceId: number(),
12674
12851
  disabled: boolean()
@@ -18599,53 +18776,15 @@ var RecentTracksPageSchema = object({
18599
18776
  /** Cursor for the next page, or null when this page is the last. */
18600
18777
  nextCursor: string().nullable()
18601
18778
  });
18602
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18603
- var LIST_GROUPS_MAX_LIMIT = 100;
18604
- var AnalyticsGroupRecordSchema = object({
18605
- id: string(),
18606
- deviceId: number().int(),
18607
- openedAt: number().int(),
18608
- closedAt: number().int(),
18609
- timestamp: number().int(),
18610
- memberCount: number().int(),
18611
- memberTrackIds: array(string()).readonly(),
18612
- className: string(),
18613
- classes: array(string()).readonly(),
18614
- /** Relative event-media path, or null when the group has no picture yet. */
18615
- mediaUrl: string().nullable(),
18616
- singleton: boolean()
18617
- });
18618
- var AnalyticsGroupMemberSchema = object({
18619
- trackId: string(),
18620
- deviceId: number().int(),
18621
- className: string(),
18622
- firstSeen: number().int(),
18623
- lastSeen: number().int(),
18624
- mediaUrl: string().nullable()
18625
- });
18626
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18627
- var ListGroupsQueryInput = object({
18628
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18629
- deviceIds: array(number()),
18630
- /** Window lower bound on `closedAt` (inclusive). */
18631
- since: number().optional(),
18632
- /** Window upper bound on `openedAt` (inclusive). */
18633
- until: number().optional(),
18634
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18635
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
18636
- cursor: string().optional()
18637
- });
18638
- var ListGroupsPageSchema = object({
18639
- groups: array(AnalyticsGroupRecordSchema).readonly(),
18640
- nextCursor: string().nullable()
18641
- });
18779
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
18780
+ var KEY_EVENTS_MAX_LIMIT = 200;
18642
18781
  var KeyEventQueryInput = object({
18643
18782
  deviceId: number(),
18644
18783
  /** Window lower bound (track firstSeen ≥ since). */
18645
18784
  since: number(),
18646
18785
  /** Window upper bound (track firstSeen ≤ until). */
18647
18786
  until: number(),
18648
- limit: number().int().min(1).max(200).default(50),
18787
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18649
18788
  /** Drop tracks scoring below this importance. */
18650
18789
  minImportance: number().min(0).max(1).optional(),
18651
18790
  /** Restrict to a single class (e.g. 'person'). */
@@ -18667,6 +18806,32 @@ var KeyEventSchema = object({
18667
18806
  ...TrackFlagFields,
18668
18807
  ...TrackRetrainFields
18669
18808
  });
18809
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
18810
+ var KeyEventBatchQueryInput = object({
18811
+ deviceIds: array(number()).min(1).max(200),
18812
+ since: number(),
18813
+ until: number(),
18814
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
18815
+ * across the set, which would let a busy camera starve a quiet one of its
18816
+ * rows and change what the merged feed contains. */
18817
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18818
+ minImportance: number().min(0).max(1).optional(),
18819
+ classFilter: string().optional()
18820
+ });
18821
+ /**
18822
+ * One camera's key events in a batch answer.
18823
+ *
18824
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
18825
+ * error rather than throwing, so a camera whose store read failed and one with
18826
+ * no events in the window were ALREADY indistinguishable per camera — the
18827
+ * batch does not make that worse, and the row keeps the deviceId the single
18828
+ * method's output never carried (the caller used to stamp it from the fan-out
18829
+ * key, which only worked because there was one query per camera).
18830
+ */
18831
+ var KeyEventsForDeviceSchema = object({
18832
+ deviceId: number(),
18833
+ events: array(KeyEventSchema).readonly()
18834
+ });
18670
18835
  object({
18671
18836
  trackId: string(),
18672
18837
  className: string(),
@@ -18714,9 +18879,7 @@ var TrackCascadeCountsSchema = object({
18714
18879
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18715
18880
  plates: number().int(),
18716
18881
  /** Per-track CLIP search vectors removed (best-effort). */
18717
- embeddings: number().int(),
18718
- /** Group membership + group rows removed with their last member (best-effort). */
18719
- groups: number().int()
18882
+ embeddings: number().int()
18720
18883
  });
18721
18884
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18722
18885
  var DiskReconcileCountsSchema = object({
@@ -18886,6 +19049,47 @@ var RebuildStatusSchema = object({
18886
19049
  /** Present when the pass ended by throwing. */
18887
19050
  error: string().nullable()
18888
19051
  });
19052
+ /**
19053
+ * Acknowledgement that a debug-media reclaim STARTED.
19054
+ *
19055
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19056
+ * it runs detached and this returns immediately. Awaiting it is how the
19057
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19058
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19059
+ */
19060
+ var MediaReclaimStartResultSchema = object({
19061
+ started: boolean(),
19062
+ /** True when a pass was already running; the new request is ignored. */
19063
+ alreadyRunning: boolean()
19064
+ });
19065
+ var MediaReclaimInputSchema = object({
19066
+ mode: _enum(["report", "reclaim"]).default("report"),
19067
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19068
+ deviceIds: array(number().int()).min(1).optional(),
19069
+ restart: boolean().optional(),
19070
+ pageSize: number().int().min(50).max(5e3).optional(),
19071
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19072
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19073
+ maxBytesPerRun: number().int().min(1).optional(),
19074
+ budgetMinutes: number().int().min(1).max(720).optional(),
19075
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19076
+ graceMinutes: number().int().min(1).max(10080).optional()
19077
+ });
19078
+ var MediaReclaimStatusSchema = object({
19079
+ running: boolean(),
19080
+ mode: _enum(["report", "reclaim"]).nullable(),
19081
+ totalExamined: number(),
19082
+ totalEligible: number(),
19083
+ totalReclaimed: number(),
19084
+ totalBytesReclaimed: number(),
19085
+ totalRefused: number(),
19086
+ /** Device+scope windows finished in this pass. */
19087
+ devicesDone: number(),
19088
+ complete: boolean().nullable(),
19089
+ startedAtMs: number().nullable(),
19090
+ finishedAtMs: number().nullable(),
19091
+ error: string().nullable()
19092
+ });
18889
19093
  var ReplayFrameInputSchema = object({
18890
19094
  timestamp: number(),
18891
19095
  frame: PipelineRunResultBridge
@@ -18924,10 +19128,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18924
19128
  * stationary registry). Default false: the timeline lists passages,
18925
19129
  * not parking records (operator decision, 2026-08-15). */
18926
19130
  includeStationary: boolean().optional()
18927
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18928
- deviceId: number(),
18929
- groupId: string().min(1)
18930
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19131
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18931
19132
  kind: "mutation",
18932
19133
  auth: "admin"
18933
19134
  }), 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({
@@ -18936,7 +19137,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18936
19137
  until: number().optional(),
18937
19138
  kinds: array(string()).optional(),
18938
19139
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18939
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19140
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
18940
19141
  deviceId: number(),
18941
19142
  since: number(),
18942
19143
  until: number(),
@@ -19027,6 +19228,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19027
19228
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19028
19229
  kind: "query",
19029
19230
  auth: "admin"
19231
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19232
+ kind: "mutation",
19233
+ auth: "admin"
19234
+ }), method(object({}), MediaReclaimStatusSchema, {
19235
+ kind: "query",
19236
+ auth: "admin"
19030
19237
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19031
19238
  kind: "query",
19032
19239
  auth: "admin"
@@ -20997,7 +21204,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
20997
21204
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
20998
21205
  kind: "mutation",
20999
21206
  auth: "admin"
21000
- });
21207
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21208
+ kind: "mutation",
21209
+ auth: "admin"
21210
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21211
+ kind: "mutation",
21212
+ auth: "admin"
21213
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21001
21214
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21002
21215
  providerId: string().min(1),
21003
21216
  displayName: string().min(1),
@@ -26435,6 +26648,33 @@ var RecordingRebalanceInputSchema = object({
26435
26648
  minMoveGb: number().min(0).optional()
26436
26649
  });
26437
26650
  /**
26651
+ * Operator-facing placement of one camera onto a recordings location.
26652
+ *
26653
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26654
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26655
+ * currently write — the plan, which may disagree with the pin when Auto.
26656
+ */
26657
+ var RecordingDevicePlacementSchema = object({
26658
+ deviceId: number().int(),
26659
+ profile: string(),
26660
+ locationId: string()
26661
+ });
26662
+ var RecordingDevicePinSchema = object({
26663
+ deviceId: number().int(),
26664
+ /** Recordings-class location this camera is pinned to. */
26665
+ locationId: string()
26666
+ });
26667
+ var RecordingPlacementViewSchema = object({
26668
+ assignments: array(RecordingDevicePlacementSchema),
26669
+ pins: array(RecordingDevicePinSchema),
26670
+ defaultLocations: record(string(), string())
26671
+ });
26672
+ var RecordingSetDevicePlacementInputSchema = object({
26673
+ deviceId: number().int(),
26674
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26675
+ locationId: string().nullable()
26676
+ });
26677
+ /**
26438
26678
  * Result of locating footage at a wall-clock instant for one device/profile.
26439
26679
  * `segment` carries the covering segment's window; `gap` reports the forward
26440
26680
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26648,6 +26888,9 @@ method(object({
26648
26888
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26649
26889
  kind: "query",
26650
26890
  auth: "admin"
26891
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26892
+ kind: "mutation",
26893
+ auth: "admin"
26651
26894
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26652
26895
  kind: "mutation",
26653
26896
  auth: "admin"
@@ -26657,6 +26900,12 @@ method(object({
26657
26900
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26658
26901
  kind: "mutation",
26659
26902
  auth: "admin"
26903
+ }), method(object({}), RecordingPlacementViewSchema, {
26904
+ kind: "query",
26905
+ auth: "admin"
26906
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
26907
+ kind: "mutation",
26908
+ auth: "admin"
26660
26909
  });
26661
26910
  /**
26662
26911
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27039,7 +27288,26 @@ var SceneMonitorStatusSchema = object({
27039
27288
  monitors: array(SceneMonitorSchema),
27040
27289
  lastFetchedAt: number()
27041
27290
  });
27042
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27291
+ /**
27292
+ * One camera's row in a `listScenesBatch` answer.
27293
+ *
27294
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27295
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27296
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27297
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27298
+ * configured. Fanned out per camera the difference was visible — one query
27299
+ * errored while the others resolved — and a batch that returned only the rows
27300
+ * it managed would have destroyed it, silently, by making an unreachable camera
27301
+ * indistinguishable from one that answered `monitors: []`.
27302
+ *
27303
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27304
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27305
+ */
27306
+ var SceneMonitorStatusForDeviceSchema = object({
27307
+ deviceId: number(),
27308
+ status: SceneMonitorStatusSchema.nullable()
27309
+ });
27310
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27043
27311
  deviceId: number(),
27044
27312
  label: string(),
27045
27313
  roi: MaskRectShapeSchema,
@@ -28363,6 +28631,27 @@ var CameraOccupancySnapshotSchema = object({
28363
28631
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28364
28632
  });
28365
28633
  /**
28634
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28635
+ *
28636
+ * THREE outcomes, and the single-camera method could only express two of them
28637
+ * because `snapshot: null` was already spoken for:
28638
+ *
28639
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28640
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28641
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28642
+ * - `read: 'unreadable'` — the owner could not answer for this
28643
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28644
+ *
28645
+ * Collapsing the last two is the failure this field exists to prevent: a
28646
+ * hydration that threw would otherwise render as an empty Stationary section,
28647
+ * which is a definite claim about a camera nobody could read.
28648
+ */
28649
+ var CameraOccupancySnapshotForDeviceSchema = object({
28650
+ deviceId: number(),
28651
+ read: _enum(["read", "unreadable"]),
28652
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28653
+ });
28654
+ /**
28366
28655
  * Time-series resolution. The history methods return one bucket per
28367
28656
  * step over the requested range. Smaller resolutions cost more
28368
28657
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28386,7 +28675,7 @@ var HistoryPointSchema = object({
28386
28675
  /** Object count averaged over the bucket (rounded to nearest integer). */
28387
28676
  count: number().int().nonnegative()
28388
28677
  });
28389
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28678
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28390
28679
  deviceId: number(),
28391
28680
  zoneId: string(),
28392
28681
  className: string().optional()
@@ -29987,6 +30276,12 @@ Object.freeze({
29987
30276
  addonId: null,
29988
30277
  access: "delete"
29989
30278
  },
30279
+ "deviceManager.renameLocation": {
30280
+ capName: "device-manager",
30281
+ capScope: "system",
30282
+ addonId: null,
30283
+ access: "create"
30284
+ },
29990
30285
  "deviceManager.runDeviceAction": {
29991
30286
  capName: "device-manager",
29992
30287
  capScope: "system",
@@ -31679,13 +31974,19 @@ Object.freeze({
31679
31974
  addonId: null,
31680
31975
  access: "view"
31681
31976
  },
31682
- "pipelineAnalytics.getGroup": {
31977
+ "pipelineAnalytics.getKeyEvents": {
31683
31978
  capName: "pipeline-analytics",
31684
31979
  capScope: "device",
31685
31980
  addonId: null,
31686
31981
  access: "view"
31687
31982
  },
31688
- "pipelineAnalytics.getKeyEvents": {
31983
+ "pipelineAnalytics.getKeyEventsBatch": {
31984
+ capName: "pipeline-analytics",
31985
+ capScope: "device",
31986
+ addonId: null,
31987
+ access: "view"
31988
+ },
31989
+ "pipelineAnalytics.getMediaReclaimStatus": {
31689
31990
  capName: "pipeline-analytics",
31690
31991
  capScope: "device",
31691
31992
  addonId: null,
@@ -31775,12 +32076,6 @@ Object.freeze({
31775
32076
  addonId: null,
31776
32077
  access: "view"
31777
32078
  },
31778
- "pipelineAnalytics.listGroups": {
31779
- capName: "pipeline-analytics",
31780
- capScope: "device",
31781
- addonId: null,
31782
- access: "view"
31783
- },
31784
32079
  "pipelineAnalytics.listOpsLog": {
31785
32080
  capName: "pipeline-analytics",
31786
32081
  capScope: "device",
@@ -31865,6 +32160,12 @@ Object.freeze({
31865
32160
  addonId: null,
31866
32161
  access: "create"
31867
32162
  },
32163
+ "pipelineAnalytics.reclaimDebugMedia": {
32164
+ capName: "pipeline-analytics",
32165
+ capScope: "device",
32166
+ addonId: null,
32167
+ access: "create"
32168
+ },
31868
32169
  "pipelineAnalytics.reconcileFromDisk": {
31869
32170
  capName: "pipeline-analytics",
31870
32171
  capScope: "device",
@@ -32789,6 +33090,12 @@ Object.freeze({
32789
33090
  addonId: null,
32790
33091
  access: "view"
32791
33092
  },
33093
+ "recording.getPlacement": {
33094
+ capName: "recording",
33095
+ capScope: "system",
33096
+ addonId: null,
33097
+ access: "view"
33098
+ },
32792
33099
  "recording.getPlaybackManifest": {
32793
33100
  capName: "recording",
32794
33101
  capScope: "system",
@@ -32867,6 +33174,12 @@ Object.freeze({
32867
33174
  addonId: null,
32868
33175
  access: "view"
32869
33176
  },
33177
+ "recording.reconcileLedgerAgainstDisk": {
33178
+ capName: "recording",
33179
+ capScope: "system",
33180
+ addonId: null,
33181
+ access: "create"
33182
+ },
32870
33183
  "recording.refreshStorageLocationsForMigration": {
32871
33184
  capName: "recording",
32872
33185
  capScope: "system",
@@ -32909,6 +33222,12 @@ Object.freeze({
32909
33222
  addonId: null,
32910
33223
  access: "create"
32911
33224
  },
33225
+ "recording.setDevicePlacement": {
33226
+ capName: "recording",
33227
+ capScope: "system",
33228
+ addonId: null,
33229
+ access: "create"
33230
+ },
32912
33231
  "recording.startStorageMigrationMove": {
32913
33232
  capName: "recording",
32914
33233
  capScope: "system",
@@ -32993,6 +33312,12 @@ Object.freeze({
32993
33312
  addonId: null,
32994
33313
  access: "view"
32995
33314
  },
33315
+ "sceneMonitor.listScenesBatch": {
33316
+ capName: "scene-monitor",
33317
+ capScope: "device",
33318
+ addonId: null,
33319
+ access: "view"
33320
+ },
32996
33321
  "sceneMonitor.recheckNow": {
32997
33322
  capName: "scene-monitor",
32998
33323
  capScope: "device",
@@ -33347,12 +33672,36 @@ Object.freeze({
33347
33672
  addonId: null,
33348
33673
  access: "create"
33349
33674
  },
33675
+ "storageMigration.cleanupCancel": {
33676
+ capName: "storage-migration",
33677
+ capScope: "system",
33678
+ addonId: null,
33679
+ access: "create"
33680
+ },
33681
+ "storageMigration.cleanupStart": {
33682
+ capName: "storage-migration",
33683
+ capScope: "system",
33684
+ addonId: null,
33685
+ access: "create"
33686
+ },
33687
+ "storageMigration.cleanupStatus": {
33688
+ capName: "storage-migration",
33689
+ capScope: "system",
33690
+ addonId: null,
33691
+ access: "view"
33692
+ },
33350
33693
  "storageMigration.drain": {
33351
33694
  capName: "storage-migration",
33352
33695
  capScope: "system",
33353
33696
  addonId: null,
33354
33697
  access: "create"
33355
33698
  },
33699
+ "storageMigration.history": {
33700
+ capName: "storage-migration",
33701
+ capScope: "system",
33702
+ addonId: null,
33703
+ access: "view"
33704
+ },
33356
33705
  "storageMigration.movers": {
33357
33706
  capName: "storage-migration",
33358
33707
  capScope: "system",
@@ -34349,6 +34698,12 @@ Object.freeze({
34349
34698
  addonId: null,
34350
34699
  access: "view"
34351
34700
  },
34701
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34702
+ capName: "zone-analytics",
34703
+ capScope: "device",
34704
+ addonId: null,
34705
+ access: "view"
34706
+ },
34352
34707
  "zoneAnalytics.getUnzonedHistory": {
34353
34708
  capName: "zone-analytics",
34354
34709
  capScope: "device",
@@ -35343,14 +35698,14 @@ Object.freeze({
35343
35698
  form: "single",
35344
35699
  optional: true
35345
35700
  }],
35346
- "pipelineAnalytics.getGroup": [{
35701
+ "pipelineAnalytics.getKeyEvents": [{
35347
35702
  name: "deviceId",
35348
35703
  form: "single",
35349
35704
  optional: false
35350
35705
  }],
35351
- "pipelineAnalytics.getKeyEvents": [{
35352
- name: "deviceId",
35353
- form: "single",
35706
+ "pipelineAnalytics.getKeyEventsBatch": [{
35707
+ name: "deviceIds",
35708
+ form: "array",
35354
35709
  optional: false
35355
35710
  }],
35356
35711
  "pipelineAnalytics.getMotionEvents": [{
@@ -35408,11 +35763,6 @@ Object.freeze({
35408
35763
  form: "single",
35409
35764
  optional: false
35410
35765
  }],
35411
- "pipelineAnalytics.listGroups": [{
35412
- name: "deviceIds",
35413
- form: "array",
35414
- optional: false
35415
- }],
35416
35766
  "pipelineAnalytics.listOpsLog": [{
35417
35767
  name: "deviceId",
35418
35768
  form: "single",
@@ -35458,6 +35808,11 @@ Object.freeze({
35458
35808
  form: "single",
35459
35809
  optional: true
35460
35810
  }],
35811
+ "pipelineAnalytics.reclaimDebugMedia": [{
35812
+ name: "deviceIds",
35813
+ form: "array",
35814
+ optional: true
35815
+ }],
35461
35816
  "pipelineAnalytics.reconcileFromDisk": [{
35462
35817
  name: "deviceId",
35463
35818
  form: "single",
@@ -35793,6 +36148,11 @@ Object.freeze({
35793
36148
  form: "single",
35794
36149
  optional: false
35795
36150
  }],
36151
+ "recording.reconcileLedgerAgainstDisk": [{
36152
+ name: "deviceId",
36153
+ form: "single",
36154
+ optional: true
36155
+ }],
35796
36156
  "recording.relocateFootage": [{
35797
36157
  name: "deviceId",
35798
36158
  form: "single",
@@ -35818,6 +36178,11 @@ Object.freeze({
35818
36178
  form: "single",
35819
36179
  optional: false
35820
36180
  }],
36181
+ "recording.setDevicePlacement": [{
36182
+ name: "deviceId",
36183
+ form: "single",
36184
+ optional: false
36185
+ }],
35821
36186
  "recording.startStorageMigrationMove": [{
35822
36187
  name: "deviceId",
35823
36188
  form: "single",
@@ -35858,6 +36223,11 @@ Object.freeze({
35858
36223
  form: "single",
35859
36224
  optional: false
35860
36225
  }],
36226
+ "sceneMonitor.listScenesBatch": [{
36227
+ name: "deviceIds",
36228
+ form: "array",
36229
+ optional: false
36230
+ }],
35861
36231
  "sceneMonitor.recheckNow": [{
35862
36232
  name: "deviceId",
35863
36233
  form: "single",
@@ -36119,6 +36489,11 @@ Object.freeze({
36119
36489
  form: "single",
36120
36490
  optional: false
36121
36491
  }],
36492
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36493
+ name: "deviceIds",
36494
+ form: "array",
36495
+ optional: false
36496
+ }],
36122
36497
  "zoneAnalytics.getUnzonedHistory": [{
36123
36498
  name: "deviceId",
36124
36499
  form: "single",