@camstack/addon-export-alexa 1.2.54 → 1.2.56

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.
@@ -8159,6 +8159,20 @@ var RelocateJobSchema = object({
8159
8159
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8160
8160
  */
8161
8161
  rowsReconciled: number().int().nonnegative().optional(),
8162
+ /**
8163
+ * Rows this run FORGOT because the file they name is not on disk.
8164
+ *
8165
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8166
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8167
+ * and the durable row is dropped through the same channel eviction uses. It
8168
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8169
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8170
+ * the same failure as one that quietly skips them (D295).
8171
+ *
8172
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8173
+ * ledger claimed 5.65 GB of footage that no longer existed.
8174
+ */
8175
+ rowsForgotten: number().int().nonnegative().optional(),
8162
8176
  startedAt: number(),
8163
8177
  finishedAt: number().nullable(),
8164
8178
  error: string().nullable()
@@ -8223,6 +8237,13 @@ var MediaRelocateModeSchema = _enum([
8223
8237
  ]);
8224
8238
  var RelocateMediaInputSchema = object({
8225
8239
  toLocationId: string(),
8240
+ /**
8241
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8242
+ * every row that is not already on `toLocationId` (the historical
8243
+ * behaviour). A named source is what a from→to migration needs: without it
8244
+ * "move events off disk 2" also emptied disk 1.
8245
+ */
8246
+ fromLocationId: string().optional(),
8226
8247
  throttleMbps: number().min(1).max(1e3).optional(),
8227
8248
  /** Omitted = `move`, the pre-existing behaviour. */
8228
8249
  mode: MediaRelocateModeSchema.optional()
@@ -8290,6 +8311,19 @@ var StorageMigrationDestinationsSchema = object({
8290
8311
  galleryMedia: string().min(1).optional()
8291
8312
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8292
8313
  /**
8314
+ * Optional named source per class. Omitted = the class's current default
8315
+ * (the historical behaviour). A named source that is NOT the default is a
8316
+ * drain of that disk: bytes move, the default stays, and the source is
8317
+ * disabled when the move finishes.
8318
+ */
8319
+ var StorageMigrationSourcesSchema = object({
8320
+ recordings: string().min(1).optional(),
8321
+ recordingsLow: string().min(1).optional(),
8322
+ eventMedia: string().min(1).optional(),
8323
+ backups: string().min(1).optional(),
8324
+ galleryMedia: string().min(1).optional()
8325
+ }).optional();
8326
+ /**
8293
8327
  * How a migration sequences the cutover against the byte move.
8294
8328
  *
8295
8329
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8311,6 +8345,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8311
8345
  /** Shared input for planning and starting an orchestrated storage migration. */
8312
8346
  var StorageMigrationInputSchema = object({
8313
8347
  destinations: StorageMigrationDestinationsSchema,
8348
+ /** Omitted = each class's current default. */
8349
+ sources: StorageMigrationSourcesSchema,
8314
8350
  throttleMbps: number().min(1).max(1e3).optional(),
8315
8351
  /** Omitted = `blocking`, which stays the default. */
8316
8352
  mode: StorageMigrationModeSchema.optional()
@@ -8390,6 +8426,13 @@ var StorageMigrationMoveSchema = object({
8390
8426
  storageClass: StorageMigrationClassSchema,
8391
8427
  fromLocationId: string(),
8392
8428
  toLocationId: string(),
8429
+ /**
8430
+ * True when `from` was NOT the class default at plan time. The move still
8431
+ * copies bytes, but the default is left alone and the source is disabled
8432
+ * once the copy verifies. Absent on jobs planned before this field existed
8433
+ * — those jobs always repointed, which is `false`.
8434
+ */
8435
+ freezeSource: boolean().optional(),
8393
8436
  moverJobId: string().nullable(),
8394
8437
  state: RelocateJobStateSchema.nullable(),
8395
8438
  error: string().nullable(),
@@ -8403,6 +8446,7 @@ var StorageMigrationJobSchema = object({
8403
8446
  * can tell a seconds-long cutover from a thirty-hour one. */
8404
8447
  mode: StorageMigrationModeSchema,
8405
8448
  destinations: StorageMigrationDestinationsSchema,
8449
+ sources: StorageMigrationSourcesSchema,
8406
8450
  throttleMbps: number(),
8407
8451
  moves: array(StorageMigrationMoveSchema),
8408
8452
  pauseLeaseId: string().nullable(),
@@ -8428,6 +8472,7 @@ var StorageMigrationFindingSchema = object({
8428
8472
  });
8429
8473
  var StorageMigrationPlanSchema = object({
8430
8474
  destinations: StorageMigrationDestinationsSchema,
8475
+ sources: StorageMigrationSourcesSchema,
8431
8476
  /** The mode this plan was built for. A plan is only valid for its mode: the
8432
8477
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8433
8478
  * it. */
@@ -8435,7 +8480,8 @@ var StorageMigrationPlanSchema = object({
8435
8480
  moves: array(object({
8436
8481
  storageClass: StorageMigrationClassSchema,
8437
8482
  fromLocationId: string(),
8438
- toLocationId: string()
8483
+ toLocationId: string(),
8484
+ freezeSource: boolean().optional()
8439
8485
  })),
8440
8486
  findings: array(StorageMigrationFindingSchema)
8441
8487
  });
@@ -8522,16 +8568,142 @@ var RelocateResidueSchema = object({
8522
8568
  segments: number().int().nonnegative(),
8523
8569
  bytes: number().int().nonnegative()
8524
8570
  }).nullable();
8571
+ /**
8572
+ * Ask one location whether its durable hour rows describe the disk — the walk
8573
+ * (D319).
8574
+ *
8575
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8576
+ * missing tool is the question, and the dry run is how they sanity-check the
8577
+ * destructive run before authorising it.
8578
+ */
8579
+ var LedgerWalkInputSchema = object({
8580
+ locationId: string().min(1),
8581
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8582
+ apply: boolean().optional(),
8583
+ /** Narrow to one camera. */
8584
+ deviceId: number().int().positive().optional(),
8585
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8586
+ profiles: array(string().min(1)).optional()
8587
+ });
8588
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8589
+ var LedgerWalkRefusalSchema = _enum([
8590
+ "location-unknown",
8591
+ "source-writable",
8592
+ "no-ledger",
8593
+ "archive-unreadable",
8594
+ "anchor-absent",
8595
+ "anchor-unreadable",
8596
+ "anchor-moved"
8597
+ ]);
8598
+ _enum([
8599
+ "live-tail",
8600
+ "listing-error",
8601
+ "path-mismatch",
8602
+ "durable-refused"
8603
+ ]);
8604
+ /** Every skip reason, always present, always a number — so a reason that never
8605
+ * fired reports as zero rather than absent and the report shape is constant
8606
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8607
+ var LedgerWalkSkipCountsSchema = object({
8608
+ "live-tail": number().int().nonnegative(),
8609
+ "listing-error": number().int().nonnegative(),
8610
+ "path-mismatch": number().int().nonnegative(),
8611
+ "durable-refused": number().int().nonnegative()
8612
+ });
8613
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8614
+ var LedgerWalkDeviceReportSchema = object({
8615
+ deviceId: number().int(),
8616
+ hoursWalked: number().int().nonnegative(),
8617
+ hoursMissing: number().int().nonnegative(),
8618
+ ghostSegments: number().int().nonnegative(),
8619
+ ghostBytes: number().int().nonnegative(),
8620
+ forgottenSegments: number().int().nonnegative(),
8621
+ orphanFiles: number().int().nonnegative()
8622
+ });
8623
+ /**
8624
+ * What one walk claimed, listed, found and (only when armed) forgot.
8625
+ *
8626
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8627
+ * walk that saw a fraction of the location is visible in its own report rather
8628
+ * than in the absence of one.
8629
+ */
8630
+ var LedgerWalkReportSchema = object({
8631
+ locationId: string(),
8632
+ applied: boolean(),
8633
+ refused: LedgerWalkRefusalSchema.nullable(),
8634
+ archiveSegments: number().int().nonnegative().nullable(),
8635
+ archiveBytes: number().int().nonnegative().nullable(),
8636
+ hoursClaimed: number().int().nonnegative(),
8637
+ hoursWalked: number().int().nonnegative(),
8638
+ hoursMissing: number().int().nonnegative(),
8639
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8640
+ listings: number().int().nonnegative(),
8641
+ segmentsClaimed: number().int().nonnegative(),
8642
+ ghostSegments: number().int().nonnegative(),
8643
+ ghostBytes: number().int().nonnegative(),
8644
+ ghostHoursWhole: number().int().nonnegative(),
8645
+ forgottenSegments: number().int().nonnegative(),
8646
+ forgottenBytes: number().int().nonnegative(),
8647
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8648
+ orphanFiles: number().int().nonnegative(),
8649
+ orphanSample: array(string()).readonly(),
8650
+ hoursSkipped: number().int().nonnegative(),
8651
+ skippedByReason: LedgerWalkSkipCountsSchema,
8652
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8653
+ bounded: boolean(),
8654
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8655
+ });
8525
8656
  /** How many rows a media pass would still act on against a given target — the
8526
8657
  * media lane's denominator AND its residue, from ONE derivation so the two can
8527
8658
  * never disagree. `null` = the count could not be taken. */
8528
8659
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8529
8660
  var RelocatableMediaCountInputSchema = object({
8530
8661
  toLocationId: string().min(1),
8662
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8663
+ fromLocationId: string().optional(),
8531
8664
  /** Omitted = `move`. */
8532
8665
  mode: MediaRelocateModeSchema.optional()
8533
8666
  });
8534
8667
  /**
8668
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8669
+ * ghost ledger entries on frozen footage locations.
8670
+ *
8671
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8672
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8673
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8674
+ * with no operator-visible status.
8675
+ */
8676
+ var StorageCleanupPhaseSchema = _enum([
8677
+ "orphans",
8678
+ "debug-media",
8679
+ "ghost-ledger",
8680
+ "done",
8681
+ "failed",
8682
+ "cancelled"
8683
+ ]);
8684
+ var StorageCleanupInputSchema = object({
8685
+ /** Also walk motion stills / track filmstrips. Off by default. */
8686
+ includeDebugMedia: boolean().optional() });
8687
+ var StorageCleanupJobSchema = object({
8688
+ jobId: string(),
8689
+ phase: StorageCleanupPhaseSchema,
8690
+ includeDebugMedia: boolean(),
8691
+ orphansReclaimed: number().int().nonnegative(),
8692
+ orphanBytesReclaimed: number().int().nonnegative(),
8693
+ debugMediaReclaimed: number().int().nonnegative(),
8694
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8695
+ ghostsForgotten: number().int().nonnegative(),
8696
+ ghostBytesForgotten: number().int().nonnegative(),
8697
+ /** Short operator-facing line: current collection, pass, or location. */
8698
+ detail: string().nullable(),
8699
+ cancelRequested: boolean(),
8700
+ startedAt: number(),
8701
+ updatedAt: number(),
8702
+ finishedAt: number().nullable(),
8703
+ error: string().nullable()
8704
+ });
8705
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8706
+ /**
8535
8707
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8536
8708
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8537
8709
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8559,11 +8731,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8559
8731
  * The default location for a type uses `id === <type>:default` by
8560
8732
  * convention (the bare type ref like `'backups'` resolves to it).
8561
8733
  *
8562
- * `isSystem: true` marks a location as orchestrator-seeded and
8563
- * undeletable. The bootstrap-installed defaults (one per type) carry
8564
- * this flag; operator-added locations don't. Editing the config of
8565
- * a system location is allowed (path migration, provider swap) but
8566
- * deleting it is rejected at the cap level.
8734
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8735
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8736
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8737
+ * / last-enabled, not on this bit.
8567
8738
  */
8568
8739
  var StorageLocationSchema = object({
8569
8740
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12952,6 +13123,12 @@ method(object({
12952
13123
  }), _void(), {
12953
13124
  kind: "mutation",
12954
13125
  auth: "admin"
13126
+ }), method(object({
13127
+ from: string(),
13128
+ to: string()
13129
+ }), object({ moved: number() }), {
13130
+ kind: "mutation",
13131
+ auth: "admin"
12955
13132
  }), method(object({
12956
13133
  deviceId: number(),
12957
13134
  disabled: boolean()
@@ -18898,53 +19075,15 @@ var RecentTracksPageSchema = object({
18898
19075
  /** Cursor for the next page, or null when this page is the last. */
18899
19076
  nextCursor: string().nullable()
18900
19077
  });
18901
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18902
- var LIST_GROUPS_MAX_LIMIT = 100;
18903
- var AnalyticsGroupRecordSchema = object({
18904
- id: string(),
18905
- deviceId: number().int(),
18906
- openedAt: number().int(),
18907
- closedAt: number().int(),
18908
- timestamp: number().int(),
18909
- memberCount: number().int(),
18910
- memberTrackIds: array(string()).readonly(),
18911
- className: string(),
18912
- classes: array(string()).readonly(),
18913
- /** Relative event-media path, or null when the group has no picture yet. */
18914
- mediaUrl: string().nullable(),
18915
- singleton: boolean()
18916
- });
18917
- var AnalyticsGroupMemberSchema = object({
18918
- trackId: string(),
18919
- deviceId: number().int(),
18920
- className: string(),
18921
- firstSeen: number().int(),
18922
- lastSeen: number().int(),
18923
- mediaUrl: string().nullable()
18924
- });
18925
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18926
- var ListGroupsQueryInput = object({
18927
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18928
- deviceIds: array(number()),
18929
- /** Window lower bound on `closedAt` (inclusive). */
18930
- since: number().optional(),
18931
- /** Window upper bound on `openedAt` (inclusive). */
18932
- until: number().optional(),
18933
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18934
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
18935
- cursor: string().optional()
18936
- });
18937
- var ListGroupsPageSchema = object({
18938
- groups: array(AnalyticsGroupRecordSchema).readonly(),
18939
- nextCursor: string().nullable()
18940
- });
19078
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19079
+ var KEY_EVENTS_MAX_LIMIT = 200;
18941
19080
  var KeyEventQueryInput = object({
18942
19081
  deviceId: number(),
18943
19082
  /** Window lower bound (track firstSeen ≥ since). */
18944
19083
  since: number(),
18945
19084
  /** Window upper bound (track firstSeen ≤ until). */
18946
19085
  until: number(),
18947
- limit: number().int().min(1).max(200).default(50),
19086
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18948
19087
  /** Drop tracks scoring below this importance. */
18949
19088
  minImportance: number().min(0).max(1).optional(),
18950
19089
  /** Restrict to a single class (e.g. 'person'). */
@@ -18966,6 +19105,32 @@ var KeyEventSchema = object({
18966
19105
  ...TrackFlagFields,
18967
19106
  ...TrackRetrainFields
18968
19107
  });
19108
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19109
+ var KeyEventBatchQueryInput = object({
19110
+ deviceIds: array(number()).min(1).max(200),
19111
+ since: number(),
19112
+ until: number(),
19113
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19114
+ * across the set, which would let a busy camera starve a quiet one of its
19115
+ * rows and change what the merged feed contains. */
19116
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19117
+ minImportance: number().min(0).max(1).optional(),
19118
+ classFilter: string().optional()
19119
+ });
19120
+ /**
19121
+ * One camera's key events in a batch answer.
19122
+ *
19123
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19124
+ * error rather than throwing, so a camera whose store read failed and one with
19125
+ * no events in the window were ALREADY indistinguishable per camera — the
19126
+ * batch does not make that worse, and the row keeps the deviceId the single
19127
+ * method's output never carried (the caller used to stamp it from the fan-out
19128
+ * key, which only worked because there was one query per camera).
19129
+ */
19130
+ var KeyEventsForDeviceSchema = object({
19131
+ deviceId: number(),
19132
+ events: array(KeyEventSchema).readonly()
19133
+ });
18969
19134
  object({
18970
19135
  trackId: string(),
18971
19136
  className: string(),
@@ -19013,9 +19178,7 @@ var TrackCascadeCountsSchema = object({
19013
19178
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19014
19179
  plates: number().int(),
19015
19180
  /** Per-track CLIP search vectors removed (best-effort). */
19016
- embeddings: number().int(),
19017
- /** Group membership + group rows removed with their last member (best-effort). */
19018
- groups: number().int()
19181
+ embeddings: number().int()
19019
19182
  });
19020
19183
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19021
19184
  var DiskReconcileCountsSchema = object({
@@ -19185,6 +19348,47 @@ var RebuildStatusSchema = object({
19185
19348
  /** Present when the pass ended by throwing. */
19186
19349
  error: string().nullable()
19187
19350
  });
19351
+ /**
19352
+ * Acknowledgement that a debug-media reclaim STARTED.
19353
+ *
19354
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19355
+ * it runs detached and this returns immediately. Awaiting it is how the
19356
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19357
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19358
+ */
19359
+ var MediaReclaimStartResultSchema = object({
19360
+ started: boolean(),
19361
+ /** True when a pass was already running; the new request is ignored. */
19362
+ alreadyRunning: boolean()
19363
+ });
19364
+ var MediaReclaimInputSchema = object({
19365
+ mode: _enum(["report", "reclaim"]).default("report"),
19366
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19367
+ deviceIds: array(number().int()).min(1).optional(),
19368
+ restart: boolean().optional(),
19369
+ pageSize: number().int().min(50).max(5e3).optional(),
19370
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19371
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19372
+ maxBytesPerRun: number().int().min(1).optional(),
19373
+ budgetMinutes: number().int().min(1).max(720).optional(),
19374
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19375
+ graceMinutes: number().int().min(1).max(10080).optional()
19376
+ });
19377
+ var MediaReclaimStatusSchema = object({
19378
+ running: boolean(),
19379
+ mode: _enum(["report", "reclaim"]).nullable(),
19380
+ totalExamined: number(),
19381
+ totalEligible: number(),
19382
+ totalReclaimed: number(),
19383
+ totalBytesReclaimed: number(),
19384
+ totalRefused: number(),
19385
+ /** Device+scope windows finished in this pass. */
19386
+ devicesDone: number(),
19387
+ complete: boolean().nullable(),
19388
+ startedAtMs: number().nullable(),
19389
+ finishedAtMs: number().nullable(),
19390
+ error: string().nullable()
19391
+ });
19188
19392
  var ReplayFrameInputSchema = object({
19189
19393
  timestamp: number(),
19190
19394
  frame: PipelineRunResultBridge
@@ -19223,10 +19427,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19223
19427
  * stationary registry). Default false: the timeline lists passages,
19224
19428
  * not parking records (operator decision, 2026-08-15). */
19225
19429
  includeStationary: boolean().optional()
19226
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19227
- deviceId: number(),
19228
- groupId: string().min(1)
19229
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19430
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19230
19431
  kind: "mutation",
19231
19432
  auth: "admin"
19232
19433
  }), 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({
@@ -19235,7 +19436,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19235
19436
  until: number().optional(),
19236
19437
  kinds: array(string()).optional(),
19237
19438
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19238
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19439
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19239
19440
  deviceId: number(),
19240
19441
  since: number(),
19241
19442
  until: number(),
@@ -19326,6 +19527,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19326
19527
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19327
19528
  kind: "query",
19328
19529
  auth: "admin"
19530
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19531
+ kind: "mutation",
19532
+ auth: "admin"
19533
+ }), method(object({}), MediaReclaimStatusSchema, {
19534
+ kind: "query",
19535
+ auth: "admin"
19329
19536
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19330
19537
  kind: "query",
19331
19538
  auth: "admin"
@@ -21296,7 +21503,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21296
21503
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21297
21504
  kind: "mutation",
21298
21505
  auth: "admin"
21299
- });
21506
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21507
+ kind: "mutation",
21508
+ auth: "admin"
21509
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21510
+ kind: "mutation",
21511
+ auth: "admin"
21512
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21300
21513
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21301
21514
  providerId: string().min(1),
21302
21515
  displayName: string().min(1),
@@ -26674,6 +26887,33 @@ var RecordingRebalanceInputSchema = object({
26674
26887
  minMoveGb: number().min(0).optional()
26675
26888
  });
26676
26889
  /**
26890
+ * Operator-facing placement of one camera onto a recordings location.
26891
+ *
26892
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26893
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26894
+ * currently write — the plan, which may disagree with the pin when Auto.
26895
+ */
26896
+ var RecordingDevicePlacementSchema = object({
26897
+ deviceId: number().int(),
26898
+ profile: string(),
26899
+ locationId: string()
26900
+ });
26901
+ var RecordingDevicePinSchema = object({
26902
+ deviceId: number().int(),
26903
+ /** Recordings-class location this camera is pinned to. */
26904
+ locationId: string()
26905
+ });
26906
+ var RecordingPlacementViewSchema = object({
26907
+ assignments: array(RecordingDevicePlacementSchema),
26908
+ pins: array(RecordingDevicePinSchema),
26909
+ defaultLocations: record(string(), string())
26910
+ });
26911
+ var RecordingSetDevicePlacementInputSchema = object({
26912
+ deviceId: number().int(),
26913
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26914
+ locationId: string().nullable()
26915
+ });
26916
+ /**
26677
26917
  * Result of locating footage at a wall-clock instant for one device/profile.
26678
26918
  * `segment` carries the covering segment's window; `gap` reports the forward
26679
26919
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26887,6 +27127,9 @@ method(object({
26887
27127
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26888
27128
  kind: "query",
26889
27129
  auth: "admin"
27130
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27131
+ kind: "mutation",
27132
+ auth: "admin"
26890
27133
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26891
27134
  kind: "mutation",
26892
27135
  auth: "admin"
@@ -26896,6 +27139,12 @@ method(object({
26896
27139
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26897
27140
  kind: "mutation",
26898
27141
  auth: "admin"
27142
+ }), method(object({}), RecordingPlacementViewSchema, {
27143
+ kind: "query",
27144
+ auth: "admin"
27145
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27146
+ kind: "mutation",
27147
+ auth: "admin"
26899
27148
  });
26900
27149
  /**
26901
27150
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27278,7 +27527,26 @@ var SceneMonitorStatusSchema = object({
27278
27527
  monitors: array(SceneMonitorSchema),
27279
27528
  lastFetchedAt: number()
27280
27529
  });
27281
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27530
+ /**
27531
+ * One camera's row in a `listScenesBatch` answer.
27532
+ *
27533
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27534
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27535
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27536
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27537
+ * configured. Fanned out per camera the difference was visible — one query
27538
+ * errored while the others resolved — and a batch that returned only the rows
27539
+ * it managed would have destroyed it, silently, by making an unreachable camera
27540
+ * indistinguishable from one that answered `monitors: []`.
27541
+ *
27542
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27543
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27544
+ */
27545
+ var SceneMonitorStatusForDeviceSchema = object({
27546
+ deviceId: number(),
27547
+ status: SceneMonitorStatusSchema.nullable()
27548
+ });
27549
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27282
27550
  deviceId: number(),
27283
27551
  label: string(),
27284
27552
  roi: MaskRectShapeSchema,
@@ -28602,6 +28870,27 @@ var CameraOccupancySnapshotSchema = object({
28602
28870
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28603
28871
  });
28604
28872
  /**
28873
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28874
+ *
28875
+ * THREE outcomes, and the single-camera method could only express two of them
28876
+ * because `snapshot: null` was already spoken for:
28877
+ *
28878
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28879
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28880
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28881
+ * - `read: 'unreadable'` — the owner could not answer for this
28882
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28883
+ *
28884
+ * Collapsing the last two is the failure this field exists to prevent: a
28885
+ * hydration that threw would otherwise render as an empty Stationary section,
28886
+ * which is a definite claim about a camera nobody could read.
28887
+ */
28888
+ var CameraOccupancySnapshotForDeviceSchema = object({
28889
+ deviceId: number(),
28890
+ read: _enum(["read", "unreadable"]),
28891
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28892
+ });
28893
+ /**
28605
28894
  * Time-series resolution. The history methods return one bucket per
28606
28895
  * step over the requested range. Smaller resolutions cost more
28607
28896
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28625,7 +28914,7 @@ var HistoryPointSchema = object({
28625
28914
  /** Object count averaged over the bucket (rounded to nearest integer). */
28626
28915
  count: number().int().nonnegative()
28627
28916
  });
28628
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28917
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28629
28918
  deviceId: number(),
28630
28919
  zoneId: string(),
28631
28920
  className: string().optional()
@@ -30226,6 +30515,12 @@ Object.freeze({
30226
30515
  addonId: null,
30227
30516
  access: "delete"
30228
30517
  },
30518
+ "deviceManager.renameLocation": {
30519
+ capName: "device-manager",
30520
+ capScope: "system",
30521
+ addonId: null,
30522
+ access: "create"
30523
+ },
30229
30524
  "deviceManager.runDeviceAction": {
30230
30525
  capName: "device-manager",
30231
30526
  capScope: "system",
@@ -31918,13 +32213,19 @@ Object.freeze({
31918
32213
  addonId: null,
31919
32214
  access: "view"
31920
32215
  },
31921
- "pipelineAnalytics.getGroup": {
32216
+ "pipelineAnalytics.getKeyEvents": {
31922
32217
  capName: "pipeline-analytics",
31923
32218
  capScope: "device",
31924
32219
  addonId: null,
31925
32220
  access: "view"
31926
32221
  },
31927
- "pipelineAnalytics.getKeyEvents": {
32222
+ "pipelineAnalytics.getKeyEventsBatch": {
32223
+ capName: "pipeline-analytics",
32224
+ capScope: "device",
32225
+ addonId: null,
32226
+ access: "view"
32227
+ },
32228
+ "pipelineAnalytics.getMediaReclaimStatus": {
31928
32229
  capName: "pipeline-analytics",
31929
32230
  capScope: "device",
31930
32231
  addonId: null,
@@ -32014,12 +32315,6 @@ Object.freeze({
32014
32315
  addonId: null,
32015
32316
  access: "view"
32016
32317
  },
32017
- "pipelineAnalytics.listGroups": {
32018
- capName: "pipeline-analytics",
32019
- capScope: "device",
32020
- addonId: null,
32021
- access: "view"
32022
- },
32023
32318
  "pipelineAnalytics.listOpsLog": {
32024
32319
  capName: "pipeline-analytics",
32025
32320
  capScope: "device",
@@ -32104,6 +32399,12 @@ Object.freeze({
32104
32399
  addonId: null,
32105
32400
  access: "create"
32106
32401
  },
32402
+ "pipelineAnalytics.reclaimDebugMedia": {
32403
+ capName: "pipeline-analytics",
32404
+ capScope: "device",
32405
+ addonId: null,
32406
+ access: "create"
32407
+ },
32107
32408
  "pipelineAnalytics.reconcileFromDisk": {
32108
32409
  capName: "pipeline-analytics",
32109
32410
  capScope: "device",
@@ -33028,6 +33329,12 @@ Object.freeze({
33028
33329
  addonId: null,
33029
33330
  access: "view"
33030
33331
  },
33332
+ "recording.getPlacement": {
33333
+ capName: "recording",
33334
+ capScope: "system",
33335
+ addonId: null,
33336
+ access: "view"
33337
+ },
33031
33338
  "recording.getPlaybackManifest": {
33032
33339
  capName: "recording",
33033
33340
  capScope: "system",
@@ -33106,6 +33413,12 @@ Object.freeze({
33106
33413
  addonId: null,
33107
33414
  access: "view"
33108
33415
  },
33416
+ "recording.reconcileLedgerAgainstDisk": {
33417
+ capName: "recording",
33418
+ capScope: "system",
33419
+ addonId: null,
33420
+ access: "create"
33421
+ },
33109
33422
  "recording.refreshStorageLocationsForMigration": {
33110
33423
  capName: "recording",
33111
33424
  capScope: "system",
@@ -33148,6 +33461,12 @@ Object.freeze({
33148
33461
  addonId: null,
33149
33462
  access: "create"
33150
33463
  },
33464
+ "recording.setDevicePlacement": {
33465
+ capName: "recording",
33466
+ capScope: "system",
33467
+ addonId: null,
33468
+ access: "create"
33469
+ },
33151
33470
  "recording.startStorageMigrationMove": {
33152
33471
  capName: "recording",
33153
33472
  capScope: "system",
@@ -33232,6 +33551,12 @@ Object.freeze({
33232
33551
  addonId: null,
33233
33552
  access: "view"
33234
33553
  },
33554
+ "sceneMonitor.listScenesBatch": {
33555
+ capName: "scene-monitor",
33556
+ capScope: "device",
33557
+ addonId: null,
33558
+ access: "view"
33559
+ },
33235
33560
  "sceneMonitor.recheckNow": {
33236
33561
  capName: "scene-monitor",
33237
33562
  capScope: "device",
@@ -33586,12 +33911,36 @@ Object.freeze({
33586
33911
  addonId: null,
33587
33912
  access: "create"
33588
33913
  },
33914
+ "storageMigration.cleanupCancel": {
33915
+ capName: "storage-migration",
33916
+ capScope: "system",
33917
+ addonId: null,
33918
+ access: "create"
33919
+ },
33920
+ "storageMigration.cleanupStart": {
33921
+ capName: "storage-migration",
33922
+ capScope: "system",
33923
+ addonId: null,
33924
+ access: "create"
33925
+ },
33926
+ "storageMigration.cleanupStatus": {
33927
+ capName: "storage-migration",
33928
+ capScope: "system",
33929
+ addonId: null,
33930
+ access: "view"
33931
+ },
33589
33932
  "storageMigration.drain": {
33590
33933
  capName: "storage-migration",
33591
33934
  capScope: "system",
33592
33935
  addonId: null,
33593
33936
  access: "create"
33594
33937
  },
33938
+ "storageMigration.history": {
33939
+ capName: "storage-migration",
33940
+ capScope: "system",
33941
+ addonId: null,
33942
+ access: "view"
33943
+ },
33595
33944
  "storageMigration.movers": {
33596
33945
  capName: "storage-migration",
33597
33946
  capScope: "system",
@@ -34588,6 +34937,12 @@ Object.freeze({
34588
34937
  addonId: null,
34589
34938
  access: "view"
34590
34939
  },
34940
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34941
+ capName: "zone-analytics",
34942
+ capScope: "device",
34943
+ addonId: null,
34944
+ access: "view"
34945
+ },
34591
34946
  "zoneAnalytics.getUnzonedHistory": {
34592
34947
  capName: "zone-analytics",
34593
34948
  capScope: "device",
@@ -35582,14 +35937,14 @@ Object.freeze({
35582
35937
  form: "single",
35583
35938
  optional: true
35584
35939
  }],
35585
- "pipelineAnalytics.getGroup": [{
35940
+ "pipelineAnalytics.getKeyEvents": [{
35586
35941
  name: "deviceId",
35587
35942
  form: "single",
35588
35943
  optional: false
35589
35944
  }],
35590
- "pipelineAnalytics.getKeyEvents": [{
35591
- name: "deviceId",
35592
- form: "single",
35945
+ "pipelineAnalytics.getKeyEventsBatch": [{
35946
+ name: "deviceIds",
35947
+ form: "array",
35593
35948
  optional: false
35594
35949
  }],
35595
35950
  "pipelineAnalytics.getMotionEvents": [{
@@ -35647,11 +36002,6 @@ Object.freeze({
35647
36002
  form: "single",
35648
36003
  optional: false
35649
36004
  }],
35650
- "pipelineAnalytics.listGroups": [{
35651
- name: "deviceIds",
35652
- form: "array",
35653
- optional: false
35654
- }],
35655
36005
  "pipelineAnalytics.listOpsLog": [{
35656
36006
  name: "deviceId",
35657
36007
  form: "single",
@@ -35697,6 +36047,11 @@ Object.freeze({
35697
36047
  form: "single",
35698
36048
  optional: true
35699
36049
  }],
36050
+ "pipelineAnalytics.reclaimDebugMedia": [{
36051
+ name: "deviceIds",
36052
+ form: "array",
36053
+ optional: true
36054
+ }],
35700
36055
  "pipelineAnalytics.reconcileFromDisk": [{
35701
36056
  name: "deviceId",
35702
36057
  form: "single",
@@ -36032,6 +36387,11 @@ Object.freeze({
36032
36387
  form: "single",
36033
36388
  optional: false
36034
36389
  }],
36390
+ "recording.reconcileLedgerAgainstDisk": [{
36391
+ name: "deviceId",
36392
+ form: "single",
36393
+ optional: true
36394
+ }],
36035
36395
  "recording.relocateFootage": [{
36036
36396
  name: "deviceId",
36037
36397
  form: "single",
@@ -36057,6 +36417,11 @@ Object.freeze({
36057
36417
  form: "single",
36058
36418
  optional: false
36059
36419
  }],
36420
+ "recording.setDevicePlacement": [{
36421
+ name: "deviceId",
36422
+ form: "single",
36423
+ optional: false
36424
+ }],
36060
36425
  "recording.startStorageMigrationMove": [{
36061
36426
  name: "deviceId",
36062
36427
  form: "single",
@@ -36097,6 +36462,11 @@ Object.freeze({
36097
36462
  form: "single",
36098
36463
  optional: false
36099
36464
  }],
36465
+ "sceneMonitor.listScenesBatch": [{
36466
+ name: "deviceIds",
36467
+ form: "array",
36468
+ optional: false
36469
+ }],
36100
36470
  "sceneMonitor.recheckNow": [{
36101
36471
  name: "deviceId",
36102
36472
  form: "single",
@@ -36358,6 +36728,11 @@ Object.freeze({
36358
36728
  form: "single",
36359
36729
  optional: false
36360
36730
  }],
36731
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36732
+ name: "deviceIds",
36733
+ form: "array",
36734
+ optional: false
36735
+ }],
36361
36736
  "zoneAnalytics.getUnzonedHistory": [{
36362
36737
  name: "deviceId",
36363
36738
  form: "single",