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