@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.
@@ -8684,6 +8684,20 @@ var RelocateJobSchema = object({
8684
8684
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8685
8685
  */
8686
8686
  rowsReconciled: number().int().nonnegative().optional(),
8687
+ /**
8688
+ * Rows this run FORGOT because the file they name is not on disk.
8689
+ *
8690
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8691
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8692
+ * and the durable row is dropped through the same channel eviction uses. It
8693
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8694
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8695
+ * the same failure as one that quietly skips them (D295).
8696
+ *
8697
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8698
+ * ledger claimed 5.65 GB of footage that no longer existed.
8699
+ */
8700
+ rowsForgotten: number().int().nonnegative().optional(),
8687
8701
  startedAt: number(),
8688
8702
  finishedAt: number().nullable(),
8689
8703
  error: string().nullable()
@@ -8748,6 +8762,13 @@ var MediaRelocateModeSchema = _enum([
8748
8762
  ]);
8749
8763
  var RelocateMediaInputSchema = object({
8750
8764
  toLocationId: string(),
8765
+ /**
8766
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8767
+ * every row that is not already on `toLocationId` (the historical
8768
+ * behaviour). A named source is what a from→to migration needs: without it
8769
+ * "move events off disk 2" also emptied disk 1.
8770
+ */
8771
+ fromLocationId: string().optional(),
8751
8772
  throttleMbps: number().min(1).max(1e3).optional(),
8752
8773
  /** Omitted = `move`, the pre-existing behaviour. */
8753
8774
  mode: MediaRelocateModeSchema.optional()
@@ -8815,6 +8836,19 @@ var StorageMigrationDestinationsSchema = object({
8815
8836
  galleryMedia: string().min(1).optional()
8816
8837
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8817
8838
  /**
8839
+ * Optional named source per class. Omitted = the class's current default
8840
+ * (the historical behaviour). A named source that is NOT the default is a
8841
+ * drain of that disk: bytes move, the default stays, and the source is
8842
+ * disabled when the move finishes.
8843
+ */
8844
+ var StorageMigrationSourcesSchema = object({
8845
+ recordings: string().min(1).optional(),
8846
+ recordingsLow: string().min(1).optional(),
8847
+ eventMedia: string().min(1).optional(),
8848
+ backups: string().min(1).optional(),
8849
+ galleryMedia: string().min(1).optional()
8850
+ }).optional();
8851
+ /**
8818
8852
  * How a migration sequences the cutover against the byte move.
8819
8853
  *
8820
8854
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8836,6 +8870,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8836
8870
  /** Shared input for planning and starting an orchestrated storage migration. */
8837
8871
  var StorageMigrationInputSchema = object({
8838
8872
  destinations: StorageMigrationDestinationsSchema,
8873
+ /** Omitted = each class's current default. */
8874
+ sources: StorageMigrationSourcesSchema,
8839
8875
  throttleMbps: number().min(1).max(1e3).optional(),
8840
8876
  /** Omitted = `blocking`, which stays the default. */
8841
8877
  mode: StorageMigrationModeSchema.optional()
@@ -8915,6 +8951,13 @@ var StorageMigrationMoveSchema = object({
8915
8951
  storageClass: StorageMigrationClassSchema,
8916
8952
  fromLocationId: string(),
8917
8953
  toLocationId: string(),
8954
+ /**
8955
+ * True when `from` was NOT the class default at plan time. The move still
8956
+ * copies bytes, but the default is left alone and the source is disabled
8957
+ * once the copy verifies. Absent on jobs planned before this field existed
8958
+ * — those jobs always repointed, which is `false`.
8959
+ */
8960
+ freezeSource: boolean().optional(),
8918
8961
  moverJobId: string().nullable(),
8919
8962
  state: RelocateJobStateSchema.nullable(),
8920
8963
  error: string().nullable(),
@@ -8928,6 +8971,7 @@ var StorageMigrationJobSchema = object({
8928
8971
  * can tell a seconds-long cutover from a thirty-hour one. */
8929
8972
  mode: StorageMigrationModeSchema,
8930
8973
  destinations: StorageMigrationDestinationsSchema,
8974
+ sources: StorageMigrationSourcesSchema,
8931
8975
  throttleMbps: number(),
8932
8976
  moves: array(StorageMigrationMoveSchema),
8933
8977
  pauseLeaseId: string().nullable(),
@@ -8953,6 +8997,7 @@ var StorageMigrationFindingSchema = object({
8953
8997
  });
8954
8998
  var StorageMigrationPlanSchema = object({
8955
8999
  destinations: StorageMigrationDestinationsSchema,
9000
+ sources: StorageMigrationSourcesSchema,
8956
9001
  /** The mode this plan was built for. A plan is only valid for its mode: the
8957
9002
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8958
9003
  * it. */
@@ -8960,7 +9005,8 @@ var StorageMigrationPlanSchema = object({
8960
9005
  moves: array(object({
8961
9006
  storageClass: StorageMigrationClassSchema,
8962
9007
  fromLocationId: string(),
8963
- toLocationId: string()
9008
+ toLocationId: string(),
9009
+ freezeSource: boolean().optional()
8964
9010
  })),
8965
9011
  findings: array(StorageMigrationFindingSchema)
8966
9012
  });
@@ -9047,16 +9093,142 @@ var RelocateResidueSchema = object({
9047
9093
  segments: number().int().nonnegative(),
9048
9094
  bytes: number().int().nonnegative()
9049
9095
  }).nullable();
9096
+ /**
9097
+ * Ask one location whether its durable hour rows describe the disk — the walk
9098
+ * (D319).
9099
+ *
9100
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
9101
+ * missing tool is the question, and the dry run is how they sanity-check the
9102
+ * destructive run before authorising it.
9103
+ */
9104
+ var LedgerWalkInputSchema = object({
9105
+ locationId: string().min(1),
9106
+ /** Forget the confirmed-absent rows, rather than only counting them. */
9107
+ apply: boolean().optional(),
9108
+ /** Narrow to one camera. */
9109
+ deviceId: number().int().positive().optional(),
9110
+ /** Narrow to these recording profiles; empty/absent = every profile. */
9111
+ profiles: array(string().min(1)).optional()
9112
+ });
9113
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
9114
+ var LedgerWalkRefusalSchema = _enum([
9115
+ "location-unknown",
9116
+ "source-writable",
9117
+ "no-ledger",
9118
+ "archive-unreadable",
9119
+ "anchor-absent",
9120
+ "anchor-unreadable",
9121
+ "anchor-moved"
9122
+ ]);
9123
+ _enum([
9124
+ "live-tail",
9125
+ "listing-error",
9126
+ "path-mismatch",
9127
+ "durable-refused"
9128
+ ]);
9129
+ /** Every skip reason, always present, always a number — so a reason that never
9130
+ * fired reports as zero rather than absent and the report shape is constant
9131
+ * between passes. Spelled out rather than `z.record` for exactly that. */
9132
+ var LedgerWalkSkipCountsSchema = object({
9133
+ "live-tail": number().int().nonnegative(),
9134
+ "listing-error": number().int().nonnegative(),
9135
+ "path-mismatch": number().int().nonnegative(),
9136
+ "durable-refused": number().int().nonnegative()
9137
+ });
9138
+ /** One camera's share of a walk, so a report names cameras and not rows. */
9139
+ var LedgerWalkDeviceReportSchema = object({
9140
+ deviceId: number().int(),
9141
+ hoursWalked: number().int().nonnegative(),
9142
+ hoursMissing: number().int().nonnegative(),
9143
+ ghostSegments: number().int().nonnegative(),
9144
+ ghostBytes: number().int().nonnegative(),
9145
+ forgottenSegments: number().int().nonnegative(),
9146
+ orphanFiles: number().int().nonnegative()
9147
+ });
9148
+ /**
9149
+ * What one walk claimed, listed, found and (only when armed) forgot.
9150
+ *
9151
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
9152
+ * walk that saw a fraction of the location is visible in its own report rather
9153
+ * than in the absence of one.
9154
+ */
9155
+ var LedgerWalkReportSchema = object({
9156
+ locationId: string(),
9157
+ applied: boolean(),
9158
+ refused: LedgerWalkRefusalSchema.nullable(),
9159
+ archiveSegments: number().int().nonnegative().nullable(),
9160
+ archiveBytes: number().int().nonnegative().nullable(),
9161
+ hoursClaimed: number().int().nonnegative(),
9162
+ hoursWalked: number().int().nonnegative(),
9163
+ hoursMissing: number().int().nonnegative(),
9164
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
9165
+ listings: number().int().nonnegative(),
9166
+ segmentsClaimed: number().int().nonnegative(),
9167
+ ghostSegments: number().int().nonnegative(),
9168
+ ghostBytes: number().int().nonnegative(),
9169
+ ghostHoursWhole: number().int().nonnegative(),
9170
+ forgottenSegments: number().int().nonnegative(),
9171
+ forgottenBytes: number().int().nonnegative(),
9172
+ /** Files under a claimed hour that no durable row names. Never deleted. */
9173
+ orphanFiles: number().int().nonnegative(),
9174
+ orphanSample: array(string()).readonly(),
9175
+ hoursSkipped: number().int().nonnegative(),
9176
+ skippedByReason: LedgerWalkSkipCountsSchema,
9177
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
9178
+ bounded: boolean(),
9179
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
9180
+ });
9050
9181
  /** How many rows a media pass would still act on against a given target — the
9051
9182
  * media lane's denominator AND its residue, from ONE derivation so the two can
9052
9183
  * never disagree. `null` = the count could not be taken. */
9053
9184
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9054
9185
  var RelocatableMediaCountInputSchema = object({
9055
9186
  toLocationId: string().min(1),
9187
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
9188
+ fromLocationId: string().optional(),
9056
9189
  /** Omitted = `move`. */
9057
9190
  mode: MediaRelocateModeSchema.optional()
9058
9191
  });
9059
9192
  /**
9193
+ * Operator cleanup of leftover analytics rows, optional debug media, and
9194
+ * ghost ledger entries on frozen footage locations.
9195
+ *
9196
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
9197
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
9198
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
9199
+ * with no operator-visible status.
9200
+ */
9201
+ var StorageCleanupPhaseSchema = _enum([
9202
+ "orphans",
9203
+ "debug-media",
9204
+ "ghost-ledger",
9205
+ "done",
9206
+ "failed",
9207
+ "cancelled"
9208
+ ]);
9209
+ var StorageCleanupInputSchema = object({
9210
+ /** Also walk motion stills / track filmstrips. Off by default. */
9211
+ includeDebugMedia: boolean().optional() });
9212
+ var StorageCleanupJobSchema = object({
9213
+ jobId: string(),
9214
+ phase: StorageCleanupPhaseSchema,
9215
+ includeDebugMedia: boolean(),
9216
+ orphansReclaimed: number().int().nonnegative(),
9217
+ orphanBytesReclaimed: number().int().nonnegative(),
9218
+ debugMediaReclaimed: number().int().nonnegative(),
9219
+ debugMediaBytesReclaimed: number().int().nonnegative(),
9220
+ ghostsForgotten: number().int().nonnegative(),
9221
+ ghostBytesForgotten: number().int().nonnegative(),
9222
+ /** Short operator-facing line: current collection, pass, or location. */
9223
+ detail: string().nullable(),
9224
+ cancelRequested: boolean(),
9225
+ startedAt: number(),
9226
+ updatedAt: number(),
9227
+ finishedAt: number().nullable(),
9228
+ error: string().nullable()
9229
+ });
9230
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
9231
+ /**
9060
9232
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9061
9233
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9062
9234
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9084,11 +9256,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
9084
9256
  * The default location for a type uses `id === <type>:default` by
9085
9257
  * convention (the bare type ref like `'backups'` resolves to it).
9086
9258
  *
9087
- * `isSystem: true` marks a location as orchestrator-seeded and
9088
- * undeletable. The bootstrap-installed defaults (one per type) carry
9089
- * this flag; operator-added locations don't. Editing the config of
9090
- * a system location is allowed (path migration, provider swap) but
9091
- * deleting it is rejected at the cap level.
9259
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
9260
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
9261
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
9262
+ * / last-enabled, not on this bit.
9092
9263
  */
9093
9264
  var StorageLocationSchema = object({
9094
9265
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13485,6 +13656,12 @@ method(object({
13485
13656
  }), _void(), {
13486
13657
  kind: "mutation",
13487
13658
  auth: "admin"
13659
+ }), method(object({
13660
+ from: string(),
13661
+ to: string()
13662
+ }), object({ moved: number() }), {
13663
+ kind: "mutation",
13664
+ auth: "admin"
13488
13665
  }), method(object({
13489
13666
  deviceId: number(),
13490
13667
  disabled: boolean()
@@ -19415,53 +19592,15 @@ var RecentTracksPageSchema = object({
19415
19592
  /** Cursor for the next page, or null when this page is the last. */
19416
19593
  nextCursor: string().nullable()
19417
19594
  });
19418
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19419
- var LIST_GROUPS_MAX_LIMIT = 100;
19420
- var AnalyticsGroupRecordSchema = object({
19421
- id: string(),
19422
- deviceId: number().int(),
19423
- openedAt: number().int(),
19424
- closedAt: number().int(),
19425
- timestamp: number().int(),
19426
- memberCount: number().int(),
19427
- memberTrackIds: array(string()).readonly(),
19428
- className: string(),
19429
- classes: array(string()).readonly(),
19430
- /** Relative event-media path, or null when the group has no picture yet. */
19431
- mediaUrl: string().nullable(),
19432
- singleton: boolean()
19433
- });
19434
- var AnalyticsGroupMemberSchema = object({
19435
- trackId: string(),
19436
- deviceId: number().int(),
19437
- className: string(),
19438
- firstSeen: number().int(),
19439
- lastSeen: number().int(),
19440
- mediaUrl: string().nullable()
19441
- });
19442
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19443
- var ListGroupsQueryInput = object({
19444
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19445
- deviceIds: array(number()),
19446
- /** Window lower bound on `closedAt` (inclusive). */
19447
- since: number().optional(),
19448
- /** Window upper bound on `openedAt` (inclusive). */
19449
- until: number().optional(),
19450
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19451
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19452
- cursor: string().optional()
19453
- });
19454
- var ListGroupsPageSchema = object({
19455
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19456
- nextCursor: string().nullable()
19457
- });
19595
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19596
+ var KEY_EVENTS_MAX_LIMIT = 200;
19458
19597
  var KeyEventQueryInput = object({
19459
19598
  deviceId: number(),
19460
19599
  /** Window lower bound (track firstSeen ≥ since). */
19461
19600
  since: number(),
19462
19601
  /** Window upper bound (track firstSeen ≤ until). */
19463
19602
  until: number(),
19464
- limit: number().int().min(1).max(200).default(50),
19603
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19465
19604
  /** Drop tracks scoring below this importance. */
19466
19605
  minImportance: number().min(0).max(1).optional(),
19467
19606
  /** Restrict to a single class (e.g. 'person'). */
@@ -19483,6 +19622,32 @@ var KeyEventSchema = object({
19483
19622
  ...TrackFlagFields,
19484
19623
  ...TrackRetrainFields
19485
19624
  });
19625
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19626
+ var KeyEventBatchQueryInput = object({
19627
+ deviceIds: array(number()).min(1).max(200),
19628
+ since: number(),
19629
+ until: number(),
19630
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19631
+ * across the set, which would let a busy camera starve a quiet one of its
19632
+ * rows and change what the merged feed contains. */
19633
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19634
+ minImportance: number().min(0).max(1).optional(),
19635
+ classFilter: string().optional()
19636
+ });
19637
+ /**
19638
+ * One camera's key events in a batch answer.
19639
+ *
19640
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19641
+ * error rather than throwing, so a camera whose store read failed and one with
19642
+ * no events in the window were ALREADY indistinguishable per camera — the
19643
+ * batch does not make that worse, and the row keeps the deviceId the single
19644
+ * method's output never carried (the caller used to stamp it from the fan-out
19645
+ * key, which only worked because there was one query per camera).
19646
+ */
19647
+ var KeyEventsForDeviceSchema = object({
19648
+ deviceId: number(),
19649
+ events: array(KeyEventSchema).readonly()
19650
+ });
19486
19651
  object({
19487
19652
  trackId: string(),
19488
19653
  className: string(),
@@ -19530,9 +19695,7 @@ var TrackCascadeCountsSchema = object({
19530
19695
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19531
19696
  plates: number().int(),
19532
19697
  /** Per-track CLIP search vectors removed (best-effort). */
19533
- embeddings: number().int(),
19534
- /** Group membership + group rows removed with their last member (best-effort). */
19535
- groups: number().int()
19698
+ embeddings: number().int()
19536
19699
  });
19537
19700
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19538
19701
  var DiskReconcileCountsSchema = object({
@@ -19702,6 +19865,47 @@ var RebuildStatusSchema = object({
19702
19865
  /** Present when the pass ended by throwing. */
19703
19866
  error: string().nullable()
19704
19867
  });
19868
+ /**
19869
+ * Acknowledgement that a debug-media reclaim STARTED.
19870
+ *
19871
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19872
+ * it runs detached and this returns immediately. Awaiting it is how the
19873
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19874
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19875
+ */
19876
+ var MediaReclaimStartResultSchema = object({
19877
+ started: boolean(),
19878
+ /** True when a pass was already running; the new request is ignored. */
19879
+ alreadyRunning: boolean()
19880
+ });
19881
+ var MediaReclaimInputSchema = object({
19882
+ mode: _enum(["report", "reclaim"]).default("report"),
19883
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19884
+ deviceIds: array(number().int()).min(1).optional(),
19885
+ restart: boolean().optional(),
19886
+ pageSize: number().int().min(50).max(5e3).optional(),
19887
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19888
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19889
+ maxBytesPerRun: number().int().min(1).optional(),
19890
+ budgetMinutes: number().int().min(1).max(720).optional(),
19891
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19892
+ graceMinutes: number().int().min(1).max(10080).optional()
19893
+ });
19894
+ var MediaReclaimStatusSchema = object({
19895
+ running: boolean(),
19896
+ mode: _enum(["report", "reclaim"]).nullable(),
19897
+ totalExamined: number(),
19898
+ totalEligible: number(),
19899
+ totalReclaimed: number(),
19900
+ totalBytesReclaimed: number(),
19901
+ totalRefused: number(),
19902
+ /** Device+scope windows finished in this pass. */
19903
+ devicesDone: number(),
19904
+ complete: boolean().nullable(),
19905
+ startedAtMs: number().nullable(),
19906
+ finishedAtMs: number().nullable(),
19907
+ error: string().nullable()
19908
+ });
19705
19909
  var ReplayFrameInputSchema = object({
19706
19910
  timestamp: number(),
19707
19911
  frame: PipelineRunResultBridge
@@ -19740,10 +19944,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19740
19944
  * stationary registry). Default false: the timeline lists passages,
19741
19945
  * not parking records (operator decision, 2026-08-15). */
19742
19946
  includeStationary: boolean().optional()
19743
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19744
- deviceId: number(),
19745
- groupId: string().min(1)
19746
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19947
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19747
19948
  kind: "mutation",
19748
19949
  auth: "admin"
19749
19950
  }), 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({
@@ -19752,7 +19953,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19752
19953
  until: number().optional(),
19753
19954
  kinds: array(string()).optional(),
19754
19955
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19755
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19956
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19756
19957
  deviceId: number(),
19757
19958
  since: number(),
19758
19959
  until: number(),
@@ -19843,6 +20044,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19843
20044
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19844
20045
  kind: "query",
19845
20046
  auth: "admin"
20047
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
20048
+ kind: "mutation",
20049
+ auth: "admin"
20050
+ }), method(object({}), MediaReclaimStatusSchema, {
20051
+ kind: "query",
20052
+ auth: "admin"
19846
20053
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19847
20054
  kind: "query",
19848
20055
  auth: "admin"
@@ -21813,7 +22020,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21813
22020
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21814
22021
  kind: "mutation",
21815
22022
  auth: "admin"
21816
- });
22023
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
22024
+ kind: "mutation",
22025
+ auth: "admin"
22026
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22027
+ kind: "mutation",
22028
+ auth: "admin"
22029
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21817
22030
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21818
22031
  providerId: string().min(1),
21819
22032
  displayName: string().min(1),
@@ -27239,6 +27452,33 @@ var RecordingRebalanceInputSchema = object({
27239
27452
  minMoveGb: number().min(0).optional()
27240
27453
  });
27241
27454
  /**
27455
+ * Operator-facing placement of one camera onto a recordings location.
27456
+ *
27457
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
27458
+ * Auto (the planner may move this camera). `locationId` is where high/mid
27459
+ * currently write — the plan, which may disagree with the pin when Auto.
27460
+ */
27461
+ var RecordingDevicePlacementSchema = object({
27462
+ deviceId: number().int(),
27463
+ profile: string(),
27464
+ locationId: string()
27465
+ });
27466
+ var RecordingDevicePinSchema = object({
27467
+ deviceId: number().int(),
27468
+ /** Recordings-class location this camera is pinned to. */
27469
+ locationId: string()
27470
+ });
27471
+ var RecordingPlacementViewSchema = object({
27472
+ assignments: array(RecordingDevicePlacementSchema),
27473
+ pins: array(RecordingDevicePinSchema),
27474
+ defaultLocations: record(string(), string())
27475
+ });
27476
+ var RecordingSetDevicePlacementInputSchema = object({
27477
+ deviceId: number().int(),
27478
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
27479
+ locationId: string().nullable()
27480
+ });
27481
+ /**
27242
27482
  * Result of locating footage at a wall-clock instant for one device/profile.
27243
27483
  * `segment` carries the covering segment's window; `gap` reports the forward
27244
27484
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -27452,6 +27692,9 @@ method(object({
27452
27692
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
27453
27693
  kind: "query",
27454
27694
  auth: "admin"
27695
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27696
+ kind: "mutation",
27697
+ auth: "admin"
27455
27698
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
27456
27699
  kind: "mutation",
27457
27700
  auth: "admin"
@@ -27461,6 +27704,12 @@ method(object({
27461
27704
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
27462
27705
  kind: "mutation",
27463
27706
  auth: "admin"
27707
+ }), method(object({}), RecordingPlacementViewSchema, {
27708
+ kind: "query",
27709
+ auth: "admin"
27710
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27711
+ kind: "mutation",
27712
+ auth: "admin"
27464
27713
  });
27465
27714
  /**
27466
27715
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27843,7 +28092,26 @@ var SceneMonitorStatusSchema = object({
27843
28092
  monitors: array(SceneMonitorSchema),
27844
28093
  lastFetchedAt: number()
27845
28094
  });
27846
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
28095
+ /**
28096
+ * One camera's row in a `listScenesBatch` answer.
28097
+ *
28098
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
28099
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
28100
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
28101
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
28102
+ * configured. Fanned out per camera the difference was visible — one query
28103
+ * errored while the others resolved — and a batch that returned only the rows
28104
+ * it managed would have destroyed it, silently, by making an unreachable camera
28105
+ * indistinguishable from one that answered `monitors: []`.
28106
+ *
28107
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
28108
+ * could not be read"; `status.monitors: []` means "read, and it has none".
28109
+ */
28110
+ var SceneMonitorStatusForDeviceSchema = object({
28111
+ deviceId: number(),
28112
+ status: SceneMonitorStatusSchema.nullable()
28113
+ });
28114
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27847
28115
  deviceId: number(),
27848
28116
  label: string(),
27849
28117
  roi: MaskRectShapeSchema,
@@ -29192,6 +29460,27 @@ var CameraOccupancySnapshotSchema = object({
29192
29460
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
29193
29461
  });
29194
29462
  /**
29463
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
29464
+ *
29465
+ * THREE outcomes, and the single-camera method could only express two of them
29466
+ * because `snapshot: null` was already spoken for:
29467
+ *
29468
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
29469
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
29470
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
29471
+ * - `read: 'unreadable'` — the owner could not answer for this
29472
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
29473
+ *
29474
+ * Collapsing the last two is the failure this field exists to prevent: a
29475
+ * hydration that threw would otherwise render as an empty Stationary section,
29476
+ * which is a definite claim about a camera nobody could read.
29477
+ */
29478
+ var CameraOccupancySnapshotForDeviceSchema = object({
29479
+ deviceId: number(),
29480
+ read: _enum(["read", "unreadable"]),
29481
+ snapshot: CameraOccupancySnapshotSchema.nullable()
29482
+ });
29483
+ /**
29195
29484
  * Time-series resolution. The history methods return one bucket per
29196
29485
  * step over the requested range. Smaller resolutions cost more
29197
29486
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -29215,7 +29504,7 @@ var HistoryPointSchema = object({
29215
29504
  /** Object count averaged over the bucket (rounded to nearest integer). */
29216
29505
  count: number().int().nonnegative()
29217
29506
  });
29218
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
29507
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
29219
29508
  deviceId: number(),
29220
29509
  zoneId: string(),
29221
29510
  className: string().optional()
@@ -30816,6 +31105,12 @@ Object.freeze({
30816
31105
  addonId: null,
30817
31106
  access: "delete"
30818
31107
  },
31108
+ "deviceManager.renameLocation": {
31109
+ capName: "device-manager",
31110
+ capScope: "system",
31111
+ addonId: null,
31112
+ access: "create"
31113
+ },
30819
31114
  "deviceManager.runDeviceAction": {
30820
31115
  capName: "device-manager",
30821
31116
  capScope: "system",
@@ -32508,13 +32803,19 @@ Object.freeze({
32508
32803
  addonId: null,
32509
32804
  access: "view"
32510
32805
  },
32511
- "pipelineAnalytics.getGroup": {
32806
+ "pipelineAnalytics.getKeyEvents": {
32512
32807
  capName: "pipeline-analytics",
32513
32808
  capScope: "device",
32514
32809
  addonId: null,
32515
32810
  access: "view"
32516
32811
  },
32517
- "pipelineAnalytics.getKeyEvents": {
32812
+ "pipelineAnalytics.getKeyEventsBatch": {
32813
+ capName: "pipeline-analytics",
32814
+ capScope: "device",
32815
+ addonId: null,
32816
+ access: "view"
32817
+ },
32818
+ "pipelineAnalytics.getMediaReclaimStatus": {
32518
32819
  capName: "pipeline-analytics",
32519
32820
  capScope: "device",
32520
32821
  addonId: null,
@@ -32604,12 +32905,6 @@ Object.freeze({
32604
32905
  addonId: null,
32605
32906
  access: "view"
32606
32907
  },
32607
- "pipelineAnalytics.listGroups": {
32608
- capName: "pipeline-analytics",
32609
- capScope: "device",
32610
- addonId: null,
32611
- access: "view"
32612
- },
32613
32908
  "pipelineAnalytics.listOpsLog": {
32614
32909
  capName: "pipeline-analytics",
32615
32910
  capScope: "device",
@@ -32694,6 +32989,12 @@ Object.freeze({
32694
32989
  addonId: null,
32695
32990
  access: "create"
32696
32991
  },
32992
+ "pipelineAnalytics.reclaimDebugMedia": {
32993
+ capName: "pipeline-analytics",
32994
+ capScope: "device",
32995
+ addonId: null,
32996
+ access: "create"
32997
+ },
32697
32998
  "pipelineAnalytics.reconcileFromDisk": {
32698
32999
  capName: "pipeline-analytics",
32699
33000
  capScope: "device",
@@ -33618,6 +33919,12 @@ Object.freeze({
33618
33919
  addonId: null,
33619
33920
  access: "view"
33620
33921
  },
33922
+ "recording.getPlacement": {
33923
+ capName: "recording",
33924
+ capScope: "system",
33925
+ addonId: null,
33926
+ access: "view"
33927
+ },
33621
33928
  "recording.getPlaybackManifest": {
33622
33929
  capName: "recording",
33623
33930
  capScope: "system",
@@ -33696,6 +34003,12 @@ Object.freeze({
33696
34003
  addonId: null,
33697
34004
  access: "view"
33698
34005
  },
34006
+ "recording.reconcileLedgerAgainstDisk": {
34007
+ capName: "recording",
34008
+ capScope: "system",
34009
+ addonId: null,
34010
+ access: "create"
34011
+ },
33699
34012
  "recording.refreshStorageLocationsForMigration": {
33700
34013
  capName: "recording",
33701
34014
  capScope: "system",
@@ -33738,6 +34051,12 @@ Object.freeze({
33738
34051
  addonId: null,
33739
34052
  access: "create"
33740
34053
  },
34054
+ "recording.setDevicePlacement": {
34055
+ capName: "recording",
34056
+ capScope: "system",
34057
+ addonId: null,
34058
+ access: "create"
34059
+ },
33741
34060
  "recording.startStorageMigrationMove": {
33742
34061
  capName: "recording",
33743
34062
  capScope: "system",
@@ -33822,6 +34141,12 @@ Object.freeze({
33822
34141
  addonId: null,
33823
34142
  access: "view"
33824
34143
  },
34144
+ "sceneMonitor.listScenesBatch": {
34145
+ capName: "scene-monitor",
34146
+ capScope: "device",
34147
+ addonId: null,
34148
+ access: "view"
34149
+ },
33825
34150
  "sceneMonitor.recheckNow": {
33826
34151
  capName: "scene-monitor",
33827
34152
  capScope: "device",
@@ -34176,12 +34501,36 @@ Object.freeze({
34176
34501
  addonId: null,
34177
34502
  access: "create"
34178
34503
  },
34504
+ "storageMigration.cleanupCancel": {
34505
+ capName: "storage-migration",
34506
+ capScope: "system",
34507
+ addonId: null,
34508
+ access: "create"
34509
+ },
34510
+ "storageMigration.cleanupStart": {
34511
+ capName: "storage-migration",
34512
+ capScope: "system",
34513
+ addonId: null,
34514
+ access: "create"
34515
+ },
34516
+ "storageMigration.cleanupStatus": {
34517
+ capName: "storage-migration",
34518
+ capScope: "system",
34519
+ addonId: null,
34520
+ access: "view"
34521
+ },
34179
34522
  "storageMigration.drain": {
34180
34523
  capName: "storage-migration",
34181
34524
  capScope: "system",
34182
34525
  addonId: null,
34183
34526
  access: "create"
34184
34527
  },
34528
+ "storageMigration.history": {
34529
+ capName: "storage-migration",
34530
+ capScope: "system",
34531
+ addonId: null,
34532
+ access: "view"
34533
+ },
34185
34534
  "storageMigration.movers": {
34186
34535
  capName: "storage-migration",
34187
34536
  capScope: "system",
@@ -35178,6 +35527,12 @@ Object.freeze({
35178
35527
  addonId: null,
35179
35528
  access: "view"
35180
35529
  },
35530
+ "zoneAnalytics.getCurrentSnapshotBatch": {
35531
+ capName: "zone-analytics",
35532
+ capScope: "device",
35533
+ addonId: null,
35534
+ access: "view"
35535
+ },
35181
35536
  "zoneAnalytics.getUnzonedHistory": {
35182
35537
  capName: "zone-analytics",
35183
35538
  capScope: "device",
@@ -36172,14 +36527,14 @@ Object.freeze({
36172
36527
  form: "single",
36173
36528
  optional: true
36174
36529
  }],
36175
- "pipelineAnalytics.getGroup": [{
36530
+ "pipelineAnalytics.getKeyEvents": [{
36176
36531
  name: "deviceId",
36177
36532
  form: "single",
36178
36533
  optional: false
36179
36534
  }],
36180
- "pipelineAnalytics.getKeyEvents": [{
36181
- name: "deviceId",
36182
- form: "single",
36535
+ "pipelineAnalytics.getKeyEventsBatch": [{
36536
+ name: "deviceIds",
36537
+ form: "array",
36183
36538
  optional: false
36184
36539
  }],
36185
36540
  "pipelineAnalytics.getMotionEvents": [{
@@ -36237,11 +36592,6 @@ Object.freeze({
36237
36592
  form: "single",
36238
36593
  optional: false
36239
36594
  }],
36240
- "pipelineAnalytics.listGroups": [{
36241
- name: "deviceIds",
36242
- form: "array",
36243
- optional: false
36244
- }],
36245
36595
  "pipelineAnalytics.listOpsLog": [{
36246
36596
  name: "deviceId",
36247
36597
  form: "single",
@@ -36287,6 +36637,11 @@ Object.freeze({
36287
36637
  form: "single",
36288
36638
  optional: true
36289
36639
  }],
36640
+ "pipelineAnalytics.reclaimDebugMedia": [{
36641
+ name: "deviceIds",
36642
+ form: "array",
36643
+ optional: true
36644
+ }],
36290
36645
  "pipelineAnalytics.reconcileFromDisk": [{
36291
36646
  name: "deviceId",
36292
36647
  form: "single",
@@ -36622,6 +36977,11 @@ Object.freeze({
36622
36977
  form: "single",
36623
36978
  optional: false
36624
36979
  }],
36980
+ "recording.reconcileLedgerAgainstDisk": [{
36981
+ name: "deviceId",
36982
+ form: "single",
36983
+ optional: true
36984
+ }],
36625
36985
  "recording.relocateFootage": [{
36626
36986
  name: "deviceId",
36627
36987
  form: "single",
@@ -36647,6 +37007,11 @@ Object.freeze({
36647
37007
  form: "single",
36648
37008
  optional: false
36649
37009
  }],
37010
+ "recording.setDevicePlacement": [{
37011
+ name: "deviceId",
37012
+ form: "single",
37013
+ optional: false
37014
+ }],
36650
37015
  "recording.startStorageMigrationMove": [{
36651
37016
  name: "deviceId",
36652
37017
  form: "single",
@@ -36687,6 +37052,11 @@ Object.freeze({
36687
37052
  form: "single",
36688
37053
  optional: false
36689
37054
  }],
37055
+ "sceneMonitor.listScenesBatch": [{
37056
+ name: "deviceIds",
37057
+ form: "array",
37058
+ optional: false
37059
+ }],
36690
37060
  "sceneMonitor.recheckNow": [{
36691
37061
  name: "deviceId",
36692
37062
  form: "single",
@@ -36948,6 +37318,11 @@ Object.freeze({
36948
37318
  form: "single",
36949
37319
  optional: false
36950
37320
  }],
37321
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
37322
+ name: "deviceIds",
37323
+ form: "array",
37324
+ optional: false
37325
+ }],
36951
37326
  "zoneAnalytics.getUnzonedHistory": [{
36952
37327
  name: "deviceId",
36953
37328
  form: "single",