@camstack/addon-export-google 0.1.12 → 0.1.14

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.
@@ -8207,6 +8207,20 @@ var RelocateJobSchema = object({
8207
8207
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8208
8208
  */
8209
8209
  rowsReconciled: number().int().nonnegative().optional(),
8210
+ /**
8211
+ * Rows this run FORGOT because the file they name is not on disk.
8212
+ *
8213
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8214
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8215
+ * and the durable row is dropped through the same channel eviction uses. It
8216
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8217
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8218
+ * the same failure as one that quietly skips them (D295).
8219
+ *
8220
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8221
+ * ledger claimed 5.65 GB of footage that no longer existed.
8222
+ */
8223
+ rowsForgotten: number().int().nonnegative().optional(),
8210
8224
  startedAt: number(),
8211
8225
  finishedAt: number().nullable(),
8212
8226
  error: string().nullable()
@@ -8271,6 +8285,13 @@ var MediaRelocateModeSchema = _enum([
8271
8285
  ]);
8272
8286
  var RelocateMediaInputSchema = object({
8273
8287
  toLocationId: string(),
8288
+ /**
8289
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8290
+ * every row that is not already on `toLocationId` (the historical
8291
+ * behaviour). A named source is what a from→to migration needs: without it
8292
+ * "move events off disk 2" also emptied disk 1.
8293
+ */
8294
+ fromLocationId: string().optional(),
8274
8295
  throttleMbps: number().min(1).max(1e3).optional(),
8275
8296
  /** Omitted = `move`, the pre-existing behaviour. */
8276
8297
  mode: MediaRelocateModeSchema.optional()
@@ -8338,6 +8359,19 @@ var StorageMigrationDestinationsSchema = object({
8338
8359
  galleryMedia: string().min(1).optional()
8339
8360
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8340
8361
  /**
8362
+ * Optional named source per class. Omitted = the class's current default
8363
+ * (the historical behaviour). A named source that is NOT the default is a
8364
+ * drain of that disk: bytes move, the default stays, and the source is
8365
+ * disabled when the move finishes.
8366
+ */
8367
+ var StorageMigrationSourcesSchema = object({
8368
+ recordings: string().min(1).optional(),
8369
+ recordingsLow: string().min(1).optional(),
8370
+ eventMedia: string().min(1).optional(),
8371
+ backups: string().min(1).optional(),
8372
+ galleryMedia: string().min(1).optional()
8373
+ }).optional();
8374
+ /**
8341
8375
  * How a migration sequences the cutover against the byte move.
8342
8376
  *
8343
8377
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8359,6 +8393,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8359
8393
  /** Shared input for planning and starting an orchestrated storage migration. */
8360
8394
  var StorageMigrationInputSchema = object({
8361
8395
  destinations: StorageMigrationDestinationsSchema,
8396
+ /** Omitted = each class's current default. */
8397
+ sources: StorageMigrationSourcesSchema,
8362
8398
  throttleMbps: number().min(1).max(1e3).optional(),
8363
8399
  /** Omitted = `blocking`, which stays the default. */
8364
8400
  mode: StorageMigrationModeSchema.optional()
@@ -8438,6 +8474,13 @@ var StorageMigrationMoveSchema = object({
8438
8474
  storageClass: StorageMigrationClassSchema,
8439
8475
  fromLocationId: string(),
8440
8476
  toLocationId: string(),
8477
+ /**
8478
+ * True when `from` was NOT the class default at plan time. The move still
8479
+ * copies bytes, but the default is left alone and the source is disabled
8480
+ * once the copy verifies. Absent on jobs planned before this field existed
8481
+ * — those jobs always repointed, which is `false`.
8482
+ */
8483
+ freezeSource: boolean().optional(),
8441
8484
  moverJobId: string().nullable(),
8442
8485
  state: RelocateJobStateSchema.nullable(),
8443
8486
  error: string().nullable(),
@@ -8451,6 +8494,7 @@ var StorageMigrationJobSchema = object({
8451
8494
  * can tell a seconds-long cutover from a thirty-hour one. */
8452
8495
  mode: StorageMigrationModeSchema,
8453
8496
  destinations: StorageMigrationDestinationsSchema,
8497
+ sources: StorageMigrationSourcesSchema,
8454
8498
  throttleMbps: number(),
8455
8499
  moves: array(StorageMigrationMoveSchema),
8456
8500
  pauseLeaseId: string().nullable(),
@@ -8476,6 +8520,7 @@ var StorageMigrationFindingSchema = object({
8476
8520
  });
8477
8521
  var StorageMigrationPlanSchema = object({
8478
8522
  destinations: StorageMigrationDestinationsSchema,
8523
+ sources: StorageMigrationSourcesSchema,
8479
8524
  /** The mode this plan was built for. A plan is only valid for its mode: the
8480
8525
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8481
8526
  * it. */
@@ -8483,7 +8528,8 @@ var StorageMigrationPlanSchema = object({
8483
8528
  moves: array(object({
8484
8529
  storageClass: StorageMigrationClassSchema,
8485
8530
  fromLocationId: string(),
8486
- toLocationId: string()
8531
+ toLocationId: string(),
8532
+ freezeSource: boolean().optional()
8487
8533
  })),
8488
8534
  findings: array(StorageMigrationFindingSchema)
8489
8535
  });
@@ -8570,16 +8616,142 @@ var RelocateResidueSchema = object({
8570
8616
  segments: number().int().nonnegative(),
8571
8617
  bytes: number().int().nonnegative()
8572
8618
  }).nullable();
8619
+ /**
8620
+ * Ask one location whether its durable hour rows describe the disk — the walk
8621
+ * (D319).
8622
+ *
8623
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8624
+ * missing tool is the question, and the dry run is how they sanity-check the
8625
+ * destructive run before authorising it.
8626
+ */
8627
+ var LedgerWalkInputSchema = object({
8628
+ locationId: string().min(1),
8629
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8630
+ apply: boolean().optional(),
8631
+ /** Narrow to one camera. */
8632
+ deviceId: number().int().positive().optional(),
8633
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8634
+ profiles: array(string().min(1)).optional()
8635
+ });
8636
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8637
+ var LedgerWalkRefusalSchema = _enum([
8638
+ "location-unknown",
8639
+ "source-writable",
8640
+ "no-ledger",
8641
+ "archive-unreadable",
8642
+ "anchor-absent",
8643
+ "anchor-unreadable",
8644
+ "anchor-moved"
8645
+ ]);
8646
+ _enum([
8647
+ "live-tail",
8648
+ "listing-error",
8649
+ "path-mismatch",
8650
+ "durable-refused"
8651
+ ]);
8652
+ /** Every skip reason, always present, always a number — so a reason that never
8653
+ * fired reports as zero rather than absent and the report shape is constant
8654
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8655
+ var LedgerWalkSkipCountsSchema = object({
8656
+ "live-tail": number().int().nonnegative(),
8657
+ "listing-error": number().int().nonnegative(),
8658
+ "path-mismatch": number().int().nonnegative(),
8659
+ "durable-refused": number().int().nonnegative()
8660
+ });
8661
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8662
+ var LedgerWalkDeviceReportSchema = object({
8663
+ deviceId: number().int(),
8664
+ hoursWalked: number().int().nonnegative(),
8665
+ hoursMissing: number().int().nonnegative(),
8666
+ ghostSegments: number().int().nonnegative(),
8667
+ ghostBytes: number().int().nonnegative(),
8668
+ forgottenSegments: number().int().nonnegative(),
8669
+ orphanFiles: number().int().nonnegative()
8670
+ });
8671
+ /**
8672
+ * What one walk claimed, listed, found and (only when armed) forgot.
8673
+ *
8674
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8675
+ * walk that saw a fraction of the location is visible in its own report rather
8676
+ * than in the absence of one.
8677
+ */
8678
+ var LedgerWalkReportSchema = object({
8679
+ locationId: string(),
8680
+ applied: boolean(),
8681
+ refused: LedgerWalkRefusalSchema.nullable(),
8682
+ archiveSegments: number().int().nonnegative().nullable(),
8683
+ archiveBytes: number().int().nonnegative().nullable(),
8684
+ hoursClaimed: number().int().nonnegative(),
8685
+ hoursWalked: number().int().nonnegative(),
8686
+ hoursMissing: number().int().nonnegative(),
8687
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8688
+ listings: number().int().nonnegative(),
8689
+ segmentsClaimed: number().int().nonnegative(),
8690
+ ghostSegments: number().int().nonnegative(),
8691
+ ghostBytes: number().int().nonnegative(),
8692
+ ghostHoursWhole: number().int().nonnegative(),
8693
+ forgottenSegments: number().int().nonnegative(),
8694
+ forgottenBytes: number().int().nonnegative(),
8695
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8696
+ orphanFiles: number().int().nonnegative(),
8697
+ orphanSample: array(string()).readonly(),
8698
+ hoursSkipped: number().int().nonnegative(),
8699
+ skippedByReason: LedgerWalkSkipCountsSchema,
8700
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8701
+ bounded: boolean(),
8702
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8703
+ });
8573
8704
  /** How many rows a media pass would still act on against a given target — the
8574
8705
  * media lane's denominator AND its residue, from ONE derivation so the two can
8575
8706
  * never disagree. `null` = the count could not be taken. */
8576
8707
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8577
8708
  var RelocatableMediaCountInputSchema = object({
8578
8709
  toLocationId: string().min(1),
8710
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8711
+ fromLocationId: string().optional(),
8579
8712
  /** Omitted = `move`. */
8580
8713
  mode: MediaRelocateModeSchema.optional()
8581
8714
  });
8582
8715
  /**
8716
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8717
+ * ghost ledger entries on frozen footage locations.
8718
+ *
8719
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8720
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8721
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8722
+ * with no operator-visible status.
8723
+ */
8724
+ var StorageCleanupPhaseSchema = _enum([
8725
+ "orphans",
8726
+ "debug-media",
8727
+ "ghost-ledger",
8728
+ "done",
8729
+ "failed",
8730
+ "cancelled"
8731
+ ]);
8732
+ var StorageCleanupInputSchema = object({
8733
+ /** Also walk motion stills / track filmstrips. Off by default. */
8734
+ includeDebugMedia: boolean().optional() });
8735
+ var StorageCleanupJobSchema = object({
8736
+ jobId: string(),
8737
+ phase: StorageCleanupPhaseSchema,
8738
+ includeDebugMedia: boolean(),
8739
+ orphansReclaimed: number().int().nonnegative(),
8740
+ orphanBytesReclaimed: number().int().nonnegative(),
8741
+ debugMediaReclaimed: number().int().nonnegative(),
8742
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8743
+ ghostsForgotten: number().int().nonnegative(),
8744
+ ghostBytesForgotten: number().int().nonnegative(),
8745
+ /** Short operator-facing line: current collection, pass, or location. */
8746
+ detail: string().nullable(),
8747
+ cancelRequested: boolean(),
8748
+ startedAt: number(),
8749
+ updatedAt: number(),
8750
+ finishedAt: number().nullable(),
8751
+ error: string().nullable()
8752
+ });
8753
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8754
+ /**
8583
8755
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8584
8756
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8585
8757
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8607,11 +8779,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8607
8779
  * The default location for a type uses `id === <type>:default` by
8608
8780
  * convention (the bare type ref like `'backups'` resolves to it).
8609
8781
  *
8610
- * `isSystem: true` marks a location as orchestrator-seeded and
8611
- * undeletable. The bootstrap-installed defaults (one per type) carry
8612
- * this flag; operator-added locations don't. Editing the config of
8613
- * a system location is allowed (path migration, provider swap) but
8614
- * deleting it is rejected at the cap level.
8782
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8783
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8784
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8785
+ * / last-enabled, not on this bit.
8615
8786
  */
8616
8787
  var StorageLocationSchema = object({
8617
8788
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12946,6 +13117,12 @@ method(object({
12946
13117
  }), _void(), {
12947
13118
  kind: "mutation",
12948
13119
  auth: "admin"
13120
+ }), method(object({
13121
+ from: string(),
13122
+ to: string()
13123
+ }), object({ moved: number() }), {
13124
+ kind: "mutation",
13125
+ auth: "admin"
12949
13126
  }), method(object({
12950
13127
  deviceId: number(),
12951
13128
  disabled: boolean()
@@ -18892,53 +19069,15 @@ var RecentTracksPageSchema = object({
18892
19069
  /** Cursor for the next page, or null when this page is the last. */
18893
19070
  nextCursor: string().nullable()
18894
19071
  });
18895
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18896
- var LIST_GROUPS_MAX_LIMIT = 100;
18897
- var AnalyticsGroupRecordSchema = object({
18898
- id: string(),
18899
- deviceId: number().int(),
18900
- openedAt: number().int(),
18901
- closedAt: number().int(),
18902
- timestamp: number().int(),
18903
- memberCount: number().int(),
18904
- memberTrackIds: array(string()).readonly(),
18905
- className: string(),
18906
- classes: array(string()).readonly(),
18907
- /** Relative event-media path, or null when the group has no picture yet. */
18908
- mediaUrl: string().nullable(),
18909
- singleton: boolean()
18910
- });
18911
- var AnalyticsGroupMemberSchema = object({
18912
- trackId: string(),
18913
- deviceId: number().int(),
18914
- className: string(),
18915
- firstSeen: number().int(),
18916
- lastSeen: number().int(),
18917
- mediaUrl: string().nullable()
18918
- });
18919
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18920
- var ListGroupsQueryInput = object({
18921
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18922
- deviceIds: array(number()),
18923
- /** Window lower bound on `closedAt` (inclusive). */
18924
- since: number().optional(),
18925
- /** Window upper bound on `openedAt` (inclusive). */
18926
- until: number().optional(),
18927
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18928
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
18929
- cursor: string().optional()
18930
- });
18931
- var ListGroupsPageSchema = object({
18932
- groups: array(AnalyticsGroupRecordSchema).readonly(),
18933
- nextCursor: string().nullable()
18934
- });
19072
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19073
+ var KEY_EVENTS_MAX_LIMIT = 200;
18935
19074
  var KeyEventQueryInput = object({
18936
19075
  deviceId: number(),
18937
19076
  /** Window lower bound (track firstSeen ≥ since). */
18938
19077
  since: number(),
18939
19078
  /** Window upper bound (track firstSeen ≤ until). */
18940
19079
  until: number(),
18941
- limit: number().int().min(1).max(200).default(50),
19080
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18942
19081
  /** Drop tracks scoring below this importance. */
18943
19082
  minImportance: number().min(0).max(1).optional(),
18944
19083
  /** Restrict to a single class (e.g. 'person'). */
@@ -18960,6 +19099,32 @@ var KeyEventSchema = object({
18960
19099
  ...TrackFlagFields,
18961
19100
  ...TrackRetrainFields
18962
19101
  });
19102
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19103
+ var KeyEventBatchQueryInput = object({
19104
+ deviceIds: array(number()).min(1).max(200),
19105
+ since: number(),
19106
+ until: number(),
19107
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19108
+ * across the set, which would let a busy camera starve a quiet one of its
19109
+ * rows and change what the merged feed contains. */
19110
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19111
+ minImportance: number().min(0).max(1).optional(),
19112
+ classFilter: string().optional()
19113
+ });
19114
+ /**
19115
+ * One camera's key events in a batch answer.
19116
+ *
19117
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19118
+ * error rather than throwing, so a camera whose store read failed and one with
19119
+ * no events in the window were ALREADY indistinguishable per camera — the
19120
+ * batch does not make that worse, and the row keeps the deviceId the single
19121
+ * method's output never carried (the caller used to stamp it from the fan-out
19122
+ * key, which only worked because there was one query per camera).
19123
+ */
19124
+ var KeyEventsForDeviceSchema = object({
19125
+ deviceId: number(),
19126
+ events: array(KeyEventSchema).readonly()
19127
+ });
18963
19128
  object({
18964
19129
  trackId: string(),
18965
19130
  className: string(),
@@ -19007,9 +19172,7 @@ var TrackCascadeCountsSchema = object({
19007
19172
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19008
19173
  plates: number().int(),
19009
19174
  /** Per-track CLIP search vectors removed (best-effort). */
19010
- embeddings: number().int(),
19011
- /** Group membership + group rows removed with their last member (best-effort). */
19012
- groups: number().int()
19175
+ embeddings: number().int()
19013
19176
  });
19014
19177
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19015
19178
  var DiskReconcileCountsSchema = object({
@@ -19179,6 +19342,47 @@ var RebuildStatusSchema = object({
19179
19342
  /** Present when the pass ended by throwing. */
19180
19343
  error: string().nullable()
19181
19344
  });
19345
+ /**
19346
+ * Acknowledgement that a debug-media reclaim STARTED.
19347
+ *
19348
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19349
+ * it runs detached and this returns immediately. Awaiting it is how the
19350
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19351
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19352
+ */
19353
+ var MediaReclaimStartResultSchema = object({
19354
+ started: boolean(),
19355
+ /** True when a pass was already running; the new request is ignored. */
19356
+ alreadyRunning: boolean()
19357
+ });
19358
+ var MediaReclaimInputSchema = object({
19359
+ mode: _enum(["report", "reclaim"]).default("report"),
19360
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19361
+ deviceIds: array(number().int()).min(1).optional(),
19362
+ restart: boolean().optional(),
19363
+ pageSize: number().int().min(50).max(5e3).optional(),
19364
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19365
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19366
+ maxBytesPerRun: number().int().min(1).optional(),
19367
+ budgetMinutes: number().int().min(1).max(720).optional(),
19368
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19369
+ graceMinutes: number().int().min(1).max(10080).optional()
19370
+ });
19371
+ var MediaReclaimStatusSchema = object({
19372
+ running: boolean(),
19373
+ mode: _enum(["report", "reclaim"]).nullable(),
19374
+ totalExamined: number(),
19375
+ totalEligible: number(),
19376
+ totalReclaimed: number(),
19377
+ totalBytesReclaimed: number(),
19378
+ totalRefused: number(),
19379
+ /** Device+scope windows finished in this pass. */
19380
+ devicesDone: number(),
19381
+ complete: boolean().nullable(),
19382
+ startedAtMs: number().nullable(),
19383
+ finishedAtMs: number().nullable(),
19384
+ error: string().nullable()
19385
+ });
19182
19386
  var ReplayFrameInputSchema = object({
19183
19387
  timestamp: number(),
19184
19388
  frame: PipelineRunResultBridge
@@ -19217,10 +19421,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19217
19421
  * stationary registry). Default false: the timeline lists passages,
19218
19422
  * not parking records (operator decision, 2026-08-15). */
19219
19423
  includeStationary: boolean().optional()
19220
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19221
- deviceId: number(),
19222
- groupId: string().min(1)
19223
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19424
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19224
19425
  kind: "mutation",
19225
19426
  auth: "admin"
19226
19427
  }), 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({
@@ -19229,7 +19430,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19229
19430
  until: number().optional(),
19230
19431
  kinds: array(string()).optional(),
19231
19432
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19232
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19433
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19233
19434
  deviceId: number(),
19234
19435
  since: number(),
19235
19436
  until: number(),
@@ -19320,6 +19521,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19320
19521
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19321
19522
  kind: "query",
19322
19523
  auth: "admin"
19524
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19525
+ kind: "mutation",
19526
+ auth: "admin"
19527
+ }), method(object({}), MediaReclaimStatusSchema, {
19528
+ kind: "query",
19529
+ auth: "admin"
19323
19530
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19324
19531
  kind: "query",
19325
19532
  auth: "admin"
@@ -21290,7 +21497,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21290
21497
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21291
21498
  kind: "mutation",
21292
21499
  auth: "admin"
21293
- });
21500
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21501
+ kind: "mutation",
21502
+ auth: "admin"
21503
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21504
+ kind: "mutation",
21505
+ auth: "admin"
21506
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21294
21507
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21295
21508
  providerId: string().min(1),
21296
21509
  displayName: string().min(1),
@@ -26680,6 +26893,33 @@ var RecordingRebalanceInputSchema = object({
26680
26893
  minMoveGb: number().min(0).optional()
26681
26894
  });
26682
26895
  /**
26896
+ * Operator-facing placement of one camera onto a recordings location.
26897
+ *
26898
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26899
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26900
+ * currently write — the plan, which may disagree with the pin when Auto.
26901
+ */
26902
+ var RecordingDevicePlacementSchema = object({
26903
+ deviceId: number().int(),
26904
+ profile: string(),
26905
+ locationId: string()
26906
+ });
26907
+ var RecordingDevicePinSchema = object({
26908
+ deviceId: number().int(),
26909
+ /** Recordings-class location this camera is pinned to. */
26910
+ locationId: string()
26911
+ });
26912
+ var RecordingPlacementViewSchema = object({
26913
+ assignments: array(RecordingDevicePlacementSchema),
26914
+ pins: array(RecordingDevicePinSchema),
26915
+ defaultLocations: record(string(), string())
26916
+ });
26917
+ var RecordingSetDevicePlacementInputSchema = object({
26918
+ deviceId: number().int(),
26919
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26920
+ locationId: string().nullable()
26921
+ });
26922
+ /**
26683
26923
  * Result of locating footage at a wall-clock instant for one device/profile.
26684
26924
  * `segment` carries the covering segment's window; `gap` reports the forward
26685
26925
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26893,6 +27133,9 @@ method(object({
26893
27133
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26894
27134
  kind: "query",
26895
27135
  auth: "admin"
27136
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27137
+ kind: "mutation",
27138
+ auth: "admin"
26896
27139
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26897
27140
  kind: "mutation",
26898
27141
  auth: "admin"
@@ -26902,6 +27145,12 @@ method(object({
26902
27145
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26903
27146
  kind: "mutation",
26904
27147
  auth: "admin"
27148
+ }), method(object({}), RecordingPlacementViewSchema, {
27149
+ kind: "query",
27150
+ auth: "admin"
27151
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27152
+ kind: "mutation",
27153
+ auth: "admin"
26905
27154
  });
26906
27155
  /**
26907
27156
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27284,7 +27533,26 @@ var SceneMonitorStatusSchema = object({
27284
27533
  monitors: array(SceneMonitorSchema),
27285
27534
  lastFetchedAt: number()
27286
27535
  });
27287
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27536
+ /**
27537
+ * One camera's row in a `listScenesBatch` answer.
27538
+ *
27539
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27540
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27541
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27542
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27543
+ * configured. Fanned out per camera the difference was visible — one query
27544
+ * errored while the others resolved — and a batch that returned only the rows
27545
+ * it managed would have destroyed it, silently, by making an unreachable camera
27546
+ * indistinguishable from one that answered `monitors: []`.
27547
+ *
27548
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27549
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27550
+ */
27551
+ var SceneMonitorStatusForDeviceSchema = object({
27552
+ deviceId: number(),
27553
+ status: SceneMonitorStatusSchema.nullable()
27554
+ });
27555
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27288
27556
  deviceId: number(),
27289
27557
  label: string(),
27290
27558
  roi: MaskRectShapeSchema,
@@ -28615,6 +28883,27 @@ var CameraOccupancySnapshotSchema = object({
28615
28883
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28616
28884
  });
28617
28885
  /**
28886
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28887
+ *
28888
+ * THREE outcomes, and the single-camera method could only express two of them
28889
+ * because `snapshot: null` was already spoken for:
28890
+ *
28891
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28892
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28893
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28894
+ * - `read: 'unreadable'` — the owner could not answer for this
28895
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28896
+ *
28897
+ * Collapsing the last two is the failure this field exists to prevent: a
28898
+ * hydration that threw would otherwise render as an empty Stationary section,
28899
+ * which is a definite claim about a camera nobody could read.
28900
+ */
28901
+ var CameraOccupancySnapshotForDeviceSchema = object({
28902
+ deviceId: number(),
28903
+ read: _enum(["read", "unreadable"]),
28904
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28905
+ });
28906
+ /**
28618
28907
  * Time-series resolution. The history methods return one bucket per
28619
28908
  * step over the requested range. Smaller resolutions cost more
28620
28909
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28638,7 +28927,7 @@ var HistoryPointSchema = object({
28638
28927
  /** Object count averaged over the bucket (rounded to nearest integer). */
28639
28928
  count: number().int().nonnegative()
28640
28929
  });
28641
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28930
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28642
28931
  deviceId: number(),
28643
28932
  zoneId: string(),
28644
28933
  className: string().optional()
@@ -30239,6 +30528,12 @@ Object.freeze({
30239
30528
  addonId: null,
30240
30529
  access: "delete"
30241
30530
  },
30531
+ "deviceManager.renameLocation": {
30532
+ capName: "device-manager",
30533
+ capScope: "system",
30534
+ addonId: null,
30535
+ access: "create"
30536
+ },
30242
30537
  "deviceManager.runDeviceAction": {
30243
30538
  capName: "device-manager",
30244
30539
  capScope: "system",
@@ -31931,13 +32226,19 @@ Object.freeze({
31931
32226
  addonId: null,
31932
32227
  access: "view"
31933
32228
  },
31934
- "pipelineAnalytics.getGroup": {
32229
+ "pipelineAnalytics.getKeyEvents": {
31935
32230
  capName: "pipeline-analytics",
31936
32231
  capScope: "device",
31937
32232
  addonId: null,
31938
32233
  access: "view"
31939
32234
  },
31940
- "pipelineAnalytics.getKeyEvents": {
32235
+ "pipelineAnalytics.getKeyEventsBatch": {
32236
+ capName: "pipeline-analytics",
32237
+ capScope: "device",
32238
+ addonId: null,
32239
+ access: "view"
32240
+ },
32241
+ "pipelineAnalytics.getMediaReclaimStatus": {
31941
32242
  capName: "pipeline-analytics",
31942
32243
  capScope: "device",
31943
32244
  addonId: null,
@@ -32027,12 +32328,6 @@ Object.freeze({
32027
32328
  addonId: null,
32028
32329
  access: "view"
32029
32330
  },
32030
- "pipelineAnalytics.listGroups": {
32031
- capName: "pipeline-analytics",
32032
- capScope: "device",
32033
- addonId: null,
32034
- access: "view"
32035
- },
32036
32331
  "pipelineAnalytics.listOpsLog": {
32037
32332
  capName: "pipeline-analytics",
32038
32333
  capScope: "device",
@@ -32117,6 +32412,12 @@ Object.freeze({
32117
32412
  addonId: null,
32118
32413
  access: "create"
32119
32414
  },
32415
+ "pipelineAnalytics.reclaimDebugMedia": {
32416
+ capName: "pipeline-analytics",
32417
+ capScope: "device",
32418
+ addonId: null,
32419
+ access: "create"
32420
+ },
32120
32421
  "pipelineAnalytics.reconcileFromDisk": {
32121
32422
  capName: "pipeline-analytics",
32122
32423
  capScope: "device",
@@ -33041,6 +33342,12 @@ Object.freeze({
33041
33342
  addonId: null,
33042
33343
  access: "view"
33043
33344
  },
33345
+ "recording.getPlacement": {
33346
+ capName: "recording",
33347
+ capScope: "system",
33348
+ addonId: null,
33349
+ access: "view"
33350
+ },
33044
33351
  "recording.getPlaybackManifest": {
33045
33352
  capName: "recording",
33046
33353
  capScope: "system",
@@ -33119,6 +33426,12 @@ Object.freeze({
33119
33426
  addonId: null,
33120
33427
  access: "view"
33121
33428
  },
33429
+ "recording.reconcileLedgerAgainstDisk": {
33430
+ capName: "recording",
33431
+ capScope: "system",
33432
+ addonId: null,
33433
+ access: "create"
33434
+ },
33122
33435
  "recording.refreshStorageLocationsForMigration": {
33123
33436
  capName: "recording",
33124
33437
  capScope: "system",
@@ -33161,6 +33474,12 @@ Object.freeze({
33161
33474
  addonId: null,
33162
33475
  access: "create"
33163
33476
  },
33477
+ "recording.setDevicePlacement": {
33478
+ capName: "recording",
33479
+ capScope: "system",
33480
+ addonId: null,
33481
+ access: "create"
33482
+ },
33164
33483
  "recording.startStorageMigrationMove": {
33165
33484
  capName: "recording",
33166
33485
  capScope: "system",
@@ -33245,6 +33564,12 @@ Object.freeze({
33245
33564
  addonId: null,
33246
33565
  access: "view"
33247
33566
  },
33567
+ "sceneMonitor.listScenesBatch": {
33568
+ capName: "scene-monitor",
33569
+ capScope: "device",
33570
+ addonId: null,
33571
+ access: "view"
33572
+ },
33248
33573
  "sceneMonitor.recheckNow": {
33249
33574
  capName: "scene-monitor",
33250
33575
  capScope: "device",
@@ -33599,12 +33924,36 @@ Object.freeze({
33599
33924
  addonId: null,
33600
33925
  access: "create"
33601
33926
  },
33927
+ "storageMigration.cleanupCancel": {
33928
+ capName: "storage-migration",
33929
+ capScope: "system",
33930
+ addonId: null,
33931
+ access: "create"
33932
+ },
33933
+ "storageMigration.cleanupStart": {
33934
+ capName: "storage-migration",
33935
+ capScope: "system",
33936
+ addonId: null,
33937
+ access: "create"
33938
+ },
33939
+ "storageMigration.cleanupStatus": {
33940
+ capName: "storage-migration",
33941
+ capScope: "system",
33942
+ addonId: null,
33943
+ access: "view"
33944
+ },
33602
33945
  "storageMigration.drain": {
33603
33946
  capName: "storage-migration",
33604
33947
  capScope: "system",
33605
33948
  addonId: null,
33606
33949
  access: "create"
33607
33950
  },
33951
+ "storageMigration.history": {
33952
+ capName: "storage-migration",
33953
+ capScope: "system",
33954
+ addonId: null,
33955
+ access: "view"
33956
+ },
33608
33957
  "storageMigration.movers": {
33609
33958
  capName: "storage-migration",
33610
33959
  capScope: "system",
@@ -34601,6 +34950,12 @@ Object.freeze({
34601
34950
  addonId: null,
34602
34951
  access: "view"
34603
34952
  },
34953
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34954
+ capName: "zone-analytics",
34955
+ capScope: "device",
34956
+ addonId: null,
34957
+ access: "view"
34958
+ },
34604
34959
  "zoneAnalytics.getUnzonedHistory": {
34605
34960
  capName: "zone-analytics",
34606
34961
  capScope: "device",
@@ -35595,14 +35950,14 @@ Object.freeze({
35595
35950
  form: "single",
35596
35951
  optional: true
35597
35952
  }],
35598
- "pipelineAnalytics.getGroup": [{
35953
+ "pipelineAnalytics.getKeyEvents": [{
35599
35954
  name: "deviceId",
35600
35955
  form: "single",
35601
35956
  optional: false
35602
35957
  }],
35603
- "pipelineAnalytics.getKeyEvents": [{
35604
- name: "deviceId",
35605
- form: "single",
35958
+ "pipelineAnalytics.getKeyEventsBatch": [{
35959
+ name: "deviceIds",
35960
+ form: "array",
35606
35961
  optional: false
35607
35962
  }],
35608
35963
  "pipelineAnalytics.getMotionEvents": [{
@@ -35660,11 +36015,6 @@ Object.freeze({
35660
36015
  form: "single",
35661
36016
  optional: false
35662
36017
  }],
35663
- "pipelineAnalytics.listGroups": [{
35664
- name: "deviceIds",
35665
- form: "array",
35666
- optional: false
35667
- }],
35668
36018
  "pipelineAnalytics.listOpsLog": [{
35669
36019
  name: "deviceId",
35670
36020
  form: "single",
@@ -35710,6 +36060,11 @@ Object.freeze({
35710
36060
  form: "single",
35711
36061
  optional: true
35712
36062
  }],
36063
+ "pipelineAnalytics.reclaimDebugMedia": [{
36064
+ name: "deviceIds",
36065
+ form: "array",
36066
+ optional: true
36067
+ }],
35713
36068
  "pipelineAnalytics.reconcileFromDisk": [{
35714
36069
  name: "deviceId",
35715
36070
  form: "single",
@@ -36045,6 +36400,11 @@ Object.freeze({
36045
36400
  form: "single",
36046
36401
  optional: false
36047
36402
  }],
36403
+ "recording.reconcileLedgerAgainstDisk": [{
36404
+ name: "deviceId",
36405
+ form: "single",
36406
+ optional: true
36407
+ }],
36048
36408
  "recording.relocateFootage": [{
36049
36409
  name: "deviceId",
36050
36410
  form: "single",
@@ -36070,6 +36430,11 @@ Object.freeze({
36070
36430
  form: "single",
36071
36431
  optional: false
36072
36432
  }],
36433
+ "recording.setDevicePlacement": [{
36434
+ name: "deviceId",
36435
+ form: "single",
36436
+ optional: false
36437
+ }],
36073
36438
  "recording.startStorageMigrationMove": [{
36074
36439
  name: "deviceId",
36075
36440
  form: "single",
@@ -36110,6 +36475,11 @@ Object.freeze({
36110
36475
  form: "single",
36111
36476
  optional: false
36112
36477
  }],
36478
+ "sceneMonitor.listScenesBatch": [{
36479
+ name: "deviceIds",
36480
+ form: "array",
36481
+ optional: false
36482
+ }],
36113
36483
  "sceneMonitor.recheckNow": [{
36114
36484
  name: "deviceId",
36115
36485
  form: "single",
@@ -36371,6 +36741,11 @@ Object.freeze({
36371
36741
  form: "single",
36372
36742
  optional: false
36373
36743
  }],
36744
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36745
+ name: "deviceIds",
36746
+ form: "array",
36747
+ optional: false
36748
+ }],
36374
36749
  "zoneAnalytics.getUnzonedHistory": [{
36375
36750
  name: "deviceId",
36376
36751
  form: "single",