@camstack/addon-provider-rademacher 0.2.47 → 0.2.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +478 -73
  2. package/dist/addon.mjs +478 -73
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -9003,6 +9003,20 @@ var RelocateJobSchema = object({
9003
9003
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
9004
9004
  */
9005
9005
  rowsReconciled: number().int().nonnegative().optional(),
9006
+ /**
9007
+ * Rows this run FORGOT because the file they name is not on disk.
9008
+ *
9009
+ * The mover derived the path from the row's own fields and `stat`ed it; an
9010
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
9011
+ * and the durable row is dropped through the same channel eviction uses. It
9012
+ * is reported for the same reason `rowsReconciled` is: this is a durable
9013
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
9014
+ * the same failure as one that quietly skips them (D295).
9015
+ *
9016
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
9017
+ * ledger claimed 5.65 GB of footage that no longer existed.
9018
+ */
9019
+ rowsForgotten: number().int().nonnegative().optional(),
9006
9020
  startedAt: number(),
9007
9021
  finishedAt: number().nullable(),
9008
9022
  error: string().nullable()
@@ -9067,6 +9081,13 @@ var MediaRelocateModeSchema = _enum([
9067
9081
  ]);
9068
9082
  var RelocateMediaInputSchema = object({
9069
9083
  toLocationId: string(),
9084
+ /**
9085
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
9086
+ * every row that is not already on `toLocationId` (the historical
9087
+ * behaviour). A named source is what a from→to migration needs: without it
9088
+ * "move events off disk 2" also emptied disk 1.
9089
+ */
9090
+ fromLocationId: string().optional(),
9070
9091
  throttleMbps: number().min(1).max(1e3).optional(),
9071
9092
  /** Omitted = `move`, the pre-existing behaviour. */
9072
9093
  mode: MediaRelocateModeSchema.optional()
@@ -9134,6 +9155,19 @@ var StorageMigrationDestinationsSchema = object({
9134
9155
  galleryMedia: string().min(1).optional()
9135
9156
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
9136
9157
  /**
9158
+ * Optional named source per class. Omitted = the class's current default
9159
+ * (the historical behaviour). A named source that is NOT the default is a
9160
+ * drain of that disk: bytes move, the default stays, and the source is
9161
+ * disabled when the move finishes.
9162
+ */
9163
+ var StorageMigrationSourcesSchema = object({
9164
+ recordings: string().min(1).optional(),
9165
+ recordingsLow: string().min(1).optional(),
9166
+ eventMedia: string().min(1).optional(),
9167
+ backups: string().min(1).optional(),
9168
+ galleryMedia: string().min(1).optional()
9169
+ }).optional();
9170
+ /**
9137
9171
  * How a migration sequences the cutover against the byte move.
9138
9172
  *
9139
9173
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -9155,6 +9189,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
9155
9189
  /** Shared input for planning and starting an orchestrated storage migration. */
9156
9190
  var StorageMigrationInputSchema = object({
9157
9191
  destinations: StorageMigrationDestinationsSchema,
9192
+ /** Omitted = each class's current default. */
9193
+ sources: StorageMigrationSourcesSchema,
9158
9194
  throttleMbps: number().min(1).max(1e3).optional(),
9159
9195
  /** Omitted = `blocking`, which stays the default. */
9160
9196
  mode: StorageMigrationModeSchema.optional()
@@ -9234,6 +9270,13 @@ var StorageMigrationMoveSchema = object({
9234
9270
  storageClass: StorageMigrationClassSchema,
9235
9271
  fromLocationId: string(),
9236
9272
  toLocationId: string(),
9273
+ /**
9274
+ * True when `from` was NOT the class default at plan time. The move still
9275
+ * copies bytes, but the default is left alone and the source is disabled
9276
+ * once the copy verifies. Absent on jobs planned before this field existed
9277
+ * — those jobs always repointed, which is `false`.
9278
+ */
9279
+ freezeSource: boolean().optional(),
9237
9280
  moverJobId: string().nullable(),
9238
9281
  state: RelocateJobStateSchema.nullable(),
9239
9282
  error: string().nullable(),
@@ -9247,6 +9290,7 @@ var StorageMigrationJobSchema = object({
9247
9290
  * can tell a seconds-long cutover from a thirty-hour one. */
9248
9291
  mode: StorageMigrationModeSchema,
9249
9292
  destinations: StorageMigrationDestinationsSchema,
9293
+ sources: StorageMigrationSourcesSchema,
9250
9294
  throttleMbps: number(),
9251
9295
  moves: array(StorageMigrationMoveSchema),
9252
9296
  pauseLeaseId: string().nullable(),
@@ -9272,6 +9316,7 @@ var StorageMigrationFindingSchema = object({
9272
9316
  });
9273
9317
  var StorageMigrationPlanSchema = object({
9274
9318
  destinations: StorageMigrationDestinationsSchema,
9319
+ sources: StorageMigrationSourcesSchema,
9275
9320
  /** The mode this plan was built for. A plan is only valid for its mode: the
9276
9321
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
9277
9322
  * it. */
@@ -9279,7 +9324,8 @@ var StorageMigrationPlanSchema = object({
9279
9324
  moves: array(object({
9280
9325
  storageClass: StorageMigrationClassSchema,
9281
9326
  fromLocationId: string(),
9282
- toLocationId: string()
9327
+ toLocationId: string(),
9328
+ freezeSource: boolean().optional()
9283
9329
  })),
9284
9330
  findings: array(StorageMigrationFindingSchema)
9285
9331
  });
@@ -9366,16 +9412,142 @@ var RelocateResidueSchema = object({
9366
9412
  segments: number().int().nonnegative(),
9367
9413
  bytes: number().int().nonnegative()
9368
9414
  }).nullable();
9415
+ /**
9416
+ * Ask one location whether its durable hour rows describe the disk — the walk
9417
+ * (D319).
9418
+ *
9419
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
9420
+ * missing tool is the question, and the dry run is how they sanity-check the
9421
+ * destructive run before authorising it.
9422
+ */
9423
+ var LedgerWalkInputSchema = object({
9424
+ locationId: string().min(1),
9425
+ /** Forget the confirmed-absent rows, rather than only counting them. */
9426
+ apply: boolean().optional(),
9427
+ /** Narrow to one camera. */
9428
+ deviceId: number().int().positive().optional(),
9429
+ /** Narrow to these recording profiles; empty/absent = every profile. */
9430
+ profiles: array(string().min(1)).optional()
9431
+ });
9432
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
9433
+ var LedgerWalkRefusalSchema = _enum([
9434
+ "location-unknown",
9435
+ "source-writable",
9436
+ "no-ledger",
9437
+ "archive-unreadable",
9438
+ "anchor-absent",
9439
+ "anchor-unreadable",
9440
+ "anchor-moved"
9441
+ ]);
9442
+ _enum([
9443
+ "live-tail",
9444
+ "listing-error",
9445
+ "path-mismatch",
9446
+ "durable-refused"
9447
+ ]);
9448
+ /** Every skip reason, always present, always a number — so a reason that never
9449
+ * fired reports as zero rather than absent and the report shape is constant
9450
+ * between passes. Spelled out rather than `z.record` for exactly that. */
9451
+ var LedgerWalkSkipCountsSchema = object({
9452
+ "live-tail": number().int().nonnegative(),
9453
+ "listing-error": number().int().nonnegative(),
9454
+ "path-mismatch": number().int().nonnegative(),
9455
+ "durable-refused": number().int().nonnegative()
9456
+ });
9457
+ /** One camera's share of a walk, so a report names cameras and not rows. */
9458
+ var LedgerWalkDeviceReportSchema = object({
9459
+ deviceId: number().int(),
9460
+ hoursWalked: number().int().nonnegative(),
9461
+ hoursMissing: number().int().nonnegative(),
9462
+ ghostSegments: number().int().nonnegative(),
9463
+ ghostBytes: number().int().nonnegative(),
9464
+ forgottenSegments: number().int().nonnegative(),
9465
+ orphanFiles: number().int().nonnegative()
9466
+ });
9467
+ /**
9468
+ * What one walk claimed, listed, found and (only when armed) forgot.
9469
+ *
9470
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
9471
+ * walk that saw a fraction of the location is visible in its own report rather
9472
+ * than in the absence of one.
9473
+ */
9474
+ var LedgerWalkReportSchema = object({
9475
+ locationId: string(),
9476
+ applied: boolean(),
9477
+ refused: LedgerWalkRefusalSchema.nullable(),
9478
+ archiveSegments: number().int().nonnegative().nullable(),
9479
+ archiveBytes: number().int().nonnegative().nullable(),
9480
+ hoursClaimed: number().int().nonnegative(),
9481
+ hoursWalked: number().int().nonnegative(),
9482
+ hoursMissing: number().int().nonnegative(),
9483
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
9484
+ listings: number().int().nonnegative(),
9485
+ segmentsClaimed: number().int().nonnegative(),
9486
+ ghostSegments: number().int().nonnegative(),
9487
+ ghostBytes: number().int().nonnegative(),
9488
+ ghostHoursWhole: number().int().nonnegative(),
9489
+ forgottenSegments: number().int().nonnegative(),
9490
+ forgottenBytes: number().int().nonnegative(),
9491
+ /** Files under a claimed hour that no durable row names. Never deleted. */
9492
+ orphanFiles: number().int().nonnegative(),
9493
+ orphanSample: array(string()).readonly(),
9494
+ hoursSkipped: number().int().nonnegative(),
9495
+ skippedByReason: LedgerWalkSkipCountsSchema,
9496
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
9497
+ bounded: boolean(),
9498
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
9499
+ });
9369
9500
  /** How many rows a media pass would still act on against a given target — the
9370
9501
  * media lane's denominator AND its residue, from ONE derivation so the two can
9371
9502
  * never disagree. `null` = the count could not be taken. */
9372
9503
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9373
9504
  var RelocatableMediaCountInputSchema = object({
9374
9505
  toLocationId: string().min(1),
9506
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
9507
+ fromLocationId: string().optional(),
9375
9508
  /** Omitted = `move`. */
9376
9509
  mode: MediaRelocateModeSchema.optional()
9377
9510
  });
9378
9511
  /**
9512
+ * Operator cleanup of leftover analytics rows, optional debug media, and
9513
+ * ghost ledger entries on frozen footage locations.
9514
+ *
9515
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
9516
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
9517
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
9518
+ * with no operator-visible status.
9519
+ */
9520
+ var StorageCleanupPhaseSchema = _enum([
9521
+ "orphans",
9522
+ "debug-media",
9523
+ "ghost-ledger",
9524
+ "done",
9525
+ "failed",
9526
+ "cancelled"
9527
+ ]);
9528
+ var StorageCleanupInputSchema = object({
9529
+ /** Also walk motion stills / track filmstrips. Off by default. */
9530
+ includeDebugMedia: boolean().optional() });
9531
+ var StorageCleanupJobSchema = object({
9532
+ jobId: string(),
9533
+ phase: StorageCleanupPhaseSchema,
9534
+ includeDebugMedia: boolean(),
9535
+ orphansReclaimed: number().int().nonnegative(),
9536
+ orphanBytesReclaimed: number().int().nonnegative(),
9537
+ debugMediaReclaimed: number().int().nonnegative(),
9538
+ debugMediaBytesReclaimed: number().int().nonnegative(),
9539
+ ghostsForgotten: number().int().nonnegative(),
9540
+ ghostBytesForgotten: number().int().nonnegative(),
9541
+ /** Short operator-facing line: current collection, pass, or location. */
9542
+ detail: string().nullable(),
9543
+ cancelRequested: boolean(),
9544
+ startedAt: number(),
9545
+ updatedAt: number(),
9546
+ finishedAt: number().nullable(),
9547
+ error: string().nullable()
9548
+ });
9549
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
9550
+ /**
9379
9551
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9380
9552
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9381
9553
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9403,11 +9575,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
9403
9575
  * The default location for a type uses `id === <type>:default` by
9404
9576
  * convention (the bare type ref like `'backups'` resolves to it).
9405
9577
  *
9406
- * `isSystem: true` marks a location as orchestrator-seeded and
9407
- * undeletable. The bootstrap-installed defaults (one per type) carry
9408
- * this flag; operator-added locations don't. Editing the config of
9409
- * a system location is allowed (path migration, provider swap) but
9410
- * deleting it is rejected at the cap level.
9578
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
9579
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
9580
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
9581
+ * / last-enabled, not on this bit.
9411
9582
  */
9412
9583
  var StorageLocationSchema = object({
9413
9584
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13933,6 +14104,12 @@ method(object({
13933
14104
  }), _void(), {
13934
14105
  kind: "mutation",
13935
14106
  auth: "admin"
14107
+ }), method(object({
14108
+ from: string(),
14109
+ to: string()
14110
+ }), object({ moved: number() }), {
14111
+ kind: "mutation",
14112
+ auth: "admin"
13936
14113
  }), method(object({
13937
14114
  deviceId: number(),
13938
14115
  disabled: boolean()
@@ -19941,53 +20118,15 @@ var RecentTracksPageSchema = object({
19941
20118
  /** Cursor for the next page, or null when this page is the last. */
19942
20119
  nextCursor: string().nullable()
19943
20120
  });
19944
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19945
- var LIST_GROUPS_MAX_LIMIT = 100;
19946
- var AnalyticsGroupRecordSchema = object({
19947
- id: string(),
19948
- deviceId: number().int(),
19949
- openedAt: number().int(),
19950
- closedAt: number().int(),
19951
- timestamp: number().int(),
19952
- memberCount: number().int(),
19953
- memberTrackIds: array(string()).readonly(),
19954
- className: string(),
19955
- classes: array(string()).readonly(),
19956
- /** Relative event-media path, or null when the group has no picture yet. */
19957
- mediaUrl: string().nullable(),
19958
- singleton: boolean()
19959
- });
19960
- var AnalyticsGroupMemberSchema = object({
19961
- trackId: string(),
19962
- deviceId: number().int(),
19963
- className: string(),
19964
- firstSeen: number().int(),
19965
- lastSeen: number().int(),
19966
- mediaUrl: string().nullable()
19967
- });
19968
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19969
- var ListGroupsQueryInput = object({
19970
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19971
- deviceIds: array(number()),
19972
- /** Window lower bound on `closedAt` (inclusive). */
19973
- since: number().optional(),
19974
- /** Window upper bound on `openedAt` (inclusive). */
19975
- until: number().optional(),
19976
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19977
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19978
- cursor: string().optional()
19979
- });
19980
- var ListGroupsPageSchema = object({
19981
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19982
- nextCursor: string().nullable()
19983
- });
20121
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
20122
+ var KEY_EVENTS_MAX_LIMIT = 200;
19984
20123
  var KeyEventQueryInput = object({
19985
20124
  deviceId: number(),
19986
20125
  /** Window lower bound (track firstSeen ≥ since). */
19987
20126
  since: number(),
19988
20127
  /** Window upper bound (track firstSeen ≤ until). */
19989
20128
  until: number(),
19990
- limit: number().int().min(1).max(200).default(50),
20129
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19991
20130
  /** Drop tracks scoring below this importance. */
19992
20131
  minImportance: number().min(0).max(1).optional(),
19993
20132
  /** Restrict to a single class (e.g. 'person'). */
@@ -20009,6 +20148,32 @@ var KeyEventSchema = object({
20009
20148
  ...TrackFlagFields,
20010
20149
  ...TrackRetrainFields
20011
20150
  });
20151
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
20152
+ var KeyEventBatchQueryInput = object({
20153
+ deviceIds: array(number()).min(1).max(200),
20154
+ since: number(),
20155
+ until: number(),
20156
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
20157
+ * across the set, which would let a busy camera starve a quiet one of its
20158
+ * rows and change what the merged feed contains. */
20159
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
20160
+ minImportance: number().min(0).max(1).optional(),
20161
+ classFilter: string().optional()
20162
+ });
20163
+ /**
20164
+ * One camera's key events in a batch answer.
20165
+ *
20166
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
20167
+ * error rather than throwing, so a camera whose store read failed and one with
20168
+ * no events in the window were ALREADY indistinguishable per camera — the
20169
+ * batch does not make that worse, and the row keeps the deviceId the single
20170
+ * method's output never carried (the caller used to stamp it from the fan-out
20171
+ * key, which only worked because there was one query per camera).
20172
+ */
20173
+ var KeyEventsForDeviceSchema = object({
20174
+ deviceId: number(),
20175
+ events: array(KeyEventSchema).readonly()
20176
+ });
20012
20177
  object({
20013
20178
  trackId: string(),
20014
20179
  className: string(),
@@ -20056,9 +20221,7 @@ var TrackCascadeCountsSchema = object({
20056
20221
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
20057
20222
  plates: number().int(),
20058
20223
  /** Per-track CLIP search vectors removed (best-effort). */
20059
- embeddings: number().int(),
20060
- /** Group membership + group rows removed with their last member (best-effort). */
20061
- groups: number().int()
20224
+ embeddings: number().int()
20062
20225
  });
20063
20226
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
20064
20227
  var DiskReconcileCountsSchema = object({
@@ -20228,6 +20391,47 @@ var RebuildStatusSchema = object({
20228
20391
  /** Present when the pass ended by throwing. */
20229
20392
  error: string().nullable()
20230
20393
  });
20394
+ /**
20395
+ * Acknowledgement that a debug-media reclaim STARTED.
20396
+ *
20397
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
20398
+ * it runs detached and this returns immediately. Awaiting it is how the
20399
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
20400
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
20401
+ */
20402
+ var MediaReclaimStartResultSchema = object({
20403
+ started: boolean(),
20404
+ /** True when a pass was already running; the new request is ignored. */
20405
+ alreadyRunning: boolean()
20406
+ });
20407
+ var MediaReclaimInputSchema = object({
20408
+ mode: _enum(["report", "reclaim"]).default("report"),
20409
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
20410
+ deviceIds: array(number().int()).min(1).optional(),
20411
+ restart: boolean().optional(),
20412
+ pageSize: number().int().min(50).max(5e3).optional(),
20413
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
20414
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
20415
+ maxBytesPerRun: number().int().min(1).optional(),
20416
+ budgetMinutes: number().int().min(1).max(720).optional(),
20417
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
20418
+ graceMinutes: number().int().min(1).max(10080).optional()
20419
+ });
20420
+ var MediaReclaimStatusSchema = object({
20421
+ running: boolean(),
20422
+ mode: _enum(["report", "reclaim"]).nullable(),
20423
+ totalExamined: number(),
20424
+ totalEligible: number(),
20425
+ totalReclaimed: number(),
20426
+ totalBytesReclaimed: number(),
20427
+ totalRefused: number(),
20428
+ /** Device+scope windows finished in this pass. */
20429
+ devicesDone: number(),
20430
+ complete: boolean().nullable(),
20431
+ startedAtMs: number().nullable(),
20432
+ finishedAtMs: number().nullable(),
20433
+ error: string().nullable()
20434
+ });
20231
20435
  var ReplayFrameInputSchema = object({
20232
20436
  timestamp: number(),
20233
20437
  frame: PipelineRunResultBridge
@@ -20266,10 +20470,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20266
20470
  * stationary registry). Default false: the timeline lists passages,
20267
20471
  * not parking records (operator decision, 2026-08-15). */
20268
20472
  includeStationary: boolean().optional()
20269
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
20270
- deviceId: number(),
20271
- groupId: string().min(1)
20272
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20473
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
20273
20474
  kind: "mutation",
20274
20475
  auth: "admin"
20275
20476
  }), 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({
@@ -20278,7 +20479,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20278
20479
  until: number().optional(),
20279
20480
  kinds: array(string()).optional(),
20280
20481
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20281
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
20482
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20282
20483
  deviceId: number(),
20283
20484
  since: number(),
20284
20485
  until: number(),
@@ -20369,6 +20570,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20369
20570
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20370
20571
  kind: "query",
20371
20572
  auth: "admin"
20573
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
20574
+ kind: "mutation",
20575
+ auth: "admin"
20576
+ }), method(object({}), MediaReclaimStatusSchema, {
20577
+ kind: "query",
20578
+ auth: "admin"
20372
20579
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
20373
20580
  kind: "query",
20374
20581
  auth: "admin"
@@ -22339,7 +22546,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
22339
22546
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
22340
22547
  kind: "mutation",
22341
22548
  auth: "admin"
22342
- });
22549
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
22550
+ kind: "mutation",
22551
+ auth: "admin"
22552
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22553
+ kind: "mutation",
22554
+ auth: "admin"
22555
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
22343
22556
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
22344
22557
  providerId: string().min(1),
22345
22558
  displayName: string().min(1),
@@ -29432,6 +29645,33 @@ var RecordingRebalanceInputSchema = object({
29432
29645
  minMoveGb: number().min(0).optional()
29433
29646
  });
29434
29647
  /**
29648
+ * Operator-facing placement of one camera onto a recordings location.
29649
+ *
29650
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
29651
+ * Auto (the planner may move this camera). `locationId` is where high/mid
29652
+ * currently write — the plan, which may disagree with the pin when Auto.
29653
+ */
29654
+ var RecordingDevicePlacementSchema = object({
29655
+ deviceId: number().int(),
29656
+ profile: string(),
29657
+ locationId: string()
29658
+ });
29659
+ var RecordingDevicePinSchema = object({
29660
+ deviceId: number().int(),
29661
+ /** Recordings-class location this camera is pinned to. */
29662
+ locationId: string()
29663
+ });
29664
+ var RecordingPlacementViewSchema = object({
29665
+ assignments: array(RecordingDevicePlacementSchema),
29666
+ pins: array(RecordingDevicePinSchema),
29667
+ defaultLocations: record(string(), string())
29668
+ });
29669
+ var RecordingSetDevicePlacementInputSchema = object({
29670
+ deviceId: number().int(),
29671
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
29672
+ locationId: string().nullable()
29673
+ });
29674
+ /**
29435
29675
  * Result of locating footage at a wall-clock instant for one device/profile.
29436
29676
  * `segment` carries the covering segment's window; `gap` reports the forward
29437
29677
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -29645,6 +29885,9 @@ method(object({
29645
29885
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29646
29886
  kind: "query",
29647
29887
  auth: "admin"
29888
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
29889
+ kind: "mutation",
29890
+ auth: "admin"
29648
29891
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29649
29892
  kind: "mutation",
29650
29893
  auth: "admin"
@@ -29654,6 +29897,12 @@ method(object({
29654
29897
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
29655
29898
  kind: "mutation",
29656
29899
  auth: "admin"
29900
+ }), method(object({}), RecordingPlacementViewSchema, {
29901
+ kind: "query",
29902
+ auth: "admin"
29903
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
29904
+ kind: "mutation",
29905
+ auth: "admin"
29657
29906
  });
29658
29907
  /**
29659
29908
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -30036,6 +30285,25 @@ var SceneMonitorStatusSchema = object({
30036
30285
  monitors: array(SceneMonitorSchema),
30037
30286
  lastFetchedAt: number()
30038
30287
  });
30288
+ /**
30289
+ * One camera's row in a `listScenesBatch` answer.
30290
+ *
30291
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
30292
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
30293
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
30294
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
30295
+ * configured. Fanned out per camera the difference was visible — one query
30296
+ * errored while the others resolved — and a batch that returned only the rows
30297
+ * it managed would have destroyed it, silently, by making an unreachable camera
30298
+ * indistinguishable from one that answered `monitors: []`.
30299
+ *
30300
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
30301
+ * could not be read"; `status.monitors: []` means "read, and it has none".
30302
+ */
30303
+ var SceneMonitorStatusForDeviceSchema = object({
30304
+ deviceId: number(),
30305
+ status: SceneMonitorStatusSchema.nullable()
30306
+ });
30039
30307
  var sceneMonitorCapability = {
30040
30308
  name: "scene-monitor",
30041
30309
  scope: "device",
@@ -30045,6 +30313,22 @@ var sceneMonitorCapability = {
30045
30313
  deviceTypes: [DeviceType.Camera],
30046
30314
  methods: {
30047
30315
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
30316
+ /**
30317
+ * The same answer, for a SET of cameras, in one round trip.
30318
+ *
30319
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
30320
+ * poll behind the push slice. Fanned out client-side that was one query
30321
+ * per camera — 29 round trips through the browser, the hub and the
30322
+ * post-analysis runner every 30 seconds to read an in-memory map the
30323
+ * owner had already merged. The work is unchanged (`statusFor` per
30324
+ * device, all in-process at the owner); what collapses is the transport.
30325
+ *
30326
+ * A camera that cannot answer still gets a row, with `status: null` —
30327
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
30328
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
30329
+ * tell which two are missing, or that any are.
30330
+ */
30331
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
30048
30332
  createScene: method(object({
30049
30333
  deviceId: number(),
30050
30334
  label: string(),
@@ -31880,6 +32164,27 @@ var CameraOccupancySnapshotSchema = object({
31880
32164
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
31881
32165
  });
31882
32166
  /**
32167
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
32168
+ *
32169
+ * THREE outcomes, and the single-camera method could only express two of them
32170
+ * because `snapshot: null` was already spoken for:
32171
+ *
32172
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
32173
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
32174
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
32175
+ * - `read: 'unreadable'` — the owner could not answer for this
32176
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
32177
+ *
32178
+ * Collapsing the last two is the failure this field exists to prevent: a
32179
+ * hydration that threw would otherwise render as an empty Stationary section,
32180
+ * which is a definite claim about a camera nobody could read.
32181
+ */
32182
+ var CameraOccupancySnapshotForDeviceSchema = object({
32183
+ deviceId: number(),
32184
+ read: _enum(["read", "unreadable"]),
32185
+ snapshot: CameraOccupancySnapshotSchema.nullable()
32186
+ });
32187
+ /**
31883
32188
  * Time-series resolution. The history methods return one bucket per
31884
32189
  * step over the requested range. Smaller resolutions cost more
31885
32190
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -31936,6 +32241,20 @@ var zoneAnalyticsCapability = {
31936
32241
  * (no inference result emitted since boot or since binding was
31937
32242
  * activated). */
31938
32243
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
32244
+ /**
32245
+ * The same snapshot, for a SET of cameras, in one round trip.
32246
+ *
32247
+ * The Events page's Stationary section polls this every 15s for every
32248
+ * selected camera. Fanned out client-side that is one query per camera to
32249
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
32250
+ * costs everything. Batched, N transports become one and the per-device
32251
+ * work is unchanged.
32252
+ *
32253
+ * Every requested deviceId gets a row, tagged `read` — see
32254
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
32255
+ * not answer for is `'unreadable'`, never an empty reading.
32256
+ */
32257
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
31939
32258
  /** Time-series object count inside one zone. `className` optional —
31940
32259
  * omit to count every class in the zone. */
31941
32260
  getZoneHistory: method(object({
@@ -34653,6 +34972,12 @@ Object.freeze({
34653
34972
  addonId: null,
34654
34973
  access: "delete"
34655
34974
  },
34975
+ "deviceManager.renameLocation": {
34976
+ capName: "device-manager",
34977
+ capScope: "system",
34978
+ addonId: null,
34979
+ access: "create"
34980
+ },
34656
34981
  "deviceManager.runDeviceAction": {
34657
34982
  capName: "device-manager",
34658
34983
  capScope: "system",
@@ -36345,13 +36670,19 @@ Object.freeze({
36345
36670
  addonId: null,
36346
36671
  access: "view"
36347
36672
  },
36348
- "pipelineAnalytics.getGroup": {
36673
+ "pipelineAnalytics.getKeyEvents": {
36349
36674
  capName: "pipeline-analytics",
36350
36675
  capScope: "device",
36351
36676
  addonId: null,
36352
36677
  access: "view"
36353
36678
  },
36354
- "pipelineAnalytics.getKeyEvents": {
36679
+ "pipelineAnalytics.getKeyEventsBatch": {
36680
+ capName: "pipeline-analytics",
36681
+ capScope: "device",
36682
+ addonId: null,
36683
+ access: "view"
36684
+ },
36685
+ "pipelineAnalytics.getMediaReclaimStatus": {
36355
36686
  capName: "pipeline-analytics",
36356
36687
  capScope: "device",
36357
36688
  addonId: null,
@@ -36441,12 +36772,6 @@ Object.freeze({
36441
36772
  addonId: null,
36442
36773
  access: "view"
36443
36774
  },
36444
- "pipelineAnalytics.listGroups": {
36445
- capName: "pipeline-analytics",
36446
- capScope: "device",
36447
- addonId: null,
36448
- access: "view"
36449
- },
36450
36775
  "pipelineAnalytics.listOpsLog": {
36451
36776
  capName: "pipeline-analytics",
36452
36777
  capScope: "device",
@@ -36531,6 +36856,12 @@ Object.freeze({
36531
36856
  addonId: null,
36532
36857
  access: "create"
36533
36858
  },
36859
+ "pipelineAnalytics.reclaimDebugMedia": {
36860
+ capName: "pipeline-analytics",
36861
+ capScope: "device",
36862
+ addonId: null,
36863
+ access: "create"
36864
+ },
36534
36865
  "pipelineAnalytics.reconcileFromDisk": {
36535
36866
  capName: "pipeline-analytics",
36536
36867
  capScope: "device",
@@ -37455,6 +37786,12 @@ Object.freeze({
37455
37786
  addonId: null,
37456
37787
  access: "view"
37457
37788
  },
37789
+ "recording.getPlacement": {
37790
+ capName: "recording",
37791
+ capScope: "system",
37792
+ addonId: null,
37793
+ access: "view"
37794
+ },
37458
37795
  "recording.getPlaybackManifest": {
37459
37796
  capName: "recording",
37460
37797
  capScope: "system",
@@ -37533,6 +37870,12 @@ Object.freeze({
37533
37870
  addonId: null,
37534
37871
  access: "view"
37535
37872
  },
37873
+ "recording.reconcileLedgerAgainstDisk": {
37874
+ capName: "recording",
37875
+ capScope: "system",
37876
+ addonId: null,
37877
+ access: "create"
37878
+ },
37536
37879
  "recording.refreshStorageLocationsForMigration": {
37537
37880
  capName: "recording",
37538
37881
  capScope: "system",
@@ -37575,6 +37918,12 @@ Object.freeze({
37575
37918
  addonId: null,
37576
37919
  access: "create"
37577
37920
  },
37921
+ "recording.setDevicePlacement": {
37922
+ capName: "recording",
37923
+ capScope: "system",
37924
+ addonId: null,
37925
+ access: "create"
37926
+ },
37578
37927
  "recording.startStorageMigrationMove": {
37579
37928
  capName: "recording",
37580
37929
  capScope: "system",
@@ -37659,6 +38008,12 @@ Object.freeze({
37659
38008
  addonId: null,
37660
38009
  access: "view"
37661
38010
  },
38011
+ "sceneMonitor.listScenesBatch": {
38012
+ capName: "scene-monitor",
38013
+ capScope: "device",
38014
+ addonId: null,
38015
+ access: "view"
38016
+ },
37662
38017
  "sceneMonitor.recheckNow": {
37663
38018
  capName: "scene-monitor",
37664
38019
  capScope: "device",
@@ -38013,12 +38368,36 @@ Object.freeze({
38013
38368
  addonId: null,
38014
38369
  access: "create"
38015
38370
  },
38371
+ "storageMigration.cleanupCancel": {
38372
+ capName: "storage-migration",
38373
+ capScope: "system",
38374
+ addonId: null,
38375
+ access: "create"
38376
+ },
38377
+ "storageMigration.cleanupStart": {
38378
+ capName: "storage-migration",
38379
+ capScope: "system",
38380
+ addonId: null,
38381
+ access: "create"
38382
+ },
38383
+ "storageMigration.cleanupStatus": {
38384
+ capName: "storage-migration",
38385
+ capScope: "system",
38386
+ addonId: null,
38387
+ access: "view"
38388
+ },
38016
38389
  "storageMigration.drain": {
38017
38390
  capName: "storage-migration",
38018
38391
  capScope: "system",
38019
38392
  addonId: null,
38020
38393
  access: "create"
38021
38394
  },
38395
+ "storageMigration.history": {
38396
+ capName: "storage-migration",
38397
+ capScope: "system",
38398
+ addonId: null,
38399
+ access: "view"
38400
+ },
38022
38401
  "storageMigration.movers": {
38023
38402
  capName: "storage-migration",
38024
38403
  capScope: "system",
@@ -39015,6 +39394,12 @@ Object.freeze({
39015
39394
  addonId: null,
39016
39395
  access: "view"
39017
39396
  },
39397
+ "zoneAnalytics.getCurrentSnapshotBatch": {
39398
+ capName: "zone-analytics",
39399
+ capScope: "device",
39400
+ addonId: null,
39401
+ access: "view"
39402
+ },
39018
39403
  "zoneAnalytics.getUnzonedHistory": {
39019
39404
  capName: "zone-analytics",
39020
39405
  capScope: "device",
@@ -40009,14 +40394,14 @@ Object.freeze({
40009
40394
  form: "single",
40010
40395
  optional: true
40011
40396
  }],
40012
- "pipelineAnalytics.getGroup": [{
40397
+ "pipelineAnalytics.getKeyEvents": [{
40013
40398
  name: "deviceId",
40014
40399
  form: "single",
40015
40400
  optional: false
40016
40401
  }],
40017
- "pipelineAnalytics.getKeyEvents": [{
40018
- name: "deviceId",
40019
- form: "single",
40402
+ "pipelineAnalytics.getKeyEventsBatch": [{
40403
+ name: "deviceIds",
40404
+ form: "array",
40020
40405
  optional: false
40021
40406
  }],
40022
40407
  "pipelineAnalytics.getMotionEvents": [{
@@ -40074,11 +40459,6 @@ Object.freeze({
40074
40459
  form: "single",
40075
40460
  optional: false
40076
40461
  }],
40077
- "pipelineAnalytics.listGroups": [{
40078
- name: "deviceIds",
40079
- form: "array",
40080
- optional: false
40081
- }],
40082
40462
  "pipelineAnalytics.listOpsLog": [{
40083
40463
  name: "deviceId",
40084
40464
  form: "single",
@@ -40124,6 +40504,11 @@ Object.freeze({
40124
40504
  form: "single",
40125
40505
  optional: true
40126
40506
  }],
40507
+ "pipelineAnalytics.reclaimDebugMedia": [{
40508
+ name: "deviceIds",
40509
+ form: "array",
40510
+ optional: true
40511
+ }],
40127
40512
  "pipelineAnalytics.reconcileFromDisk": [{
40128
40513
  name: "deviceId",
40129
40514
  form: "single",
@@ -40459,6 +40844,11 @@ Object.freeze({
40459
40844
  form: "single",
40460
40845
  optional: false
40461
40846
  }],
40847
+ "recording.reconcileLedgerAgainstDisk": [{
40848
+ name: "deviceId",
40849
+ form: "single",
40850
+ optional: true
40851
+ }],
40462
40852
  "recording.relocateFootage": [{
40463
40853
  name: "deviceId",
40464
40854
  form: "single",
@@ -40484,6 +40874,11 @@ Object.freeze({
40484
40874
  form: "single",
40485
40875
  optional: false
40486
40876
  }],
40877
+ "recording.setDevicePlacement": [{
40878
+ name: "deviceId",
40879
+ form: "single",
40880
+ optional: false
40881
+ }],
40487
40882
  "recording.startStorageMigrationMove": [{
40488
40883
  name: "deviceId",
40489
40884
  form: "single",
@@ -40524,6 +40919,11 @@ Object.freeze({
40524
40919
  form: "single",
40525
40920
  optional: false
40526
40921
  }],
40922
+ "sceneMonitor.listScenesBatch": [{
40923
+ name: "deviceIds",
40924
+ form: "array",
40925
+ optional: false
40926
+ }],
40527
40927
  "sceneMonitor.recheckNow": [{
40528
40928
  name: "deviceId",
40529
40929
  form: "single",
@@ -40785,6 +41185,11 @@ Object.freeze({
40785
41185
  form: "single",
40786
41186
  optional: false
40787
41187
  }],
41188
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
41189
+ name: "deviceIds",
41190
+ form: "array",
41191
+ optional: false
41192
+ }],
40788
41193
  "zoneAnalytics.getUnzonedHistory": [{
40789
41194
  name: "deviceId",
40790
41195
  form: "single",