@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.
@@ -8026,6 +8026,20 @@ var RelocateJobSchema = object({
8026
8026
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8027
8027
  */
8028
8028
  rowsReconciled: number().int().nonnegative().optional(),
8029
+ /**
8030
+ * Rows this run FORGOT because the file they name is not on disk.
8031
+ *
8032
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8033
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8034
+ * and the durable row is dropped through the same channel eviction uses. It
8035
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8036
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8037
+ * the same failure as one that quietly skips them (D295).
8038
+ *
8039
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8040
+ * ledger claimed 5.65 GB of footage that no longer existed.
8041
+ */
8042
+ rowsForgotten: number().int().nonnegative().optional(),
8029
8043
  startedAt: number(),
8030
8044
  finishedAt: number().nullable(),
8031
8045
  error: string().nullable()
@@ -8090,6 +8104,13 @@ var MediaRelocateModeSchema = _enum([
8090
8104
  ]);
8091
8105
  var RelocateMediaInputSchema = object({
8092
8106
  toLocationId: string(),
8107
+ /**
8108
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8109
+ * every row that is not already on `toLocationId` (the historical
8110
+ * behaviour). A named source is what a from→to migration needs: without it
8111
+ * "move events off disk 2" also emptied disk 1.
8112
+ */
8113
+ fromLocationId: string().optional(),
8093
8114
  throttleMbps: number().min(1).max(1e3).optional(),
8094
8115
  /** Omitted = `move`, the pre-existing behaviour. */
8095
8116
  mode: MediaRelocateModeSchema.optional()
@@ -8157,6 +8178,19 @@ var StorageMigrationDestinationsSchema = object({
8157
8178
  galleryMedia: string().min(1).optional()
8158
8179
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8159
8180
  /**
8181
+ * Optional named source per class. Omitted = the class's current default
8182
+ * (the historical behaviour). A named source that is NOT the default is a
8183
+ * drain of that disk: bytes move, the default stays, and the source is
8184
+ * disabled when the move finishes.
8185
+ */
8186
+ var StorageMigrationSourcesSchema = object({
8187
+ recordings: string().min(1).optional(),
8188
+ recordingsLow: string().min(1).optional(),
8189
+ eventMedia: string().min(1).optional(),
8190
+ backups: string().min(1).optional(),
8191
+ galleryMedia: string().min(1).optional()
8192
+ }).optional();
8193
+ /**
8160
8194
  * How a migration sequences the cutover against the byte move.
8161
8195
  *
8162
8196
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8178,6 +8212,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8178
8212
  /** Shared input for planning and starting an orchestrated storage migration. */
8179
8213
  var StorageMigrationInputSchema = object({
8180
8214
  destinations: StorageMigrationDestinationsSchema,
8215
+ /** Omitted = each class's current default. */
8216
+ sources: StorageMigrationSourcesSchema,
8181
8217
  throttleMbps: number().min(1).max(1e3).optional(),
8182
8218
  /** Omitted = `blocking`, which stays the default. */
8183
8219
  mode: StorageMigrationModeSchema.optional()
@@ -8257,6 +8293,13 @@ var StorageMigrationMoveSchema = object({
8257
8293
  storageClass: StorageMigrationClassSchema,
8258
8294
  fromLocationId: string(),
8259
8295
  toLocationId: string(),
8296
+ /**
8297
+ * True when `from` was NOT the class default at plan time. The move still
8298
+ * copies bytes, but the default is left alone and the source is disabled
8299
+ * once the copy verifies. Absent on jobs planned before this field existed
8300
+ * — those jobs always repointed, which is `false`.
8301
+ */
8302
+ freezeSource: boolean().optional(),
8260
8303
  moverJobId: string().nullable(),
8261
8304
  state: RelocateJobStateSchema.nullable(),
8262
8305
  error: string().nullable(),
@@ -8270,6 +8313,7 @@ var StorageMigrationJobSchema = object({
8270
8313
  * can tell a seconds-long cutover from a thirty-hour one. */
8271
8314
  mode: StorageMigrationModeSchema,
8272
8315
  destinations: StorageMigrationDestinationsSchema,
8316
+ sources: StorageMigrationSourcesSchema,
8273
8317
  throttleMbps: number(),
8274
8318
  moves: array(StorageMigrationMoveSchema),
8275
8319
  pauseLeaseId: string().nullable(),
@@ -8295,6 +8339,7 @@ var StorageMigrationFindingSchema = object({
8295
8339
  });
8296
8340
  var StorageMigrationPlanSchema = object({
8297
8341
  destinations: StorageMigrationDestinationsSchema,
8342
+ sources: StorageMigrationSourcesSchema,
8298
8343
  /** The mode this plan was built for. A plan is only valid for its mode: the
8299
8344
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8300
8345
  * it. */
@@ -8302,7 +8347,8 @@ var StorageMigrationPlanSchema = object({
8302
8347
  moves: array(object({
8303
8348
  storageClass: StorageMigrationClassSchema,
8304
8349
  fromLocationId: string(),
8305
- toLocationId: string()
8350
+ toLocationId: string(),
8351
+ freezeSource: boolean().optional()
8306
8352
  })),
8307
8353
  findings: array(StorageMigrationFindingSchema)
8308
8354
  });
@@ -8389,16 +8435,142 @@ var RelocateResidueSchema = object({
8389
8435
  segments: number().int().nonnegative(),
8390
8436
  bytes: number().int().nonnegative()
8391
8437
  }).nullable();
8438
+ /**
8439
+ * Ask one location whether its durable hour rows describe the disk — the walk
8440
+ * (D319).
8441
+ *
8442
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8443
+ * missing tool is the question, and the dry run is how they sanity-check the
8444
+ * destructive run before authorising it.
8445
+ */
8446
+ var LedgerWalkInputSchema = object({
8447
+ locationId: string().min(1),
8448
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8449
+ apply: boolean().optional(),
8450
+ /** Narrow to one camera. */
8451
+ deviceId: number().int().positive().optional(),
8452
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8453
+ profiles: array(string().min(1)).optional()
8454
+ });
8455
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8456
+ var LedgerWalkRefusalSchema = _enum([
8457
+ "location-unknown",
8458
+ "source-writable",
8459
+ "no-ledger",
8460
+ "archive-unreadable",
8461
+ "anchor-absent",
8462
+ "anchor-unreadable",
8463
+ "anchor-moved"
8464
+ ]);
8465
+ _enum([
8466
+ "live-tail",
8467
+ "listing-error",
8468
+ "path-mismatch",
8469
+ "durable-refused"
8470
+ ]);
8471
+ /** Every skip reason, always present, always a number — so a reason that never
8472
+ * fired reports as zero rather than absent and the report shape is constant
8473
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8474
+ var LedgerWalkSkipCountsSchema = object({
8475
+ "live-tail": number().int().nonnegative(),
8476
+ "listing-error": number().int().nonnegative(),
8477
+ "path-mismatch": number().int().nonnegative(),
8478
+ "durable-refused": number().int().nonnegative()
8479
+ });
8480
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8481
+ var LedgerWalkDeviceReportSchema = object({
8482
+ deviceId: number().int(),
8483
+ hoursWalked: number().int().nonnegative(),
8484
+ hoursMissing: number().int().nonnegative(),
8485
+ ghostSegments: number().int().nonnegative(),
8486
+ ghostBytes: number().int().nonnegative(),
8487
+ forgottenSegments: number().int().nonnegative(),
8488
+ orphanFiles: number().int().nonnegative()
8489
+ });
8490
+ /**
8491
+ * What one walk claimed, listed, found and (only when armed) forgot.
8492
+ *
8493
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8494
+ * walk that saw a fraction of the location is visible in its own report rather
8495
+ * than in the absence of one.
8496
+ */
8497
+ var LedgerWalkReportSchema = object({
8498
+ locationId: string(),
8499
+ applied: boolean(),
8500
+ refused: LedgerWalkRefusalSchema.nullable(),
8501
+ archiveSegments: number().int().nonnegative().nullable(),
8502
+ archiveBytes: number().int().nonnegative().nullable(),
8503
+ hoursClaimed: number().int().nonnegative(),
8504
+ hoursWalked: number().int().nonnegative(),
8505
+ hoursMissing: number().int().nonnegative(),
8506
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8507
+ listings: number().int().nonnegative(),
8508
+ segmentsClaimed: number().int().nonnegative(),
8509
+ ghostSegments: number().int().nonnegative(),
8510
+ ghostBytes: number().int().nonnegative(),
8511
+ ghostHoursWhole: number().int().nonnegative(),
8512
+ forgottenSegments: number().int().nonnegative(),
8513
+ forgottenBytes: number().int().nonnegative(),
8514
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8515
+ orphanFiles: number().int().nonnegative(),
8516
+ orphanSample: array(string()).readonly(),
8517
+ hoursSkipped: number().int().nonnegative(),
8518
+ skippedByReason: LedgerWalkSkipCountsSchema,
8519
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8520
+ bounded: boolean(),
8521
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8522
+ });
8392
8523
  /** How many rows a media pass would still act on against a given target — the
8393
8524
  * media lane's denominator AND its residue, from ONE derivation so the two can
8394
8525
  * never disagree. `null` = the count could not be taken. */
8395
8526
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8396
8527
  var RelocatableMediaCountInputSchema = object({
8397
8528
  toLocationId: string().min(1),
8529
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8530
+ fromLocationId: string().optional(),
8398
8531
  /** Omitted = `move`. */
8399
8532
  mode: MediaRelocateModeSchema.optional()
8400
8533
  });
8401
8534
  /**
8535
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8536
+ * ghost ledger entries on frozen footage locations.
8537
+ *
8538
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8539
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8540
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8541
+ * with no operator-visible status.
8542
+ */
8543
+ var StorageCleanupPhaseSchema = _enum([
8544
+ "orphans",
8545
+ "debug-media",
8546
+ "ghost-ledger",
8547
+ "done",
8548
+ "failed",
8549
+ "cancelled"
8550
+ ]);
8551
+ var StorageCleanupInputSchema = object({
8552
+ /** Also walk motion stills / track filmstrips. Off by default. */
8553
+ includeDebugMedia: boolean().optional() });
8554
+ var StorageCleanupJobSchema = object({
8555
+ jobId: string(),
8556
+ phase: StorageCleanupPhaseSchema,
8557
+ includeDebugMedia: boolean(),
8558
+ orphansReclaimed: number().int().nonnegative(),
8559
+ orphanBytesReclaimed: number().int().nonnegative(),
8560
+ debugMediaReclaimed: number().int().nonnegative(),
8561
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8562
+ ghostsForgotten: number().int().nonnegative(),
8563
+ ghostBytesForgotten: number().int().nonnegative(),
8564
+ /** Short operator-facing line: current collection, pass, or location. */
8565
+ detail: string().nullable(),
8566
+ cancelRequested: boolean(),
8567
+ startedAt: number(),
8568
+ updatedAt: number(),
8569
+ finishedAt: number().nullable(),
8570
+ error: string().nullable()
8571
+ });
8572
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8573
+ /**
8402
8574
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8403
8575
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8404
8576
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8426,11 +8598,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8426
8598
  * The default location for a type uses `id === <type>:default` by
8427
8599
  * convention (the bare type ref like `'backups'` resolves to it).
8428
8600
  *
8429
- * `isSystem: true` marks a location as orchestrator-seeded and
8430
- * undeletable. The bootstrap-installed defaults (one per type) carry
8431
- * this flag; operator-added locations don't. Editing the config of
8432
- * a system location is allowed (path migration, provider swap) but
8433
- * deleting it is rejected at the cap level.
8601
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8602
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8603
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8604
+ * / last-enabled, not on this bit.
8434
8605
  */
8435
8606
  var StorageLocationSchema = object({
8436
8607
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12692,6 +12863,12 @@ method(object({
12692
12863
  }), _void(), {
12693
12864
  kind: "mutation",
12694
12865
  auth: "admin"
12866
+ }), method(object({
12867
+ from: string(),
12868
+ to: string()
12869
+ }), object({ moved: number() }), {
12870
+ kind: "mutation",
12871
+ auth: "admin"
12695
12872
  }), method(object({
12696
12873
  deviceId: number(),
12697
12874
  disabled: boolean()
@@ -18622,53 +18799,15 @@ var RecentTracksPageSchema = object({
18622
18799
  /** Cursor for the next page, or null when this page is the last. */
18623
18800
  nextCursor: string().nullable()
18624
18801
  });
18625
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18626
- var LIST_GROUPS_MAX_LIMIT = 100;
18627
- var AnalyticsGroupRecordSchema = object({
18628
- id: string(),
18629
- deviceId: number().int(),
18630
- openedAt: number().int(),
18631
- closedAt: number().int(),
18632
- timestamp: number().int(),
18633
- memberCount: number().int(),
18634
- memberTrackIds: array(string()).readonly(),
18635
- className: string(),
18636
- classes: array(string()).readonly(),
18637
- /** Relative event-media path, or null when the group has no picture yet. */
18638
- mediaUrl: string().nullable(),
18639
- singleton: boolean()
18640
- });
18641
- var AnalyticsGroupMemberSchema = object({
18642
- trackId: string(),
18643
- deviceId: number().int(),
18644
- className: string(),
18645
- firstSeen: number().int(),
18646
- lastSeen: number().int(),
18647
- mediaUrl: string().nullable()
18648
- });
18649
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18650
- var ListGroupsQueryInput = object({
18651
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18652
- deviceIds: array(number()),
18653
- /** Window lower bound on `closedAt` (inclusive). */
18654
- since: number().optional(),
18655
- /** Window upper bound on `openedAt` (inclusive). */
18656
- until: number().optional(),
18657
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18658
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
18659
- cursor: string().optional()
18660
- });
18661
- var ListGroupsPageSchema = object({
18662
- groups: array(AnalyticsGroupRecordSchema).readonly(),
18663
- nextCursor: string().nullable()
18664
- });
18802
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
18803
+ var KEY_EVENTS_MAX_LIMIT = 200;
18665
18804
  var KeyEventQueryInput = object({
18666
18805
  deviceId: number(),
18667
18806
  /** Window lower bound (track firstSeen ≥ since). */
18668
18807
  since: number(),
18669
18808
  /** Window upper bound (track firstSeen ≤ until). */
18670
18809
  until: number(),
18671
- limit: number().int().min(1).max(200).default(50),
18810
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18672
18811
  /** Drop tracks scoring below this importance. */
18673
18812
  minImportance: number().min(0).max(1).optional(),
18674
18813
  /** Restrict to a single class (e.g. 'person'). */
@@ -18690,6 +18829,32 @@ var KeyEventSchema = object({
18690
18829
  ...TrackFlagFields,
18691
18830
  ...TrackRetrainFields
18692
18831
  });
18832
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
18833
+ var KeyEventBatchQueryInput = object({
18834
+ deviceIds: array(number()).min(1).max(200),
18835
+ since: number(),
18836
+ until: number(),
18837
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
18838
+ * across the set, which would let a busy camera starve a quiet one of its
18839
+ * rows and change what the merged feed contains. */
18840
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18841
+ minImportance: number().min(0).max(1).optional(),
18842
+ classFilter: string().optional()
18843
+ });
18844
+ /**
18845
+ * One camera's key events in a batch answer.
18846
+ *
18847
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
18848
+ * error rather than throwing, so a camera whose store read failed and one with
18849
+ * no events in the window were ALREADY indistinguishable per camera — the
18850
+ * batch does not make that worse, and the row keeps the deviceId the single
18851
+ * method's output never carried (the caller used to stamp it from the fan-out
18852
+ * key, which only worked because there was one query per camera).
18853
+ */
18854
+ var KeyEventsForDeviceSchema = object({
18855
+ deviceId: number(),
18856
+ events: array(KeyEventSchema).readonly()
18857
+ });
18693
18858
  object({
18694
18859
  trackId: string(),
18695
18860
  className: string(),
@@ -18737,9 +18902,7 @@ var TrackCascadeCountsSchema = object({
18737
18902
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18738
18903
  plates: number().int(),
18739
18904
  /** Per-track CLIP search vectors removed (best-effort). */
18740
- embeddings: number().int(),
18741
- /** Group membership + group rows removed with their last member (best-effort). */
18742
- groups: number().int()
18905
+ embeddings: number().int()
18743
18906
  });
18744
18907
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18745
18908
  var DiskReconcileCountsSchema = object({
@@ -18909,6 +19072,47 @@ var RebuildStatusSchema = object({
18909
19072
  /** Present when the pass ended by throwing. */
18910
19073
  error: string().nullable()
18911
19074
  });
19075
+ /**
19076
+ * Acknowledgement that a debug-media reclaim STARTED.
19077
+ *
19078
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19079
+ * it runs detached and this returns immediately. Awaiting it is how the
19080
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19081
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19082
+ */
19083
+ var MediaReclaimStartResultSchema = object({
19084
+ started: boolean(),
19085
+ /** True when a pass was already running; the new request is ignored. */
19086
+ alreadyRunning: boolean()
19087
+ });
19088
+ var MediaReclaimInputSchema = object({
19089
+ mode: _enum(["report", "reclaim"]).default("report"),
19090
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19091
+ deviceIds: array(number().int()).min(1).optional(),
19092
+ restart: boolean().optional(),
19093
+ pageSize: number().int().min(50).max(5e3).optional(),
19094
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19095
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19096
+ maxBytesPerRun: number().int().min(1).optional(),
19097
+ budgetMinutes: number().int().min(1).max(720).optional(),
19098
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19099
+ graceMinutes: number().int().min(1).max(10080).optional()
19100
+ });
19101
+ var MediaReclaimStatusSchema = object({
19102
+ running: boolean(),
19103
+ mode: _enum(["report", "reclaim"]).nullable(),
19104
+ totalExamined: number(),
19105
+ totalEligible: number(),
19106
+ totalReclaimed: number(),
19107
+ totalBytesReclaimed: number(),
19108
+ totalRefused: number(),
19109
+ /** Device+scope windows finished in this pass. */
19110
+ devicesDone: number(),
19111
+ complete: boolean().nullable(),
19112
+ startedAtMs: number().nullable(),
19113
+ finishedAtMs: number().nullable(),
19114
+ error: string().nullable()
19115
+ });
18912
19116
  var ReplayFrameInputSchema = object({
18913
19117
  timestamp: number(),
18914
19118
  frame: PipelineRunResultBridge
@@ -18947,10 +19151,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18947
19151
  * stationary registry). Default false: the timeline lists passages,
18948
19152
  * not parking records (operator decision, 2026-08-15). */
18949
19153
  includeStationary: boolean().optional()
18950
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18951
- deviceId: number(),
18952
- groupId: string().min(1)
18953
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19154
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18954
19155
  kind: "mutation",
18955
19156
  auth: "admin"
18956
19157
  }), 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({
@@ -18959,7 +19160,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18959
19160
  until: number().optional(),
18960
19161
  kinds: array(string()).optional(),
18961
19162
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18962
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19163
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
18963
19164
  deviceId: number(),
18964
19165
  since: number(),
18965
19166
  until: number(),
@@ -19050,6 +19251,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19050
19251
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19051
19252
  kind: "query",
19052
19253
  auth: "admin"
19254
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19255
+ kind: "mutation",
19256
+ auth: "admin"
19257
+ }), method(object({}), MediaReclaimStatusSchema, {
19258
+ kind: "query",
19259
+ auth: "admin"
19053
19260
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19054
19261
  kind: "query",
19055
19262
  auth: "admin"
@@ -21020,7 +21227,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21020
21227
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21021
21228
  kind: "mutation",
21022
21229
  auth: "admin"
21023
- });
21230
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21231
+ kind: "mutation",
21232
+ auth: "admin"
21233
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21234
+ kind: "mutation",
21235
+ auth: "admin"
21236
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21024
21237
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21025
21238
  providerId: string().min(1),
21026
21239
  displayName: string().min(1),
@@ -26458,6 +26671,33 @@ var RecordingRebalanceInputSchema = object({
26458
26671
  minMoveGb: number().min(0).optional()
26459
26672
  });
26460
26673
  /**
26674
+ * Operator-facing placement of one camera onto a recordings location.
26675
+ *
26676
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26677
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26678
+ * currently write — the plan, which may disagree with the pin when Auto.
26679
+ */
26680
+ var RecordingDevicePlacementSchema = object({
26681
+ deviceId: number().int(),
26682
+ profile: string(),
26683
+ locationId: string()
26684
+ });
26685
+ var RecordingDevicePinSchema = object({
26686
+ deviceId: number().int(),
26687
+ /** Recordings-class location this camera is pinned to. */
26688
+ locationId: string()
26689
+ });
26690
+ var RecordingPlacementViewSchema = object({
26691
+ assignments: array(RecordingDevicePlacementSchema),
26692
+ pins: array(RecordingDevicePinSchema),
26693
+ defaultLocations: record(string(), string())
26694
+ });
26695
+ var RecordingSetDevicePlacementInputSchema = object({
26696
+ deviceId: number().int(),
26697
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26698
+ locationId: string().nullable()
26699
+ });
26700
+ /**
26461
26701
  * Result of locating footage at a wall-clock instant for one device/profile.
26462
26702
  * `segment` carries the covering segment's window; `gap` reports the forward
26463
26703
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26671,6 +26911,9 @@ method(object({
26671
26911
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26672
26912
  kind: "query",
26673
26913
  auth: "admin"
26914
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26915
+ kind: "mutation",
26916
+ auth: "admin"
26674
26917
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26675
26918
  kind: "mutation",
26676
26919
  auth: "admin"
@@ -26680,6 +26923,12 @@ method(object({
26680
26923
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26681
26924
  kind: "mutation",
26682
26925
  auth: "admin"
26926
+ }), method(object({}), RecordingPlacementViewSchema, {
26927
+ kind: "query",
26928
+ auth: "admin"
26929
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
26930
+ kind: "mutation",
26931
+ auth: "admin"
26683
26932
  });
26684
26933
  /**
26685
26934
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27062,7 +27311,26 @@ var SceneMonitorStatusSchema = object({
27062
27311
  monitors: array(SceneMonitorSchema),
27063
27312
  lastFetchedAt: number()
27064
27313
  });
27065
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27314
+ /**
27315
+ * One camera's row in a `listScenesBatch` answer.
27316
+ *
27317
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27318
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27319
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27320
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27321
+ * configured. Fanned out per camera the difference was visible — one query
27322
+ * errored while the others resolved — and a batch that returned only the rows
27323
+ * it managed would have destroyed it, silently, by making an unreachable camera
27324
+ * indistinguishable from one that answered `monitors: []`.
27325
+ *
27326
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27327
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27328
+ */
27329
+ var SceneMonitorStatusForDeviceSchema = object({
27330
+ deviceId: number(),
27331
+ status: SceneMonitorStatusSchema.nullable()
27332
+ });
27333
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27066
27334
  deviceId: number(),
27067
27335
  label: string(),
27068
27336
  roi: MaskRectShapeSchema,
@@ -28386,6 +28654,27 @@ var CameraOccupancySnapshotSchema = object({
28386
28654
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28387
28655
  });
28388
28656
  /**
28657
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28658
+ *
28659
+ * THREE outcomes, and the single-camera method could only express two of them
28660
+ * because `snapshot: null` was already spoken for:
28661
+ *
28662
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28663
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28664
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28665
+ * - `read: 'unreadable'` — the owner could not answer for this
28666
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28667
+ *
28668
+ * Collapsing the last two is the failure this field exists to prevent: a
28669
+ * hydration that threw would otherwise render as an empty Stationary section,
28670
+ * which is a definite claim about a camera nobody could read.
28671
+ */
28672
+ var CameraOccupancySnapshotForDeviceSchema = object({
28673
+ deviceId: number(),
28674
+ read: _enum(["read", "unreadable"]),
28675
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28676
+ });
28677
+ /**
28389
28678
  * Time-series resolution. The history methods return one bucket per
28390
28679
  * step over the requested range. Smaller resolutions cost more
28391
28680
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28409,7 +28698,7 @@ var HistoryPointSchema = object({
28409
28698
  /** Object count averaged over the bucket (rounded to nearest integer). */
28410
28699
  count: number().int().nonnegative()
28411
28700
  });
28412
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28701
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28413
28702
  deviceId: number(),
28414
28703
  zoneId: string(),
28415
28704
  className: string().optional()
@@ -30010,6 +30299,12 @@ Object.freeze({
30010
30299
  addonId: null,
30011
30300
  access: "delete"
30012
30301
  },
30302
+ "deviceManager.renameLocation": {
30303
+ capName: "device-manager",
30304
+ capScope: "system",
30305
+ addonId: null,
30306
+ access: "create"
30307
+ },
30013
30308
  "deviceManager.runDeviceAction": {
30014
30309
  capName: "device-manager",
30015
30310
  capScope: "system",
@@ -31702,13 +31997,19 @@ Object.freeze({
31702
31997
  addonId: null,
31703
31998
  access: "view"
31704
31999
  },
31705
- "pipelineAnalytics.getGroup": {
32000
+ "pipelineAnalytics.getKeyEvents": {
31706
32001
  capName: "pipeline-analytics",
31707
32002
  capScope: "device",
31708
32003
  addonId: null,
31709
32004
  access: "view"
31710
32005
  },
31711
- "pipelineAnalytics.getKeyEvents": {
32006
+ "pipelineAnalytics.getKeyEventsBatch": {
32007
+ capName: "pipeline-analytics",
32008
+ capScope: "device",
32009
+ addonId: null,
32010
+ access: "view"
32011
+ },
32012
+ "pipelineAnalytics.getMediaReclaimStatus": {
31712
32013
  capName: "pipeline-analytics",
31713
32014
  capScope: "device",
31714
32015
  addonId: null,
@@ -31798,12 +32099,6 @@ Object.freeze({
31798
32099
  addonId: null,
31799
32100
  access: "view"
31800
32101
  },
31801
- "pipelineAnalytics.listGroups": {
31802
- capName: "pipeline-analytics",
31803
- capScope: "device",
31804
- addonId: null,
31805
- access: "view"
31806
- },
31807
32102
  "pipelineAnalytics.listOpsLog": {
31808
32103
  capName: "pipeline-analytics",
31809
32104
  capScope: "device",
@@ -31888,6 +32183,12 @@ Object.freeze({
31888
32183
  addonId: null,
31889
32184
  access: "create"
31890
32185
  },
32186
+ "pipelineAnalytics.reclaimDebugMedia": {
32187
+ capName: "pipeline-analytics",
32188
+ capScope: "device",
32189
+ addonId: null,
32190
+ access: "create"
32191
+ },
31891
32192
  "pipelineAnalytics.reconcileFromDisk": {
31892
32193
  capName: "pipeline-analytics",
31893
32194
  capScope: "device",
@@ -32812,6 +33113,12 @@ Object.freeze({
32812
33113
  addonId: null,
32813
33114
  access: "view"
32814
33115
  },
33116
+ "recording.getPlacement": {
33117
+ capName: "recording",
33118
+ capScope: "system",
33119
+ addonId: null,
33120
+ access: "view"
33121
+ },
32815
33122
  "recording.getPlaybackManifest": {
32816
33123
  capName: "recording",
32817
33124
  capScope: "system",
@@ -32890,6 +33197,12 @@ Object.freeze({
32890
33197
  addonId: null,
32891
33198
  access: "view"
32892
33199
  },
33200
+ "recording.reconcileLedgerAgainstDisk": {
33201
+ capName: "recording",
33202
+ capScope: "system",
33203
+ addonId: null,
33204
+ access: "create"
33205
+ },
32893
33206
  "recording.refreshStorageLocationsForMigration": {
32894
33207
  capName: "recording",
32895
33208
  capScope: "system",
@@ -32932,6 +33245,12 @@ Object.freeze({
32932
33245
  addonId: null,
32933
33246
  access: "create"
32934
33247
  },
33248
+ "recording.setDevicePlacement": {
33249
+ capName: "recording",
33250
+ capScope: "system",
33251
+ addonId: null,
33252
+ access: "create"
33253
+ },
32935
33254
  "recording.startStorageMigrationMove": {
32936
33255
  capName: "recording",
32937
33256
  capScope: "system",
@@ -33016,6 +33335,12 @@ Object.freeze({
33016
33335
  addonId: null,
33017
33336
  access: "view"
33018
33337
  },
33338
+ "sceneMonitor.listScenesBatch": {
33339
+ capName: "scene-monitor",
33340
+ capScope: "device",
33341
+ addonId: null,
33342
+ access: "view"
33343
+ },
33019
33344
  "sceneMonitor.recheckNow": {
33020
33345
  capName: "scene-monitor",
33021
33346
  capScope: "device",
@@ -33370,12 +33695,36 @@ Object.freeze({
33370
33695
  addonId: null,
33371
33696
  access: "create"
33372
33697
  },
33698
+ "storageMigration.cleanupCancel": {
33699
+ capName: "storage-migration",
33700
+ capScope: "system",
33701
+ addonId: null,
33702
+ access: "create"
33703
+ },
33704
+ "storageMigration.cleanupStart": {
33705
+ capName: "storage-migration",
33706
+ capScope: "system",
33707
+ addonId: null,
33708
+ access: "create"
33709
+ },
33710
+ "storageMigration.cleanupStatus": {
33711
+ capName: "storage-migration",
33712
+ capScope: "system",
33713
+ addonId: null,
33714
+ access: "view"
33715
+ },
33373
33716
  "storageMigration.drain": {
33374
33717
  capName: "storage-migration",
33375
33718
  capScope: "system",
33376
33719
  addonId: null,
33377
33720
  access: "create"
33378
33721
  },
33722
+ "storageMigration.history": {
33723
+ capName: "storage-migration",
33724
+ capScope: "system",
33725
+ addonId: null,
33726
+ access: "view"
33727
+ },
33379
33728
  "storageMigration.movers": {
33380
33729
  capName: "storage-migration",
33381
33730
  capScope: "system",
@@ -34372,6 +34721,12 @@ Object.freeze({
34372
34721
  addonId: null,
34373
34722
  access: "view"
34374
34723
  },
34724
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34725
+ capName: "zone-analytics",
34726
+ capScope: "device",
34727
+ addonId: null,
34728
+ access: "view"
34729
+ },
34375
34730
  "zoneAnalytics.getUnzonedHistory": {
34376
34731
  capName: "zone-analytics",
34377
34732
  capScope: "device",
@@ -35366,14 +35721,14 @@ Object.freeze({
35366
35721
  form: "single",
35367
35722
  optional: true
35368
35723
  }],
35369
- "pipelineAnalytics.getGroup": [{
35724
+ "pipelineAnalytics.getKeyEvents": [{
35370
35725
  name: "deviceId",
35371
35726
  form: "single",
35372
35727
  optional: false
35373
35728
  }],
35374
- "pipelineAnalytics.getKeyEvents": [{
35375
- name: "deviceId",
35376
- form: "single",
35729
+ "pipelineAnalytics.getKeyEventsBatch": [{
35730
+ name: "deviceIds",
35731
+ form: "array",
35377
35732
  optional: false
35378
35733
  }],
35379
35734
  "pipelineAnalytics.getMotionEvents": [{
@@ -35431,11 +35786,6 @@ Object.freeze({
35431
35786
  form: "single",
35432
35787
  optional: false
35433
35788
  }],
35434
- "pipelineAnalytics.listGroups": [{
35435
- name: "deviceIds",
35436
- form: "array",
35437
- optional: false
35438
- }],
35439
35789
  "pipelineAnalytics.listOpsLog": [{
35440
35790
  name: "deviceId",
35441
35791
  form: "single",
@@ -35481,6 +35831,11 @@ Object.freeze({
35481
35831
  form: "single",
35482
35832
  optional: true
35483
35833
  }],
35834
+ "pipelineAnalytics.reclaimDebugMedia": [{
35835
+ name: "deviceIds",
35836
+ form: "array",
35837
+ optional: true
35838
+ }],
35484
35839
  "pipelineAnalytics.reconcileFromDisk": [{
35485
35840
  name: "deviceId",
35486
35841
  form: "single",
@@ -35816,6 +36171,11 @@ Object.freeze({
35816
36171
  form: "single",
35817
36172
  optional: false
35818
36173
  }],
36174
+ "recording.reconcileLedgerAgainstDisk": [{
36175
+ name: "deviceId",
36176
+ form: "single",
36177
+ optional: true
36178
+ }],
35819
36179
  "recording.relocateFootage": [{
35820
36180
  name: "deviceId",
35821
36181
  form: "single",
@@ -35841,6 +36201,11 @@ Object.freeze({
35841
36201
  form: "single",
35842
36202
  optional: false
35843
36203
  }],
36204
+ "recording.setDevicePlacement": [{
36205
+ name: "deviceId",
36206
+ form: "single",
36207
+ optional: false
36208
+ }],
35844
36209
  "recording.startStorageMigrationMove": [{
35845
36210
  name: "deviceId",
35846
36211
  form: "single",
@@ -35881,6 +36246,11 @@ Object.freeze({
35881
36246
  form: "single",
35882
36247
  optional: false
35883
36248
  }],
36249
+ "sceneMonitor.listScenesBatch": [{
36250
+ name: "deviceIds",
36251
+ form: "array",
36252
+ optional: false
36253
+ }],
35884
36254
  "sceneMonitor.recheckNow": [{
35885
36255
  name: "deviceId",
35886
36256
  form: "single",
@@ -36142,6 +36512,11 @@ Object.freeze({
36142
36512
  form: "single",
36143
36513
  optional: false
36144
36514
  }],
36515
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36516
+ name: "deviceIds",
36517
+ form: "array",
36518
+ optional: false
36519
+ }],
36145
36520
  "zoneAnalytics.getUnzonedHistory": [{
36146
36521
  name: "deviceId",
36147
36522
  form: "single",