@camstack/addon-export-hap 1.2.59 → 1.2.61

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.
@@ -8672,6 +8672,20 @@ var RelocateJobSchema = object({
8672
8672
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8673
8673
  */
8674
8674
  rowsReconciled: number().int().nonnegative().optional(),
8675
+ /**
8676
+ * Rows this run FORGOT because the file they name is not on disk.
8677
+ *
8678
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8679
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8680
+ * and the durable row is dropped through the same channel eviction uses. It
8681
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8682
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8683
+ * the same failure as one that quietly skips them (D295).
8684
+ *
8685
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8686
+ * ledger claimed 5.65 GB of footage that no longer existed.
8687
+ */
8688
+ rowsForgotten: number().int().nonnegative().optional(),
8675
8689
  startedAt: number(),
8676
8690
  finishedAt: number().nullable(),
8677
8691
  error: string().nullable()
@@ -8736,6 +8750,13 @@ var MediaRelocateModeSchema = _enum([
8736
8750
  ]);
8737
8751
  var RelocateMediaInputSchema = object({
8738
8752
  toLocationId: string(),
8753
+ /**
8754
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8755
+ * every row that is not already on `toLocationId` (the historical
8756
+ * behaviour). A named source is what a from→to migration needs: without it
8757
+ * "move events off disk 2" also emptied disk 1.
8758
+ */
8759
+ fromLocationId: string().optional(),
8739
8760
  throttleMbps: number().min(1).max(1e3).optional(),
8740
8761
  /** Omitted = `move`, the pre-existing behaviour. */
8741
8762
  mode: MediaRelocateModeSchema.optional()
@@ -8803,6 +8824,19 @@ var StorageMigrationDestinationsSchema = object({
8803
8824
  galleryMedia: string().min(1).optional()
8804
8825
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8805
8826
  /**
8827
+ * Optional named source per class. Omitted = the class's current default
8828
+ * (the historical behaviour). A named source that is NOT the default is a
8829
+ * drain of that disk: bytes move, the default stays, and the source is
8830
+ * disabled when the move finishes.
8831
+ */
8832
+ var StorageMigrationSourcesSchema = object({
8833
+ recordings: string().min(1).optional(),
8834
+ recordingsLow: string().min(1).optional(),
8835
+ eventMedia: string().min(1).optional(),
8836
+ backups: string().min(1).optional(),
8837
+ galleryMedia: string().min(1).optional()
8838
+ }).optional();
8839
+ /**
8806
8840
  * How a migration sequences the cutover against the byte move.
8807
8841
  *
8808
8842
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8824,6 +8858,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8824
8858
  /** Shared input for planning and starting an orchestrated storage migration. */
8825
8859
  var StorageMigrationInputSchema = object({
8826
8860
  destinations: StorageMigrationDestinationsSchema,
8861
+ /** Omitted = each class's current default. */
8862
+ sources: StorageMigrationSourcesSchema,
8827
8863
  throttleMbps: number().min(1).max(1e3).optional(),
8828
8864
  /** Omitted = `blocking`, which stays the default. */
8829
8865
  mode: StorageMigrationModeSchema.optional()
@@ -8903,6 +8939,13 @@ var StorageMigrationMoveSchema = object({
8903
8939
  storageClass: StorageMigrationClassSchema,
8904
8940
  fromLocationId: string(),
8905
8941
  toLocationId: string(),
8942
+ /**
8943
+ * True when `from` was NOT the class default at plan time. The move still
8944
+ * copies bytes, but the default is left alone and the source is disabled
8945
+ * once the copy verifies. Absent on jobs planned before this field existed
8946
+ * — those jobs always repointed, which is `false`.
8947
+ */
8948
+ freezeSource: boolean().optional(),
8906
8949
  moverJobId: string().nullable(),
8907
8950
  state: RelocateJobStateSchema.nullable(),
8908
8951
  error: string().nullable(),
@@ -8916,6 +8959,7 @@ var StorageMigrationJobSchema = object({
8916
8959
  * can tell a seconds-long cutover from a thirty-hour one. */
8917
8960
  mode: StorageMigrationModeSchema,
8918
8961
  destinations: StorageMigrationDestinationsSchema,
8962
+ sources: StorageMigrationSourcesSchema,
8919
8963
  throttleMbps: number(),
8920
8964
  moves: array(StorageMigrationMoveSchema),
8921
8965
  pauseLeaseId: string().nullable(),
@@ -8941,6 +8985,7 @@ var StorageMigrationFindingSchema = object({
8941
8985
  });
8942
8986
  var StorageMigrationPlanSchema = object({
8943
8987
  destinations: StorageMigrationDestinationsSchema,
8988
+ sources: StorageMigrationSourcesSchema,
8944
8989
  /** The mode this plan was built for. A plan is only valid for its mode: the
8945
8990
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8946
8991
  * it. */
@@ -8948,7 +8993,8 @@ var StorageMigrationPlanSchema = object({
8948
8993
  moves: array(object({
8949
8994
  storageClass: StorageMigrationClassSchema,
8950
8995
  fromLocationId: string(),
8951
- toLocationId: string()
8996
+ toLocationId: string(),
8997
+ freezeSource: boolean().optional()
8952
8998
  })),
8953
8999
  findings: array(StorageMigrationFindingSchema)
8954
9000
  });
@@ -9035,16 +9081,142 @@ var RelocateResidueSchema = object({
9035
9081
  segments: number().int().nonnegative(),
9036
9082
  bytes: number().int().nonnegative()
9037
9083
  }).nullable();
9084
+ /**
9085
+ * Ask one location whether its durable hour rows describe the disk — the walk
9086
+ * (D319).
9087
+ *
9088
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
9089
+ * missing tool is the question, and the dry run is how they sanity-check the
9090
+ * destructive run before authorising it.
9091
+ */
9092
+ var LedgerWalkInputSchema = object({
9093
+ locationId: string().min(1),
9094
+ /** Forget the confirmed-absent rows, rather than only counting them. */
9095
+ apply: boolean().optional(),
9096
+ /** Narrow to one camera. */
9097
+ deviceId: number().int().positive().optional(),
9098
+ /** Narrow to these recording profiles; empty/absent = every profile. */
9099
+ profiles: array(string().min(1)).optional()
9100
+ });
9101
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
9102
+ var LedgerWalkRefusalSchema = _enum([
9103
+ "location-unknown",
9104
+ "source-writable",
9105
+ "no-ledger",
9106
+ "archive-unreadable",
9107
+ "anchor-absent",
9108
+ "anchor-unreadable",
9109
+ "anchor-moved"
9110
+ ]);
9111
+ _enum([
9112
+ "live-tail",
9113
+ "listing-error",
9114
+ "path-mismatch",
9115
+ "durable-refused"
9116
+ ]);
9117
+ /** Every skip reason, always present, always a number — so a reason that never
9118
+ * fired reports as zero rather than absent and the report shape is constant
9119
+ * between passes. Spelled out rather than `z.record` for exactly that. */
9120
+ var LedgerWalkSkipCountsSchema = object({
9121
+ "live-tail": number().int().nonnegative(),
9122
+ "listing-error": number().int().nonnegative(),
9123
+ "path-mismatch": number().int().nonnegative(),
9124
+ "durable-refused": number().int().nonnegative()
9125
+ });
9126
+ /** One camera's share of a walk, so a report names cameras and not rows. */
9127
+ var LedgerWalkDeviceReportSchema = object({
9128
+ deviceId: number().int(),
9129
+ hoursWalked: number().int().nonnegative(),
9130
+ hoursMissing: number().int().nonnegative(),
9131
+ ghostSegments: number().int().nonnegative(),
9132
+ ghostBytes: number().int().nonnegative(),
9133
+ forgottenSegments: number().int().nonnegative(),
9134
+ orphanFiles: number().int().nonnegative()
9135
+ });
9136
+ /**
9137
+ * What one walk claimed, listed, found and (only when armed) forgot.
9138
+ *
9139
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
9140
+ * walk that saw a fraction of the location is visible in its own report rather
9141
+ * than in the absence of one.
9142
+ */
9143
+ var LedgerWalkReportSchema = object({
9144
+ locationId: string(),
9145
+ applied: boolean(),
9146
+ refused: LedgerWalkRefusalSchema.nullable(),
9147
+ archiveSegments: number().int().nonnegative().nullable(),
9148
+ archiveBytes: number().int().nonnegative().nullable(),
9149
+ hoursClaimed: number().int().nonnegative(),
9150
+ hoursWalked: number().int().nonnegative(),
9151
+ hoursMissing: number().int().nonnegative(),
9152
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
9153
+ listings: number().int().nonnegative(),
9154
+ segmentsClaimed: number().int().nonnegative(),
9155
+ ghostSegments: number().int().nonnegative(),
9156
+ ghostBytes: number().int().nonnegative(),
9157
+ ghostHoursWhole: number().int().nonnegative(),
9158
+ forgottenSegments: number().int().nonnegative(),
9159
+ forgottenBytes: number().int().nonnegative(),
9160
+ /** Files under a claimed hour that no durable row names. Never deleted. */
9161
+ orphanFiles: number().int().nonnegative(),
9162
+ orphanSample: array(string()).readonly(),
9163
+ hoursSkipped: number().int().nonnegative(),
9164
+ skippedByReason: LedgerWalkSkipCountsSchema,
9165
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
9166
+ bounded: boolean(),
9167
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
9168
+ });
9038
9169
  /** How many rows a media pass would still act on against a given target — the
9039
9170
  * media lane's denominator AND its residue, from ONE derivation so the two can
9040
9171
  * never disagree. `null` = the count could not be taken. */
9041
9172
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9042
9173
  var RelocatableMediaCountInputSchema = object({
9043
9174
  toLocationId: string().min(1),
9175
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
9176
+ fromLocationId: string().optional(),
9044
9177
  /** Omitted = `move`. */
9045
9178
  mode: MediaRelocateModeSchema.optional()
9046
9179
  });
9047
9180
  /**
9181
+ * Operator cleanup of leftover analytics rows, optional debug media, and
9182
+ * ghost ledger entries on frozen footage locations.
9183
+ *
9184
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
9185
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
9186
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
9187
+ * with no operator-visible status.
9188
+ */
9189
+ var StorageCleanupPhaseSchema = _enum([
9190
+ "orphans",
9191
+ "debug-media",
9192
+ "ghost-ledger",
9193
+ "done",
9194
+ "failed",
9195
+ "cancelled"
9196
+ ]);
9197
+ var StorageCleanupInputSchema = object({
9198
+ /** Also walk motion stills / track filmstrips. Off by default. */
9199
+ includeDebugMedia: boolean().optional() });
9200
+ var StorageCleanupJobSchema = object({
9201
+ jobId: string(),
9202
+ phase: StorageCleanupPhaseSchema,
9203
+ includeDebugMedia: boolean(),
9204
+ orphansReclaimed: number().int().nonnegative(),
9205
+ orphanBytesReclaimed: number().int().nonnegative(),
9206
+ debugMediaReclaimed: number().int().nonnegative(),
9207
+ debugMediaBytesReclaimed: number().int().nonnegative(),
9208
+ ghostsForgotten: number().int().nonnegative(),
9209
+ ghostBytesForgotten: number().int().nonnegative(),
9210
+ /** Short operator-facing line: current collection, pass, or location. */
9211
+ detail: string().nullable(),
9212
+ cancelRequested: boolean(),
9213
+ startedAt: number(),
9214
+ updatedAt: number(),
9215
+ finishedAt: number().nullable(),
9216
+ error: string().nullable()
9217
+ });
9218
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
9219
+ /**
9048
9220
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9049
9221
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9050
9222
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9072,11 +9244,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
9072
9244
  * The default location for a type uses `id === <type>:default` by
9073
9245
  * convention (the bare type ref like `'backups'` resolves to it).
9074
9246
  *
9075
- * `isSystem: true` marks a location as orchestrator-seeded and
9076
- * undeletable. The bootstrap-installed defaults (one per type) carry
9077
- * this flag; operator-added locations don't. Editing the config of
9078
- * a system location is allowed (path migration, provider swap) but
9079
- * deleting it is rejected at the cap level.
9247
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
9248
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
9249
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
9250
+ * / last-enabled, not on this bit.
9080
9251
  */
9081
9252
  var StorageLocationSchema = object({
9082
9253
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13473,6 +13644,12 @@ method(object({
13473
13644
  }), _void(), {
13474
13645
  kind: "mutation",
13475
13646
  auth: "admin"
13647
+ }), method(object({
13648
+ from: string(),
13649
+ to: string()
13650
+ }), object({ moved: number() }), {
13651
+ kind: "mutation",
13652
+ auth: "admin"
13476
13653
  }), method(object({
13477
13654
  deviceId: number(),
13478
13655
  disabled: boolean()
@@ -19403,53 +19580,15 @@ var RecentTracksPageSchema = object({
19403
19580
  /** Cursor for the next page, or null when this page is the last. */
19404
19581
  nextCursor: string().nullable()
19405
19582
  });
19406
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19407
- var LIST_GROUPS_MAX_LIMIT = 100;
19408
- var AnalyticsGroupRecordSchema = object({
19409
- id: string(),
19410
- deviceId: number().int(),
19411
- openedAt: number().int(),
19412
- closedAt: number().int(),
19413
- timestamp: number().int(),
19414
- memberCount: number().int(),
19415
- memberTrackIds: array(string()).readonly(),
19416
- className: string(),
19417
- classes: array(string()).readonly(),
19418
- /** Relative event-media path, or null when the group has no picture yet. */
19419
- mediaUrl: string().nullable(),
19420
- singleton: boolean()
19421
- });
19422
- var AnalyticsGroupMemberSchema = object({
19423
- trackId: string(),
19424
- deviceId: number().int(),
19425
- className: string(),
19426
- firstSeen: number().int(),
19427
- lastSeen: number().int(),
19428
- mediaUrl: string().nullable()
19429
- });
19430
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19431
- var ListGroupsQueryInput = object({
19432
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19433
- deviceIds: array(number()),
19434
- /** Window lower bound on `closedAt` (inclusive). */
19435
- since: number().optional(),
19436
- /** Window upper bound on `openedAt` (inclusive). */
19437
- until: number().optional(),
19438
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19439
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19440
- cursor: string().optional()
19441
- });
19442
- var ListGroupsPageSchema = object({
19443
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19444
- nextCursor: string().nullable()
19445
- });
19583
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19584
+ var KEY_EVENTS_MAX_LIMIT = 200;
19446
19585
  var KeyEventQueryInput = object({
19447
19586
  deviceId: number(),
19448
19587
  /** Window lower bound (track firstSeen ≥ since). */
19449
19588
  since: number(),
19450
19589
  /** Window upper bound (track firstSeen ≤ until). */
19451
19590
  until: number(),
19452
- limit: number().int().min(1).max(200).default(50),
19591
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19453
19592
  /** Drop tracks scoring below this importance. */
19454
19593
  minImportance: number().min(0).max(1).optional(),
19455
19594
  /** Restrict to a single class (e.g. 'person'). */
@@ -19471,6 +19610,32 @@ var KeyEventSchema = object({
19471
19610
  ...TrackFlagFields,
19472
19611
  ...TrackRetrainFields
19473
19612
  });
19613
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19614
+ var KeyEventBatchQueryInput = object({
19615
+ deviceIds: array(number()).min(1).max(200),
19616
+ since: number(),
19617
+ until: number(),
19618
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19619
+ * across the set, which would let a busy camera starve a quiet one of its
19620
+ * rows and change what the merged feed contains. */
19621
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19622
+ minImportance: number().min(0).max(1).optional(),
19623
+ classFilter: string().optional()
19624
+ });
19625
+ /**
19626
+ * One camera's key events in a batch answer.
19627
+ *
19628
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19629
+ * error rather than throwing, so a camera whose store read failed and one with
19630
+ * no events in the window were ALREADY indistinguishable per camera — the
19631
+ * batch does not make that worse, and the row keeps the deviceId the single
19632
+ * method's output never carried (the caller used to stamp it from the fan-out
19633
+ * key, which only worked because there was one query per camera).
19634
+ */
19635
+ var KeyEventsForDeviceSchema = object({
19636
+ deviceId: number(),
19637
+ events: array(KeyEventSchema).readonly()
19638
+ });
19474
19639
  object({
19475
19640
  trackId: string(),
19476
19641
  className: string(),
@@ -19518,9 +19683,7 @@ var TrackCascadeCountsSchema = object({
19518
19683
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19519
19684
  plates: number().int(),
19520
19685
  /** Per-track CLIP search vectors removed (best-effort). */
19521
- embeddings: number().int(),
19522
- /** Group membership + group rows removed with their last member (best-effort). */
19523
- groups: number().int()
19686
+ embeddings: number().int()
19524
19687
  });
19525
19688
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19526
19689
  var DiskReconcileCountsSchema = object({
@@ -19690,6 +19853,47 @@ var RebuildStatusSchema = object({
19690
19853
  /** Present when the pass ended by throwing. */
19691
19854
  error: string().nullable()
19692
19855
  });
19856
+ /**
19857
+ * Acknowledgement that a debug-media reclaim STARTED.
19858
+ *
19859
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19860
+ * it runs detached and this returns immediately. Awaiting it is how the
19861
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19862
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19863
+ */
19864
+ var MediaReclaimStartResultSchema = object({
19865
+ started: boolean(),
19866
+ /** True when a pass was already running; the new request is ignored. */
19867
+ alreadyRunning: boolean()
19868
+ });
19869
+ var MediaReclaimInputSchema = object({
19870
+ mode: _enum(["report", "reclaim"]).default("report"),
19871
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19872
+ deviceIds: array(number().int()).min(1).optional(),
19873
+ restart: boolean().optional(),
19874
+ pageSize: number().int().min(50).max(5e3).optional(),
19875
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19876
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19877
+ maxBytesPerRun: number().int().min(1).optional(),
19878
+ budgetMinutes: number().int().min(1).max(720).optional(),
19879
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19880
+ graceMinutes: number().int().min(1).max(10080).optional()
19881
+ });
19882
+ var MediaReclaimStatusSchema = object({
19883
+ running: boolean(),
19884
+ mode: _enum(["report", "reclaim"]).nullable(),
19885
+ totalExamined: number(),
19886
+ totalEligible: number(),
19887
+ totalReclaimed: number(),
19888
+ totalBytesReclaimed: number(),
19889
+ totalRefused: number(),
19890
+ /** Device+scope windows finished in this pass. */
19891
+ devicesDone: number(),
19892
+ complete: boolean().nullable(),
19893
+ startedAtMs: number().nullable(),
19894
+ finishedAtMs: number().nullable(),
19895
+ error: string().nullable()
19896
+ });
19693
19897
  var ReplayFrameInputSchema = object({
19694
19898
  timestamp: number(),
19695
19899
  frame: PipelineRunResultBridge
@@ -19728,10 +19932,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19728
19932
  * stationary registry). Default false: the timeline lists passages,
19729
19933
  * not parking records (operator decision, 2026-08-15). */
19730
19934
  includeStationary: boolean().optional()
19731
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19732
- deviceId: number(),
19733
- groupId: string().min(1)
19734
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19935
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19735
19936
  kind: "mutation",
19736
19937
  auth: "admin"
19737
19938
  }), 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({
@@ -19740,7 +19941,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19740
19941
  until: number().optional(),
19741
19942
  kinds: array(string()).optional(),
19742
19943
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19743
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19944
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19744
19945
  deviceId: number(),
19745
19946
  since: number(),
19746
19947
  until: number(),
@@ -19831,6 +20032,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19831
20032
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19832
20033
  kind: "query",
19833
20034
  auth: "admin"
20035
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
20036
+ kind: "mutation",
20037
+ auth: "admin"
20038
+ }), method(object({}), MediaReclaimStatusSchema, {
20039
+ kind: "query",
20040
+ auth: "admin"
19834
20041
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19835
20042
  kind: "query",
19836
20043
  auth: "admin"
@@ -21801,7 +22008,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21801
22008
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21802
22009
  kind: "mutation",
21803
22010
  auth: "admin"
21804
- });
22011
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
22012
+ kind: "mutation",
22013
+ auth: "admin"
22014
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22015
+ kind: "mutation",
22016
+ auth: "admin"
22017
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21805
22018
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21806
22019
  providerId: string().min(1),
21807
22020
  displayName: string().min(1),
@@ -27227,6 +27440,33 @@ var RecordingRebalanceInputSchema = object({
27227
27440
  minMoveGb: number().min(0).optional()
27228
27441
  });
27229
27442
  /**
27443
+ * Operator-facing placement of one camera onto a recordings location.
27444
+ *
27445
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
27446
+ * Auto (the planner may move this camera). `locationId` is where high/mid
27447
+ * currently write — the plan, which may disagree with the pin when Auto.
27448
+ */
27449
+ var RecordingDevicePlacementSchema = object({
27450
+ deviceId: number().int(),
27451
+ profile: string(),
27452
+ locationId: string()
27453
+ });
27454
+ var RecordingDevicePinSchema = object({
27455
+ deviceId: number().int(),
27456
+ /** Recordings-class location this camera is pinned to. */
27457
+ locationId: string()
27458
+ });
27459
+ var RecordingPlacementViewSchema = object({
27460
+ assignments: array(RecordingDevicePlacementSchema),
27461
+ pins: array(RecordingDevicePinSchema),
27462
+ defaultLocations: record(string(), string())
27463
+ });
27464
+ var RecordingSetDevicePlacementInputSchema = object({
27465
+ deviceId: number().int(),
27466
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
27467
+ locationId: string().nullable()
27468
+ });
27469
+ /**
27230
27470
  * Result of locating footage at a wall-clock instant for one device/profile.
27231
27471
  * `segment` carries the covering segment's window; `gap` reports the forward
27232
27472
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -27440,6 +27680,9 @@ method(object({
27440
27680
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
27441
27681
  kind: "query",
27442
27682
  auth: "admin"
27683
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27684
+ kind: "mutation",
27685
+ auth: "admin"
27443
27686
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
27444
27687
  kind: "mutation",
27445
27688
  auth: "admin"
@@ -27449,6 +27692,12 @@ method(object({
27449
27692
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
27450
27693
  kind: "mutation",
27451
27694
  auth: "admin"
27695
+ }), method(object({}), RecordingPlacementViewSchema, {
27696
+ kind: "query",
27697
+ auth: "admin"
27698
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27699
+ kind: "mutation",
27700
+ auth: "admin"
27452
27701
  });
27453
27702
  /**
27454
27703
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27831,7 +28080,26 @@ var SceneMonitorStatusSchema = object({
27831
28080
  monitors: array(SceneMonitorSchema),
27832
28081
  lastFetchedAt: number()
27833
28082
  });
27834
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
28083
+ /**
28084
+ * One camera's row in a `listScenesBatch` answer.
28085
+ *
28086
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
28087
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
28088
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
28089
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
28090
+ * configured. Fanned out per camera the difference was visible — one query
28091
+ * errored while the others resolved — and a batch that returned only the rows
28092
+ * it managed would have destroyed it, silently, by making an unreachable camera
28093
+ * indistinguishable from one that answered `monitors: []`.
28094
+ *
28095
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
28096
+ * could not be read"; `status.monitors: []` means "read, and it has none".
28097
+ */
28098
+ var SceneMonitorStatusForDeviceSchema = object({
28099
+ deviceId: number(),
28100
+ status: SceneMonitorStatusSchema.nullable()
28101
+ });
28102
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27835
28103
  deviceId: number(),
27836
28104
  label: string(),
27837
28105
  roi: MaskRectShapeSchema,
@@ -29180,6 +29448,27 @@ var CameraOccupancySnapshotSchema = object({
29180
29448
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
29181
29449
  });
29182
29450
  /**
29451
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
29452
+ *
29453
+ * THREE outcomes, and the single-camera method could only express two of them
29454
+ * because `snapshot: null` was already spoken for:
29455
+ *
29456
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
29457
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
29458
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
29459
+ * - `read: 'unreadable'` — the owner could not answer for this
29460
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
29461
+ *
29462
+ * Collapsing the last two is the failure this field exists to prevent: a
29463
+ * hydration that threw would otherwise render as an empty Stationary section,
29464
+ * which is a definite claim about a camera nobody could read.
29465
+ */
29466
+ var CameraOccupancySnapshotForDeviceSchema = object({
29467
+ deviceId: number(),
29468
+ read: _enum(["read", "unreadable"]),
29469
+ snapshot: CameraOccupancySnapshotSchema.nullable()
29470
+ });
29471
+ /**
29183
29472
  * Time-series resolution. The history methods return one bucket per
29184
29473
  * step over the requested range. Smaller resolutions cost more
29185
29474
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -29203,7 +29492,7 @@ var HistoryPointSchema = object({
29203
29492
  /** Object count averaged over the bucket (rounded to nearest integer). */
29204
29493
  count: number().int().nonnegative()
29205
29494
  });
29206
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
29495
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
29207
29496
  deviceId: number(),
29208
29497
  zoneId: string(),
29209
29498
  className: string().optional()
@@ -30804,6 +31093,12 @@ Object.freeze({
30804
31093
  addonId: null,
30805
31094
  access: "delete"
30806
31095
  },
31096
+ "deviceManager.renameLocation": {
31097
+ capName: "device-manager",
31098
+ capScope: "system",
31099
+ addonId: null,
31100
+ access: "create"
31101
+ },
30807
31102
  "deviceManager.runDeviceAction": {
30808
31103
  capName: "device-manager",
30809
31104
  capScope: "system",
@@ -32496,13 +32791,19 @@ Object.freeze({
32496
32791
  addonId: null,
32497
32792
  access: "view"
32498
32793
  },
32499
- "pipelineAnalytics.getGroup": {
32794
+ "pipelineAnalytics.getKeyEvents": {
32500
32795
  capName: "pipeline-analytics",
32501
32796
  capScope: "device",
32502
32797
  addonId: null,
32503
32798
  access: "view"
32504
32799
  },
32505
- "pipelineAnalytics.getKeyEvents": {
32800
+ "pipelineAnalytics.getKeyEventsBatch": {
32801
+ capName: "pipeline-analytics",
32802
+ capScope: "device",
32803
+ addonId: null,
32804
+ access: "view"
32805
+ },
32806
+ "pipelineAnalytics.getMediaReclaimStatus": {
32506
32807
  capName: "pipeline-analytics",
32507
32808
  capScope: "device",
32508
32809
  addonId: null,
@@ -32592,12 +32893,6 @@ Object.freeze({
32592
32893
  addonId: null,
32593
32894
  access: "view"
32594
32895
  },
32595
- "pipelineAnalytics.listGroups": {
32596
- capName: "pipeline-analytics",
32597
- capScope: "device",
32598
- addonId: null,
32599
- access: "view"
32600
- },
32601
32896
  "pipelineAnalytics.listOpsLog": {
32602
32897
  capName: "pipeline-analytics",
32603
32898
  capScope: "device",
@@ -32682,6 +32977,12 @@ Object.freeze({
32682
32977
  addonId: null,
32683
32978
  access: "create"
32684
32979
  },
32980
+ "pipelineAnalytics.reclaimDebugMedia": {
32981
+ capName: "pipeline-analytics",
32982
+ capScope: "device",
32983
+ addonId: null,
32984
+ access: "create"
32985
+ },
32685
32986
  "pipelineAnalytics.reconcileFromDisk": {
32686
32987
  capName: "pipeline-analytics",
32687
32988
  capScope: "device",
@@ -33606,6 +33907,12 @@ Object.freeze({
33606
33907
  addonId: null,
33607
33908
  access: "view"
33608
33909
  },
33910
+ "recording.getPlacement": {
33911
+ capName: "recording",
33912
+ capScope: "system",
33913
+ addonId: null,
33914
+ access: "view"
33915
+ },
33609
33916
  "recording.getPlaybackManifest": {
33610
33917
  capName: "recording",
33611
33918
  capScope: "system",
@@ -33684,6 +33991,12 @@ Object.freeze({
33684
33991
  addonId: null,
33685
33992
  access: "view"
33686
33993
  },
33994
+ "recording.reconcileLedgerAgainstDisk": {
33995
+ capName: "recording",
33996
+ capScope: "system",
33997
+ addonId: null,
33998
+ access: "create"
33999
+ },
33687
34000
  "recording.refreshStorageLocationsForMigration": {
33688
34001
  capName: "recording",
33689
34002
  capScope: "system",
@@ -33726,6 +34039,12 @@ Object.freeze({
33726
34039
  addonId: null,
33727
34040
  access: "create"
33728
34041
  },
34042
+ "recording.setDevicePlacement": {
34043
+ capName: "recording",
34044
+ capScope: "system",
34045
+ addonId: null,
34046
+ access: "create"
34047
+ },
33729
34048
  "recording.startStorageMigrationMove": {
33730
34049
  capName: "recording",
33731
34050
  capScope: "system",
@@ -33810,6 +34129,12 @@ Object.freeze({
33810
34129
  addonId: null,
33811
34130
  access: "view"
33812
34131
  },
34132
+ "sceneMonitor.listScenesBatch": {
34133
+ capName: "scene-monitor",
34134
+ capScope: "device",
34135
+ addonId: null,
34136
+ access: "view"
34137
+ },
33813
34138
  "sceneMonitor.recheckNow": {
33814
34139
  capName: "scene-monitor",
33815
34140
  capScope: "device",
@@ -34164,12 +34489,36 @@ Object.freeze({
34164
34489
  addonId: null,
34165
34490
  access: "create"
34166
34491
  },
34492
+ "storageMigration.cleanupCancel": {
34493
+ capName: "storage-migration",
34494
+ capScope: "system",
34495
+ addonId: null,
34496
+ access: "create"
34497
+ },
34498
+ "storageMigration.cleanupStart": {
34499
+ capName: "storage-migration",
34500
+ capScope: "system",
34501
+ addonId: null,
34502
+ access: "create"
34503
+ },
34504
+ "storageMigration.cleanupStatus": {
34505
+ capName: "storage-migration",
34506
+ capScope: "system",
34507
+ addonId: null,
34508
+ access: "view"
34509
+ },
34167
34510
  "storageMigration.drain": {
34168
34511
  capName: "storage-migration",
34169
34512
  capScope: "system",
34170
34513
  addonId: null,
34171
34514
  access: "create"
34172
34515
  },
34516
+ "storageMigration.history": {
34517
+ capName: "storage-migration",
34518
+ capScope: "system",
34519
+ addonId: null,
34520
+ access: "view"
34521
+ },
34173
34522
  "storageMigration.movers": {
34174
34523
  capName: "storage-migration",
34175
34524
  capScope: "system",
@@ -35166,6 +35515,12 @@ Object.freeze({
35166
35515
  addonId: null,
35167
35516
  access: "view"
35168
35517
  },
35518
+ "zoneAnalytics.getCurrentSnapshotBatch": {
35519
+ capName: "zone-analytics",
35520
+ capScope: "device",
35521
+ addonId: null,
35522
+ access: "view"
35523
+ },
35169
35524
  "zoneAnalytics.getUnzonedHistory": {
35170
35525
  capName: "zone-analytics",
35171
35526
  capScope: "device",
@@ -36160,14 +36515,14 @@ Object.freeze({
36160
36515
  form: "single",
36161
36516
  optional: true
36162
36517
  }],
36163
- "pipelineAnalytics.getGroup": [{
36518
+ "pipelineAnalytics.getKeyEvents": [{
36164
36519
  name: "deviceId",
36165
36520
  form: "single",
36166
36521
  optional: false
36167
36522
  }],
36168
- "pipelineAnalytics.getKeyEvents": [{
36169
- name: "deviceId",
36170
- form: "single",
36523
+ "pipelineAnalytics.getKeyEventsBatch": [{
36524
+ name: "deviceIds",
36525
+ form: "array",
36171
36526
  optional: false
36172
36527
  }],
36173
36528
  "pipelineAnalytics.getMotionEvents": [{
@@ -36225,11 +36580,6 @@ Object.freeze({
36225
36580
  form: "single",
36226
36581
  optional: false
36227
36582
  }],
36228
- "pipelineAnalytics.listGroups": [{
36229
- name: "deviceIds",
36230
- form: "array",
36231
- optional: false
36232
- }],
36233
36583
  "pipelineAnalytics.listOpsLog": [{
36234
36584
  name: "deviceId",
36235
36585
  form: "single",
@@ -36275,6 +36625,11 @@ Object.freeze({
36275
36625
  form: "single",
36276
36626
  optional: true
36277
36627
  }],
36628
+ "pipelineAnalytics.reclaimDebugMedia": [{
36629
+ name: "deviceIds",
36630
+ form: "array",
36631
+ optional: true
36632
+ }],
36278
36633
  "pipelineAnalytics.reconcileFromDisk": [{
36279
36634
  name: "deviceId",
36280
36635
  form: "single",
@@ -36610,6 +36965,11 @@ Object.freeze({
36610
36965
  form: "single",
36611
36966
  optional: false
36612
36967
  }],
36968
+ "recording.reconcileLedgerAgainstDisk": [{
36969
+ name: "deviceId",
36970
+ form: "single",
36971
+ optional: true
36972
+ }],
36613
36973
  "recording.relocateFootage": [{
36614
36974
  name: "deviceId",
36615
36975
  form: "single",
@@ -36635,6 +36995,11 @@ Object.freeze({
36635
36995
  form: "single",
36636
36996
  optional: false
36637
36997
  }],
36998
+ "recording.setDevicePlacement": [{
36999
+ name: "deviceId",
37000
+ form: "single",
37001
+ optional: false
37002
+ }],
36638
37003
  "recording.startStorageMigrationMove": [{
36639
37004
  name: "deviceId",
36640
37005
  form: "single",
@@ -36675,6 +37040,11 @@ Object.freeze({
36675
37040
  form: "single",
36676
37041
  optional: false
36677
37042
  }],
37043
+ "sceneMonitor.listScenesBatch": [{
37044
+ name: "deviceIds",
37045
+ form: "array",
37046
+ optional: false
37047
+ }],
36678
37048
  "sceneMonitor.recheckNow": [{
36679
37049
  name: "deviceId",
36680
37050
  form: "single",
@@ -36936,6 +37306,11 @@ Object.freeze({
36936
37306
  form: "single",
36937
37307
  optional: false
36938
37308
  }],
37309
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
37310
+ name: "deviceIds",
37311
+ form: "array",
37312
+ optional: false
37313
+ }],
36939
37314
  "zoneAnalytics.getUnzonedHistory": [{
36940
37315
  name: "deviceId",
36941
37316
  form: "single",