@camstack/addon-provider-petkit 0.2.48 → 0.2.50

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
@@ -9120,6 +9120,20 @@ var RelocateJobSchema = object({
9120
9120
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
9121
9121
  */
9122
9122
  rowsReconciled: number().int().nonnegative().optional(),
9123
+ /**
9124
+ * Rows this run FORGOT because the file they name is not on disk.
9125
+ *
9126
+ * The mover derived the path from the row's own fields and `stat`ed it; an
9127
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
9128
+ * and the durable row is dropped through the same channel eviction uses. It
9129
+ * is reported for the same reason `rowsReconciled` is: this is a durable
9130
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
9131
+ * the same failure as one that quietly skips them (D295).
9132
+ *
9133
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
9134
+ * ledger claimed 5.65 GB of footage that no longer existed.
9135
+ */
9136
+ rowsForgotten: number().int().nonnegative().optional(),
9123
9137
  startedAt: number(),
9124
9138
  finishedAt: number().nullable(),
9125
9139
  error: string().nullable()
@@ -9184,6 +9198,13 @@ var MediaRelocateModeSchema = _enum([
9184
9198
  ]);
9185
9199
  var RelocateMediaInputSchema = object({
9186
9200
  toLocationId: string(),
9201
+ /**
9202
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
9203
+ * every row that is not already on `toLocationId` (the historical
9204
+ * behaviour). A named source is what a from→to migration needs: without it
9205
+ * "move events off disk 2" also emptied disk 1.
9206
+ */
9207
+ fromLocationId: string().optional(),
9187
9208
  throttleMbps: number().min(1).max(1e3).optional(),
9188
9209
  /** Omitted = `move`, the pre-existing behaviour. */
9189
9210
  mode: MediaRelocateModeSchema.optional()
@@ -9251,6 +9272,19 @@ var StorageMigrationDestinationsSchema = object({
9251
9272
  galleryMedia: string().min(1).optional()
9252
9273
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
9253
9274
  /**
9275
+ * Optional named source per class. Omitted = the class's current default
9276
+ * (the historical behaviour). A named source that is NOT the default is a
9277
+ * drain of that disk: bytes move, the default stays, and the source is
9278
+ * disabled when the move finishes.
9279
+ */
9280
+ var StorageMigrationSourcesSchema = object({
9281
+ recordings: string().min(1).optional(),
9282
+ recordingsLow: string().min(1).optional(),
9283
+ eventMedia: string().min(1).optional(),
9284
+ backups: string().min(1).optional(),
9285
+ galleryMedia: string().min(1).optional()
9286
+ }).optional();
9287
+ /**
9254
9288
  * How a migration sequences the cutover against the byte move.
9255
9289
  *
9256
9290
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -9272,6 +9306,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
9272
9306
  /** Shared input for planning and starting an orchestrated storage migration. */
9273
9307
  var StorageMigrationInputSchema = object({
9274
9308
  destinations: StorageMigrationDestinationsSchema,
9309
+ /** Omitted = each class's current default. */
9310
+ sources: StorageMigrationSourcesSchema,
9275
9311
  throttleMbps: number().min(1).max(1e3).optional(),
9276
9312
  /** Omitted = `blocking`, which stays the default. */
9277
9313
  mode: StorageMigrationModeSchema.optional()
@@ -9351,6 +9387,13 @@ var StorageMigrationMoveSchema = object({
9351
9387
  storageClass: StorageMigrationClassSchema,
9352
9388
  fromLocationId: string(),
9353
9389
  toLocationId: string(),
9390
+ /**
9391
+ * True when `from` was NOT the class default at plan time. The move still
9392
+ * copies bytes, but the default is left alone and the source is disabled
9393
+ * once the copy verifies. Absent on jobs planned before this field existed
9394
+ * — those jobs always repointed, which is `false`.
9395
+ */
9396
+ freezeSource: boolean().optional(),
9354
9397
  moverJobId: string().nullable(),
9355
9398
  state: RelocateJobStateSchema.nullable(),
9356
9399
  error: string().nullable(),
@@ -9364,6 +9407,7 @@ var StorageMigrationJobSchema = object({
9364
9407
  * can tell a seconds-long cutover from a thirty-hour one. */
9365
9408
  mode: StorageMigrationModeSchema,
9366
9409
  destinations: StorageMigrationDestinationsSchema,
9410
+ sources: StorageMigrationSourcesSchema,
9367
9411
  throttleMbps: number(),
9368
9412
  moves: array(StorageMigrationMoveSchema),
9369
9413
  pauseLeaseId: string().nullable(),
@@ -9389,6 +9433,7 @@ var StorageMigrationFindingSchema = object({
9389
9433
  });
9390
9434
  var StorageMigrationPlanSchema = object({
9391
9435
  destinations: StorageMigrationDestinationsSchema,
9436
+ sources: StorageMigrationSourcesSchema,
9392
9437
  /** The mode this plan was built for. A plan is only valid for its mode: the
9393
9438
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
9394
9439
  * it. */
@@ -9396,7 +9441,8 @@ var StorageMigrationPlanSchema = object({
9396
9441
  moves: array(object({
9397
9442
  storageClass: StorageMigrationClassSchema,
9398
9443
  fromLocationId: string(),
9399
- toLocationId: string()
9444
+ toLocationId: string(),
9445
+ freezeSource: boolean().optional()
9400
9446
  })),
9401
9447
  findings: array(StorageMigrationFindingSchema)
9402
9448
  });
@@ -9483,16 +9529,142 @@ var RelocateResidueSchema = object({
9483
9529
  segments: number().int().nonnegative(),
9484
9530
  bytes: number().int().nonnegative()
9485
9531
  }).nullable();
9532
+ /**
9533
+ * Ask one location whether its durable hour rows describe the disk — the walk
9534
+ * (D319).
9535
+ *
9536
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
9537
+ * missing tool is the question, and the dry run is how they sanity-check the
9538
+ * destructive run before authorising it.
9539
+ */
9540
+ var LedgerWalkInputSchema = object({
9541
+ locationId: string().min(1),
9542
+ /** Forget the confirmed-absent rows, rather than only counting them. */
9543
+ apply: boolean().optional(),
9544
+ /** Narrow to one camera. */
9545
+ deviceId: number().int().positive().optional(),
9546
+ /** Narrow to these recording profiles; empty/absent = every profile. */
9547
+ profiles: array(string().min(1)).optional()
9548
+ });
9549
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
9550
+ var LedgerWalkRefusalSchema = _enum([
9551
+ "location-unknown",
9552
+ "source-writable",
9553
+ "no-ledger",
9554
+ "archive-unreadable",
9555
+ "anchor-absent",
9556
+ "anchor-unreadable",
9557
+ "anchor-moved"
9558
+ ]);
9559
+ _enum([
9560
+ "live-tail",
9561
+ "listing-error",
9562
+ "path-mismatch",
9563
+ "durable-refused"
9564
+ ]);
9565
+ /** Every skip reason, always present, always a number — so a reason that never
9566
+ * fired reports as zero rather than absent and the report shape is constant
9567
+ * between passes. Spelled out rather than `z.record` for exactly that. */
9568
+ var LedgerWalkSkipCountsSchema = object({
9569
+ "live-tail": number().int().nonnegative(),
9570
+ "listing-error": number().int().nonnegative(),
9571
+ "path-mismatch": number().int().nonnegative(),
9572
+ "durable-refused": number().int().nonnegative()
9573
+ });
9574
+ /** One camera's share of a walk, so a report names cameras and not rows. */
9575
+ var LedgerWalkDeviceReportSchema = object({
9576
+ deviceId: number().int(),
9577
+ hoursWalked: number().int().nonnegative(),
9578
+ hoursMissing: number().int().nonnegative(),
9579
+ ghostSegments: number().int().nonnegative(),
9580
+ ghostBytes: number().int().nonnegative(),
9581
+ forgottenSegments: number().int().nonnegative(),
9582
+ orphanFiles: number().int().nonnegative()
9583
+ });
9584
+ /**
9585
+ * What one walk claimed, listed, found and (only when armed) forgot.
9586
+ *
9587
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
9588
+ * walk that saw a fraction of the location is visible in its own report rather
9589
+ * than in the absence of one.
9590
+ */
9591
+ var LedgerWalkReportSchema = object({
9592
+ locationId: string(),
9593
+ applied: boolean(),
9594
+ refused: LedgerWalkRefusalSchema.nullable(),
9595
+ archiveSegments: number().int().nonnegative().nullable(),
9596
+ archiveBytes: number().int().nonnegative().nullable(),
9597
+ hoursClaimed: number().int().nonnegative(),
9598
+ hoursWalked: number().int().nonnegative(),
9599
+ hoursMissing: number().int().nonnegative(),
9600
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
9601
+ listings: number().int().nonnegative(),
9602
+ segmentsClaimed: number().int().nonnegative(),
9603
+ ghostSegments: number().int().nonnegative(),
9604
+ ghostBytes: number().int().nonnegative(),
9605
+ ghostHoursWhole: number().int().nonnegative(),
9606
+ forgottenSegments: number().int().nonnegative(),
9607
+ forgottenBytes: number().int().nonnegative(),
9608
+ /** Files under a claimed hour that no durable row names. Never deleted. */
9609
+ orphanFiles: number().int().nonnegative(),
9610
+ orphanSample: array(string()).readonly(),
9611
+ hoursSkipped: number().int().nonnegative(),
9612
+ skippedByReason: LedgerWalkSkipCountsSchema,
9613
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
9614
+ bounded: boolean(),
9615
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
9616
+ });
9486
9617
  /** How many rows a media pass would still act on against a given target — the
9487
9618
  * media lane's denominator AND its residue, from ONE derivation so the two can
9488
9619
  * never disagree. `null` = the count could not be taken. */
9489
9620
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9490
9621
  var RelocatableMediaCountInputSchema = object({
9491
9622
  toLocationId: string().min(1),
9623
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
9624
+ fromLocationId: string().optional(),
9492
9625
  /** Omitted = `move`. */
9493
9626
  mode: MediaRelocateModeSchema.optional()
9494
9627
  });
9495
9628
  /**
9629
+ * Operator cleanup of leftover analytics rows, optional debug media, and
9630
+ * ghost ledger entries on frozen footage locations.
9631
+ *
9632
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
9633
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
9634
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
9635
+ * with no operator-visible status.
9636
+ */
9637
+ var StorageCleanupPhaseSchema = _enum([
9638
+ "orphans",
9639
+ "debug-media",
9640
+ "ghost-ledger",
9641
+ "done",
9642
+ "failed",
9643
+ "cancelled"
9644
+ ]);
9645
+ var StorageCleanupInputSchema = object({
9646
+ /** Also walk motion stills / track filmstrips. Off by default. */
9647
+ includeDebugMedia: boolean().optional() });
9648
+ var StorageCleanupJobSchema = object({
9649
+ jobId: string(),
9650
+ phase: StorageCleanupPhaseSchema,
9651
+ includeDebugMedia: boolean(),
9652
+ orphansReclaimed: number().int().nonnegative(),
9653
+ orphanBytesReclaimed: number().int().nonnegative(),
9654
+ debugMediaReclaimed: number().int().nonnegative(),
9655
+ debugMediaBytesReclaimed: number().int().nonnegative(),
9656
+ ghostsForgotten: number().int().nonnegative(),
9657
+ ghostBytesForgotten: number().int().nonnegative(),
9658
+ /** Short operator-facing line: current collection, pass, or location. */
9659
+ detail: string().nullable(),
9660
+ cancelRequested: boolean(),
9661
+ startedAt: number(),
9662
+ updatedAt: number(),
9663
+ finishedAt: number().nullable(),
9664
+ error: string().nullable()
9665
+ });
9666
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
9667
+ /**
9496
9668
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9497
9669
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9498
9670
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9520,11 +9692,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
9520
9692
  * The default location for a type uses `id === <type>:default` by
9521
9693
  * convention (the bare type ref like `'backups'` resolves to it).
9522
9694
  *
9523
- * `isSystem: true` marks a location as orchestrator-seeded and
9524
- * undeletable. The bootstrap-installed defaults (one per type) carry
9525
- * this flag; operator-added locations don't. Editing the config of
9526
- * a system location is allowed (path migration, provider swap) but
9527
- * deleting it is rejected at the cap level.
9695
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
9696
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
9697
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
9698
+ * / last-enabled, not on this bit.
9528
9699
  */
9529
9700
  var StorageLocationSchema = object({
9530
9701
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -14067,6 +14238,12 @@ method(object({
14067
14238
  }), _void(), {
14068
14239
  kind: "mutation",
14069
14240
  auth: "admin"
14241
+ }), method(object({
14242
+ from: string(),
14243
+ to: string()
14244
+ }), object({ moved: number() }), {
14245
+ kind: "mutation",
14246
+ auth: "admin"
14070
14247
  }), method(object({
14071
14248
  deviceId: number(),
14072
14249
  disabled: boolean()
@@ -20075,53 +20252,15 @@ var RecentTracksPageSchema = object({
20075
20252
  /** Cursor for the next page, or null when this page is the last. */
20076
20253
  nextCursor: string().nullable()
20077
20254
  });
20078
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
20079
- var LIST_GROUPS_MAX_LIMIT = 100;
20080
- var AnalyticsGroupRecordSchema = object({
20081
- id: string(),
20082
- deviceId: number().int(),
20083
- openedAt: number().int(),
20084
- closedAt: number().int(),
20085
- timestamp: number().int(),
20086
- memberCount: number().int(),
20087
- memberTrackIds: array(string()).readonly(),
20088
- className: string(),
20089
- classes: array(string()).readonly(),
20090
- /** Relative event-media path, or null when the group has no picture yet. */
20091
- mediaUrl: string().nullable(),
20092
- singleton: boolean()
20093
- });
20094
- var AnalyticsGroupMemberSchema = object({
20095
- trackId: string(),
20096
- deviceId: number().int(),
20097
- className: string(),
20098
- firstSeen: number().int(),
20099
- lastSeen: number().int(),
20100
- mediaUrl: string().nullable()
20101
- });
20102
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
20103
- var ListGroupsQueryInput = object({
20104
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
20105
- deviceIds: array(number()),
20106
- /** Window lower bound on `closedAt` (inclusive). */
20107
- since: number().optional(),
20108
- /** Window upper bound on `openedAt` (inclusive). */
20109
- until: number().optional(),
20110
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
20111
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
20112
- cursor: string().optional()
20113
- });
20114
- var ListGroupsPageSchema = object({
20115
- groups: array(AnalyticsGroupRecordSchema).readonly(),
20116
- nextCursor: string().nullable()
20117
- });
20255
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
20256
+ var KEY_EVENTS_MAX_LIMIT = 200;
20118
20257
  var KeyEventQueryInput = object({
20119
20258
  deviceId: number(),
20120
20259
  /** Window lower bound (track firstSeen ≥ since). */
20121
20260
  since: number(),
20122
20261
  /** Window upper bound (track firstSeen ≤ until). */
20123
20262
  until: number(),
20124
- limit: number().int().min(1).max(200).default(50),
20263
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
20125
20264
  /** Drop tracks scoring below this importance. */
20126
20265
  minImportance: number().min(0).max(1).optional(),
20127
20266
  /** Restrict to a single class (e.g. 'person'). */
@@ -20143,6 +20282,32 @@ var KeyEventSchema = object({
20143
20282
  ...TrackFlagFields,
20144
20283
  ...TrackRetrainFields
20145
20284
  });
20285
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
20286
+ var KeyEventBatchQueryInput = object({
20287
+ deviceIds: array(number()).min(1).max(200),
20288
+ since: number(),
20289
+ until: number(),
20290
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
20291
+ * across the set, which would let a busy camera starve a quiet one of its
20292
+ * rows and change what the merged feed contains. */
20293
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
20294
+ minImportance: number().min(0).max(1).optional(),
20295
+ classFilter: string().optional()
20296
+ });
20297
+ /**
20298
+ * One camera's key events in a batch answer.
20299
+ *
20300
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
20301
+ * error rather than throwing, so a camera whose store read failed and one with
20302
+ * no events in the window were ALREADY indistinguishable per camera — the
20303
+ * batch does not make that worse, and the row keeps the deviceId the single
20304
+ * method's output never carried (the caller used to stamp it from the fan-out
20305
+ * key, which only worked because there was one query per camera).
20306
+ */
20307
+ var KeyEventsForDeviceSchema = object({
20308
+ deviceId: number(),
20309
+ events: array(KeyEventSchema).readonly()
20310
+ });
20146
20311
  object({
20147
20312
  trackId: string(),
20148
20313
  className: string(),
@@ -20190,9 +20355,7 @@ var TrackCascadeCountsSchema = object({
20190
20355
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
20191
20356
  plates: number().int(),
20192
20357
  /** Per-track CLIP search vectors removed (best-effort). */
20193
- embeddings: number().int(),
20194
- /** Group membership + group rows removed with their last member (best-effort). */
20195
- groups: number().int()
20358
+ embeddings: number().int()
20196
20359
  });
20197
20360
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
20198
20361
  var DiskReconcileCountsSchema = object({
@@ -20362,6 +20525,47 @@ var RebuildStatusSchema = object({
20362
20525
  /** Present when the pass ended by throwing. */
20363
20526
  error: string().nullable()
20364
20527
  });
20528
+ /**
20529
+ * Acknowledgement that a debug-media reclaim STARTED.
20530
+ *
20531
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
20532
+ * it runs detached and this returns immediately. Awaiting it is how the
20533
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
20534
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
20535
+ */
20536
+ var MediaReclaimStartResultSchema = object({
20537
+ started: boolean(),
20538
+ /** True when a pass was already running; the new request is ignored. */
20539
+ alreadyRunning: boolean()
20540
+ });
20541
+ var MediaReclaimInputSchema = object({
20542
+ mode: _enum(["report", "reclaim"]).default("report"),
20543
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
20544
+ deviceIds: array(number().int()).min(1).optional(),
20545
+ restart: boolean().optional(),
20546
+ pageSize: number().int().min(50).max(5e3).optional(),
20547
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
20548
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
20549
+ maxBytesPerRun: number().int().min(1).optional(),
20550
+ budgetMinutes: number().int().min(1).max(720).optional(),
20551
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
20552
+ graceMinutes: number().int().min(1).max(10080).optional()
20553
+ });
20554
+ var MediaReclaimStatusSchema = object({
20555
+ running: boolean(),
20556
+ mode: _enum(["report", "reclaim"]).nullable(),
20557
+ totalExamined: number(),
20558
+ totalEligible: number(),
20559
+ totalReclaimed: number(),
20560
+ totalBytesReclaimed: number(),
20561
+ totalRefused: number(),
20562
+ /** Device+scope windows finished in this pass. */
20563
+ devicesDone: number(),
20564
+ complete: boolean().nullable(),
20565
+ startedAtMs: number().nullable(),
20566
+ finishedAtMs: number().nullable(),
20567
+ error: string().nullable()
20568
+ });
20365
20569
  var ReplayFrameInputSchema = object({
20366
20570
  timestamp: number(),
20367
20571
  frame: PipelineRunResultBridge
@@ -20400,10 +20604,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20400
20604
  * stationary registry). Default false: the timeline lists passages,
20401
20605
  * not parking records (operator decision, 2026-08-15). */
20402
20606
  includeStationary: boolean().optional()
20403
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
20404
- deviceId: number(),
20405
- groupId: string().min(1)
20406
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20607
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
20407
20608
  kind: "mutation",
20408
20609
  auth: "admin"
20409
20610
  }), 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({
@@ -20412,7 +20613,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20412
20613
  until: number().optional(),
20413
20614
  kinds: array(string()).optional(),
20414
20615
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20415
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
20616
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20416
20617
  deviceId: number(),
20417
20618
  since: number(),
20418
20619
  until: number(),
@@ -20503,6 +20704,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20503
20704
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20504
20705
  kind: "query",
20505
20706
  auth: "admin"
20707
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
20708
+ kind: "mutation",
20709
+ auth: "admin"
20710
+ }), method(object({}), MediaReclaimStatusSchema, {
20711
+ kind: "query",
20712
+ auth: "admin"
20506
20713
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
20507
20714
  kind: "query",
20508
20715
  auth: "admin"
@@ -22473,7 +22680,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
22473
22680
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
22474
22681
  kind: "mutation",
22475
22682
  auth: "admin"
22476
- });
22683
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
22684
+ kind: "mutation",
22685
+ auth: "admin"
22686
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22687
+ kind: "mutation",
22688
+ auth: "admin"
22689
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
22477
22690
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
22478
22691
  providerId: string().min(1),
22479
22692
  displayName: string().min(1),
@@ -29574,6 +29787,33 @@ var RecordingRebalanceInputSchema = object({
29574
29787
  minMoveGb: number().min(0).optional()
29575
29788
  });
29576
29789
  /**
29790
+ * Operator-facing placement of one camera onto a recordings location.
29791
+ *
29792
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
29793
+ * Auto (the planner may move this camera). `locationId` is where high/mid
29794
+ * currently write — the plan, which may disagree with the pin when Auto.
29795
+ */
29796
+ var RecordingDevicePlacementSchema = object({
29797
+ deviceId: number().int(),
29798
+ profile: string(),
29799
+ locationId: string()
29800
+ });
29801
+ var RecordingDevicePinSchema = object({
29802
+ deviceId: number().int(),
29803
+ /** Recordings-class location this camera is pinned to. */
29804
+ locationId: string()
29805
+ });
29806
+ var RecordingPlacementViewSchema = object({
29807
+ assignments: array(RecordingDevicePlacementSchema),
29808
+ pins: array(RecordingDevicePinSchema),
29809
+ defaultLocations: record(string(), string())
29810
+ });
29811
+ var RecordingSetDevicePlacementInputSchema = object({
29812
+ deviceId: number().int(),
29813
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
29814
+ locationId: string().nullable()
29815
+ });
29816
+ /**
29577
29817
  * Result of locating footage at a wall-clock instant for one device/profile.
29578
29818
  * `segment` carries the covering segment's window; `gap` reports the forward
29579
29819
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -29787,6 +30027,9 @@ method(object({
29787
30027
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29788
30028
  kind: "query",
29789
30029
  auth: "admin"
30030
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
30031
+ kind: "mutation",
30032
+ auth: "admin"
29790
30033
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29791
30034
  kind: "mutation",
29792
30035
  auth: "admin"
@@ -29796,6 +30039,12 @@ method(object({
29796
30039
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
29797
30040
  kind: "mutation",
29798
30041
  auth: "admin"
30042
+ }), method(object({}), RecordingPlacementViewSchema, {
30043
+ kind: "query",
30044
+ auth: "admin"
30045
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
30046
+ kind: "mutation",
30047
+ auth: "admin"
29799
30048
  });
29800
30049
  /**
29801
30050
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -30178,6 +30427,25 @@ var SceneMonitorStatusSchema = object({
30178
30427
  monitors: array(SceneMonitorSchema),
30179
30428
  lastFetchedAt: number()
30180
30429
  });
30430
+ /**
30431
+ * One camera's row in a `listScenesBatch` answer.
30432
+ *
30433
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
30434
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
30435
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
30436
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
30437
+ * configured. Fanned out per camera the difference was visible — one query
30438
+ * errored while the others resolved — and a batch that returned only the rows
30439
+ * it managed would have destroyed it, silently, by making an unreachable camera
30440
+ * indistinguishable from one that answered `monitors: []`.
30441
+ *
30442
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
30443
+ * could not be read"; `status.monitors: []` means "read, and it has none".
30444
+ */
30445
+ var SceneMonitorStatusForDeviceSchema = object({
30446
+ deviceId: number(),
30447
+ status: SceneMonitorStatusSchema.nullable()
30448
+ });
30181
30449
  var sceneMonitorCapability = {
30182
30450
  name: "scene-monitor",
30183
30451
  scope: "device",
@@ -30187,6 +30455,22 @@ var sceneMonitorCapability = {
30187
30455
  deviceTypes: [DeviceType.Camera],
30188
30456
  methods: {
30189
30457
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
30458
+ /**
30459
+ * The same answer, for a SET of cameras, in one round trip.
30460
+ *
30461
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
30462
+ * poll behind the push slice. Fanned out client-side that was one query
30463
+ * per camera — 29 round trips through the browser, the hub and the
30464
+ * post-analysis runner every 30 seconds to read an in-memory map the
30465
+ * owner had already merged. The work is unchanged (`statusFor` per
30466
+ * device, all in-process at the owner); what collapses is the transport.
30467
+ *
30468
+ * A camera that cannot answer still gets a row, with `status: null` —
30469
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
30470
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
30471
+ * tell which two are missing, or that any are.
30472
+ */
30473
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
30190
30474
  createScene: method(object({
30191
30475
  deviceId: number(),
30192
30476
  label: string(),
@@ -32022,6 +32306,27 @@ var CameraOccupancySnapshotSchema = object({
32022
32306
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
32023
32307
  });
32024
32308
  /**
32309
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
32310
+ *
32311
+ * THREE outcomes, and the single-camera method could only express two of them
32312
+ * because `snapshot: null` was already spoken for:
32313
+ *
32314
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
32315
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
32316
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
32317
+ * - `read: 'unreadable'` — the owner could not answer for this
32318
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
32319
+ *
32320
+ * Collapsing the last two is the failure this field exists to prevent: a
32321
+ * hydration that threw would otherwise render as an empty Stationary section,
32322
+ * which is a definite claim about a camera nobody could read.
32323
+ */
32324
+ var CameraOccupancySnapshotForDeviceSchema = object({
32325
+ deviceId: number(),
32326
+ read: _enum(["read", "unreadable"]),
32327
+ snapshot: CameraOccupancySnapshotSchema.nullable()
32328
+ });
32329
+ /**
32025
32330
  * Time-series resolution. The history methods return one bucket per
32026
32331
  * step over the requested range. Smaller resolutions cost more
32027
32332
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -32078,6 +32383,20 @@ var zoneAnalyticsCapability = {
32078
32383
  * (no inference result emitted since boot or since binding was
32079
32384
  * activated). */
32080
32385
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
32386
+ /**
32387
+ * The same snapshot, for a SET of cameras, in one round trip.
32388
+ *
32389
+ * The Events page's Stationary section polls this every 15s for every
32390
+ * selected camera. Fanned out client-side that is one query per camera to
32391
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
32392
+ * costs everything. Batched, N transports become one and the per-device
32393
+ * work is unchanged.
32394
+ *
32395
+ * Every requested deviceId gets a row, tagged `read` — see
32396
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
32397
+ * not answer for is `'unreadable'`, never an empty reading.
32398
+ */
32399
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
32081
32400
  /** Time-series object count inside one zone. `className` optional —
32082
32401
  * omit to count every class in the zone. */
32083
32402
  getZoneHistory: method(object({
@@ -34795,6 +35114,12 @@ Object.freeze({
34795
35114
  addonId: null,
34796
35115
  access: "delete"
34797
35116
  },
35117
+ "deviceManager.renameLocation": {
35118
+ capName: "device-manager",
35119
+ capScope: "system",
35120
+ addonId: null,
35121
+ access: "create"
35122
+ },
34798
35123
  "deviceManager.runDeviceAction": {
34799
35124
  capName: "device-manager",
34800
35125
  capScope: "system",
@@ -36487,13 +36812,19 @@ Object.freeze({
36487
36812
  addonId: null,
36488
36813
  access: "view"
36489
36814
  },
36490
- "pipelineAnalytics.getGroup": {
36815
+ "pipelineAnalytics.getKeyEvents": {
36491
36816
  capName: "pipeline-analytics",
36492
36817
  capScope: "device",
36493
36818
  addonId: null,
36494
36819
  access: "view"
36495
36820
  },
36496
- "pipelineAnalytics.getKeyEvents": {
36821
+ "pipelineAnalytics.getKeyEventsBatch": {
36822
+ capName: "pipeline-analytics",
36823
+ capScope: "device",
36824
+ addonId: null,
36825
+ access: "view"
36826
+ },
36827
+ "pipelineAnalytics.getMediaReclaimStatus": {
36497
36828
  capName: "pipeline-analytics",
36498
36829
  capScope: "device",
36499
36830
  addonId: null,
@@ -36583,12 +36914,6 @@ Object.freeze({
36583
36914
  addonId: null,
36584
36915
  access: "view"
36585
36916
  },
36586
- "pipelineAnalytics.listGroups": {
36587
- capName: "pipeline-analytics",
36588
- capScope: "device",
36589
- addonId: null,
36590
- access: "view"
36591
- },
36592
36917
  "pipelineAnalytics.listOpsLog": {
36593
36918
  capName: "pipeline-analytics",
36594
36919
  capScope: "device",
@@ -36673,6 +36998,12 @@ Object.freeze({
36673
36998
  addonId: null,
36674
36999
  access: "create"
36675
37000
  },
37001
+ "pipelineAnalytics.reclaimDebugMedia": {
37002
+ capName: "pipeline-analytics",
37003
+ capScope: "device",
37004
+ addonId: null,
37005
+ access: "create"
37006
+ },
36676
37007
  "pipelineAnalytics.reconcileFromDisk": {
36677
37008
  capName: "pipeline-analytics",
36678
37009
  capScope: "device",
@@ -37597,6 +37928,12 @@ Object.freeze({
37597
37928
  addonId: null,
37598
37929
  access: "view"
37599
37930
  },
37931
+ "recording.getPlacement": {
37932
+ capName: "recording",
37933
+ capScope: "system",
37934
+ addonId: null,
37935
+ access: "view"
37936
+ },
37600
37937
  "recording.getPlaybackManifest": {
37601
37938
  capName: "recording",
37602
37939
  capScope: "system",
@@ -37675,6 +38012,12 @@ Object.freeze({
37675
38012
  addonId: null,
37676
38013
  access: "view"
37677
38014
  },
38015
+ "recording.reconcileLedgerAgainstDisk": {
38016
+ capName: "recording",
38017
+ capScope: "system",
38018
+ addonId: null,
38019
+ access: "create"
38020
+ },
37678
38021
  "recording.refreshStorageLocationsForMigration": {
37679
38022
  capName: "recording",
37680
38023
  capScope: "system",
@@ -37717,6 +38060,12 @@ Object.freeze({
37717
38060
  addonId: null,
37718
38061
  access: "create"
37719
38062
  },
38063
+ "recording.setDevicePlacement": {
38064
+ capName: "recording",
38065
+ capScope: "system",
38066
+ addonId: null,
38067
+ access: "create"
38068
+ },
37720
38069
  "recording.startStorageMigrationMove": {
37721
38070
  capName: "recording",
37722
38071
  capScope: "system",
@@ -37801,6 +38150,12 @@ Object.freeze({
37801
38150
  addonId: null,
37802
38151
  access: "view"
37803
38152
  },
38153
+ "sceneMonitor.listScenesBatch": {
38154
+ capName: "scene-monitor",
38155
+ capScope: "device",
38156
+ addonId: null,
38157
+ access: "view"
38158
+ },
37804
38159
  "sceneMonitor.recheckNow": {
37805
38160
  capName: "scene-monitor",
37806
38161
  capScope: "device",
@@ -38155,12 +38510,36 @@ Object.freeze({
38155
38510
  addonId: null,
38156
38511
  access: "create"
38157
38512
  },
38513
+ "storageMigration.cleanupCancel": {
38514
+ capName: "storage-migration",
38515
+ capScope: "system",
38516
+ addonId: null,
38517
+ access: "create"
38518
+ },
38519
+ "storageMigration.cleanupStart": {
38520
+ capName: "storage-migration",
38521
+ capScope: "system",
38522
+ addonId: null,
38523
+ access: "create"
38524
+ },
38525
+ "storageMigration.cleanupStatus": {
38526
+ capName: "storage-migration",
38527
+ capScope: "system",
38528
+ addonId: null,
38529
+ access: "view"
38530
+ },
38158
38531
  "storageMigration.drain": {
38159
38532
  capName: "storage-migration",
38160
38533
  capScope: "system",
38161
38534
  addonId: null,
38162
38535
  access: "create"
38163
38536
  },
38537
+ "storageMigration.history": {
38538
+ capName: "storage-migration",
38539
+ capScope: "system",
38540
+ addonId: null,
38541
+ access: "view"
38542
+ },
38164
38543
  "storageMigration.movers": {
38165
38544
  capName: "storage-migration",
38166
38545
  capScope: "system",
@@ -39157,6 +39536,12 @@ Object.freeze({
39157
39536
  addonId: null,
39158
39537
  access: "view"
39159
39538
  },
39539
+ "zoneAnalytics.getCurrentSnapshotBatch": {
39540
+ capName: "zone-analytics",
39541
+ capScope: "device",
39542
+ addonId: null,
39543
+ access: "view"
39544
+ },
39160
39545
  "zoneAnalytics.getUnzonedHistory": {
39161
39546
  capName: "zone-analytics",
39162
39547
  capScope: "device",
@@ -40151,14 +40536,14 @@ Object.freeze({
40151
40536
  form: "single",
40152
40537
  optional: true
40153
40538
  }],
40154
- "pipelineAnalytics.getGroup": [{
40539
+ "pipelineAnalytics.getKeyEvents": [{
40155
40540
  name: "deviceId",
40156
40541
  form: "single",
40157
40542
  optional: false
40158
40543
  }],
40159
- "pipelineAnalytics.getKeyEvents": [{
40160
- name: "deviceId",
40161
- form: "single",
40544
+ "pipelineAnalytics.getKeyEventsBatch": [{
40545
+ name: "deviceIds",
40546
+ form: "array",
40162
40547
  optional: false
40163
40548
  }],
40164
40549
  "pipelineAnalytics.getMotionEvents": [{
@@ -40216,11 +40601,6 @@ Object.freeze({
40216
40601
  form: "single",
40217
40602
  optional: false
40218
40603
  }],
40219
- "pipelineAnalytics.listGroups": [{
40220
- name: "deviceIds",
40221
- form: "array",
40222
- optional: false
40223
- }],
40224
40604
  "pipelineAnalytics.listOpsLog": [{
40225
40605
  name: "deviceId",
40226
40606
  form: "single",
@@ -40266,6 +40646,11 @@ Object.freeze({
40266
40646
  form: "single",
40267
40647
  optional: true
40268
40648
  }],
40649
+ "pipelineAnalytics.reclaimDebugMedia": [{
40650
+ name: "deviceIds",
40651
+ form: "array",
40652
+ optional: true
40653
+ }],
40269
40654
  "pipelineAnalytics.reconcileFromDisk": [{
40270
40655
  name: "deviceId",
40271
40656
  form: "single",
@@ -40601,6 +40986,11 @@ Object.freeze({
40601
40986
  form: "single",
40602
40987
  optional: false
40603
40988
  }],
40989
+ "recording.reconcileLedgerAgainstDisk": [{
40990
+ name: "deviceId",
40991
+ form: "single",
40992
+ optional: true
40993
+ }],
40604
40994
  "recording.relocateFootage": [{
40605
40995
  name: "deviceId",
40606
40996
  form: "single",
@@ -40626,6 +41016,11 @@ Object.freeze({
40626
41016
  form: "single",
40627
41017
  optional: false
40628
41018
  }],
41019
+ "recording.setDevicePlacement": [{
41020
+ name: "deviceId",
41021
+ form: "single",
41022
+ optional: false
41023
+ }],
40629
41024
  "recording.startStorageMigrationMove": [{
40630
41025
  name: "deviceId",
40631
41026
  form: "single",
@@ -40666,6 +41061,11 @@ Object.freeze({
40666
41061
  form: "single",
40667
41062
  optional: false
40668
41063
  }],
41064
+ "sceneMonitor.listScenesBatch": [{
41065
+ name: "deviceIds",
41066
+ form: "array",
41067
+ optional: false
41068
+ }],
40669
41069
  "sceneMonitor.recheckNow": [{
40670
41070
  name: "deviceId",
40671
41071
  form: "single",
@@ -40927,6 +41327,11 @@ Object.freeze({
40927
41327
  form: "single",
40928
41328
  optional: false
40929
41329
  }],
41330
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
41331
+ name: "deviceIds",
41332
+ form: "array",
41333
+ optional: false
41334
+ }],
40930
41335
  "zoneAnalytics.getUnzonedHistory": [{
40931
41336
  name: "deviceId",
40932
41337
  form: "single",