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