@camstack/addon-auth 1.2.51 → 1.2.53

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.
@@ -8132,6 +8132,20 @@ var RelocateJobSchema = object({
8132
8132
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8133
8133
  */
8134
8134
  rowsReconciled: number().int().nonnegative().optional(),
8135
+ /**
8136
+ * Rows this run FORGOT because the file they name is not on disk.
8137
+ *
8138
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8139
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8140
+ * and the durable row is dropped through the same channel eviction uses. It
8141
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8142
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8143
+ * the same failure as one that quietly skips them (D295).
8144
+ *
8145
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8146
+ * ledger claimed 5.65 GB of footage that no longer existed.
8147
+ */
8148
+ rowsForgotten: number().int().nonnegative().optional(),
8135
8149
  startedAt: number(),
8136
8150
  finishedAt: number().nullable(),
8137
8151
  error: string().nullable()
@@ -8196,6 +8210,13 @@ var MediaRelocateModeSchema = _enum([
8196
8210
  ]);
8197
8211
  var RelocateMediaInputSchema = object({
8198
8212
  toLocationId: string(),
8213
+ /**
8214
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8215
+ * every row that is not already on `toLocationId` (the historical
8216
+ * behaviour). A named source is what a from→to migration needs: without it
8217
+ * "move events off disk 2" also emptied disk 1.
8218
+ */
8219
+ fromLocationId: string().optional(),
8199
8220
  throttleMbps: number().min(1).max(1e3).optional(),
8200
8221
  /** Omitted = `move`, the pre-existing behaviour. */
8201
8222
  mode: MediaRelocateModeSchema.optional()
@@ -8263,6 +8284,19 @@ var StorageMigrationDestinationsSchema = object({
8263
8284
  galleryMedia: string().min(1).optional()
8264
8285
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8265
8286
  /**
8287
+ * Optional named source per class. Omitted = the class's current default
8288
+ * (the historical behaviour). A named source that is NOT the default is a
8289
+ * drain of that disk: bytes move, the default stays, and the source is
8290
+ * disabled when the move finishes.
8291
+ */
8292
+ var StorageMigrationSourcesSchema = object({
8293
+ recordings: string().min(1).optional(),
8294
+ recordingsLow: string().min(1).optional(),
8295
+ eventMedia: string().min(1).optional(),
8296
+ backups: string().min(1).optional(),
8297
+ galleryMedia: string().min(1).optional()
8298
+ }).optional();
8299
+ /**
8266
8300
  * How a migration sequences the cutover against the byte move.
8267
8301
  *
8268
8302
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8284,6 +8318,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8284
8318
  /** Shared input for planning and starting an orchestrated storage migration. */
8285
8319
  var StorageMigrationInputSchema = object({
8286
8320
  destinations: StorageMigrationDestinationsSchema,
8321
+ /** Omitted = each class's current default. */
8322
+ sources: StorageMigrationSourcesSchema,
8287
8323
  throttleMbps: number().min(1).max(1e3).optional(),
8288
8324
  /** Omitted = `blocking`, which stays the default. */
8289
8325
  mode: StorageMigrationModeSchema.optional()
@@ -8363,6 +8399,13 @@ var StorageMigrationMoveSchema = object({
8363
8399
  storageClass: StorageMigrationClassSchema,
8364
8400
  fromLocationId: string(),
8365
8401
  toLocationId: string(),
8402
+ /**
8403
+ * True when `from` was NOT the class default at plan time. The move still
8404
+ * copies bytes, but the default is left alone and the source is disabled
8405
+ * once the copy verifies. Absent on jobs planned before this field existed
8406
+ * — those jobs always repointed, which is `false`.
8407
+ */
8408
+ freezeSource: boolean().optional(),
8366
8409
  moverJobId: string().nullable(),
8367
8410
  state: RelocateJobStateSchema.nullable(),
8368
8411
  error: string().nullable(),
@@ -8376,6 +8419,7 @@ var StorageMigrationJobSchema = object({
8376
8419
  * can tell a seconds-long cutover from a thirty-hour one. */
8377
8420
  mode: StorageMigrationModeSchema,
8378
8421
  destinations: StorageMigrationDestinationsSchema,
8422
+ sources: StorageMigrationSourcesSchema,
8379
8423
  throttleMbps: number(),
8380
8424
  moves: array(StorageMigrationMoveSchema),
8381
8425
  pauseLeaseId: string().nullable(),
@@ -8401,6 +8445,7 @@ var StorageMigrationFindingSchema = object({
8401
8445
  });
8402
8446
  var StorageMigrationPlanSchema = object({
8403
8447
  destinations: StorageMigrationDestinationsSchema,
8448
+ sources: StorageMigrationSourcesSchema,
8404
8449
  /** The mode this plan was built for. A plan is only valid for its mode: the
8405
8450
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8406
8451
  * it. */
@@ -8408,7 +8453,8 @@ var StorageMigrationPlanSchema = object({
8408
8453
  moves: array(object({
8409
8454
  storageClass: StorageMigrationClassSchema,
8410
8455
  fromLocationId: string(),
8411
- toLocationId: string()
8456
+ toLocationId: string(),
8457
+ freezeSource: boolean().optional()
8412
8458
  })),
8413
8459
  findings: array(StorageMigrationFindingSchema)
8414
8460
  });
@@ -8495,16 +8541,142 @@ var RelocateResidueSchema = object({
8495
8541
  segments: number().int().nonnegative(),
8496
8542
  bytes: number().int().nonnegative()
8497
8543
  }).nullable();
8544
+ /**
8545
+ * Ask one location whether its durable hour rows describe the disk — the walk
8546
+ * (D319).
8547
+ *
8548
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8549
+ * missing tool is the question, and the dry run is how they sanity-check the
8550
+ * destructive run before authorising it.
8551
+ */
8552
+ var LedgerWalkInputSchema = object({
8553
+ locationId: string().min(1),
8554
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8555
+ apply: boolean().optional(),
8556
+ /** Narrow to one camera. */
8557
+ deviceId: number().int().positive().optional(),
8558
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8559
+ profiles: array(string().min(1)).optional()
8560
+ });
8561
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8562
+ var LedgerWalkRefusalSchema = _enum([
8563
+ "location-unknown",
8564
+ "source-writable",
8565
+ "no-ledger",
8566
+ "archive-unreadable",
8567
+ "anchor-absent",
8568
+ "anchor-unreadable",
8569
+ "anchor-moved"
8570
+ ]);
8571
+ _enum([
8572
+ "live-tail",
8573
+ "listing-error",
8574
+ "path-mismatch",
8575
+ "durable-refused"
8576
+ ]);
8577
+ /** Every skip reason, always present, always a number — so a reason that never
8578
+ * fired reports as zero rather than absent and the report shape is constant
8579
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8580
+ var LedgerWalkSkipCountsSchema = object({
8581
+ "live-tail": number().int().nonnegative(),
8582
+ "listing-error": number().int().nonnegative(),
8583
+ "path-mismatch": number().int().nonnegative(),
8584
+ "durable-refused": number().int().nonnegative()
8585
+ });
8586
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8587
+ var LedgerWalkDeviceReportSchema = object({
8588
+ deviceId: number().int(),
8589
+ hoursWalked: number().int().nonnegative(),
8590
+ hoursMissing: number().int().nonnegative(),
8591
+ ghostSegments: number().int().nonnegative(),
8592
+ ghostBytes: number().int().nonnegative(),
8593
+ forgottenSegments: number().int().nonnegative(),
8594
+ orphanFiles: number().int().nonnegative()
8595
+ });
8596
+ /**
8597
+ * What one walk claimed, listed, found and (only when armed) forgot.
8598
+ *
8599
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8600
+ * walk that saw a fraction of the location is visible in its own report rather
8601
+ * than in the absence of one.
8602
+ */
8603
+ var LedgerWalkReportSchema = object({
8604
+ locationId: string(),
8605
+ applied: boolean(),
8606
+ refused: LedgerWalkRefusalSchema.nullable(),
8607
+ archiveSegments: number().int().nonnegative().nullable(),
8608
+ archiveBytes: number().int().nonnegative().nullable(),
8609
+ hoursClaimed: number().int().nonnegative(),
8610
+ hoursWalked: number().int().nonnegative(),
8611
+ hoursMissing: number().int().nonnegative(),
8612
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8613
+ listings: number().int().nonnegative(),
8614
+ segmentsClaimed: number().int().nonnegative(),
8615
+ ghostSegments: number().int().nonnegative(),
8616
+ ghostBytes: number().int().nonnegative(),
8617
+ ghostHoursWhole: number().int().nonnegative(),
8618
+ forgottenSegments: number().int().nonnegative(),
8619
+ forgottenBytes: number().int().nonnegative(),
8620
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8621
+ orphanFiles: number().int().nonnegative(),
8622
+ orphanSample: array(string()).readonly(),
8623
+ hoursSkipped: number().int().nonnegative(),
8624
+ skippedByReason: LedgerWalkSkipCountsSchema,
8625
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8626
+ bounded: boolean(),
8627
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8628
+ });
8498
8629
  /** How many rows a media pass would still act on against a given target — the
8499
8630
  * media lane's denominator AND its residue, from ONE derivation so the two can
8500
8631
  * never disagree. `null` = the count could not be taken. */
8501
8632
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8502
8633
  var RelocatableMediaCountInputSchema = object({
8503
8634
  toLocationId: string().min(1),
8635
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8636
+ fromLocationId: string().optional(),
8504
8637
  /** Omitted = `move`. */
8505
8638
  mode: MediaRelocateModeSchema.optional()
8506
8639
  });
8507
8640
  /**
8641
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8642
+ * ghost ledger entries on frozen footage locations.
8643
+ *
8644
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8645
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8646
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8647
+ * with no operator-visible status.
8648
+ */
8649
+ var StorageCleanupPhaseSchema = _enum([
8650
+ "orphans",
8651
+ "debug-media",
8652
+ "ghost-ledger",
8653
+ "done",
8654
+ "failed",
8655
+ "cancelled"
8656
+ ]);
8657
+ var StorageCleanupInputSchema = object({
8658
+ /** Also walk motion stills / track filmstrips. Off by default. */
8659
+ includeDebugMedia: boolean().optional() });
8660
+ var StorageCleanupJobSchema = object({
8661
+ jobId: string(),
8662
+ phase: StorageCleanupPhaseSchema,
8663
+ includeDebugMedia: boolean(),
8664
+ orphansReclaimed: number().int().nonnegative(),
8665
+ orphanBytesReclaimed: number().int().nonnegative(),
8666
+ debugMediaReclaimed: number().int().nonnegative(),
8667
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8668
+ ghostsForgotten: number().int().nonnegative(),
8669
+ ghostBytesForgotten: number().int().nonnegative(),
8670
+ /** Short operator-facing line: current collection, pass, or location. */
8671
+ detail: string().nullable(),
8672
+ cancelRequested: boolean(),
8673
+ startedAt: number(),
8674
+ updatedAt: number(),
8675
+ finishedAt: number().nullable(),
8676
+ error: string().nullable()
8677
+ });
8678
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8679
+ /**
8508
8680
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8509
8681
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8510
8682
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8532,11 +8704,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8532
8704
  * The default location for a type uses `id === <type>:default` by
8533
8705
  * convention (the bare type ref like `'backups'` resolves to it).
8534
8706
  *
8535
- * `isSystem: true` marks a location as orchestrator-seeded and
8536
- * undeletable. The bootstrap-installed defaults (one per type) carry
8537
- * this flag; operator-added locations don't. Editing the config of
8538
- * a system location is allowed (path migration, provider swap) but
8539
- * deleting it is rejected at the cap level.
8707
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8708
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8709
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8710
+ * / last-enabled, not on this bit.
8540
8711
  */
8541
8712
  var StorageLocationSchema = object({
8542
8713
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -18827,13 +18998,15 @@ var ListGroupsPageSchema = object({
18827
18998
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18828
18999
  nextCursor: string().nullable()
18829
19000
  });
19001
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19002
+ var KEY_EVENTS_MAX_LIMIT = 200;
18830
19003
  var KeyEventQueryInput = object({
18831
19004
  deviceId: number(),
18832
19005
  /** Window lower bound (track firstSeen ≥ since). */
18833
19006
  since: number(),
18834
19007
  /** Window upper bound (track firstSeen ≤ until). */
18835
19008
  until: number(),
18836
- limit: number().int().min(1).max(200).default(50),
19009
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18837
19010
  /** Drop tracks scoring below this importance. */
18838
19011
  minImportance: number().min(0).max(1).optional(),
18839
19012
  /** Restrict to a single class (e.g. 'person'). */
@@ -18855,6 +19028,32 @@ var KeyEventSchema = object({
18855
19028
  ...TrackFlagFields,
18856
19029
  ...TrackRetrainFields
18857
19030
  });
19031
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19032
+ var KeyEventBatchQueryInput = object({
19033
+ deviceIds: array(number()).min(1).max(200),
19034
+ since: number(),
19035
+ until: number(),
19036
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19037
+ * across the set, which would let a busy camera starve a quiet one of its
19038
+ * rows and change what the merged feed contains. */
19039
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19040
+ minImportance: number().min(0).max(1).optional(),
19041
+ classFilter: string().optional()
19042
+ });
19043
+ /**
19044
+ * One camera's key events in a batch answer.
19045
+ *
19046
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19047
+ * error rather than throwing, so a camera whose store read failed and one with
19048
+ * no events in the window were ALREADY indistinguishable per camera — the
19049
+ * batch does not make that worse, and the row keeps the deviceId the single
19050
+ * method's output never carried (the caller used to stamp it from the fan-out
19051
+ * key, which only worked because there was one query per camera).
19052
+ */
19053
+ var KeyEventsForDeviceSchema = object({
19054
+ deviceId: number(),
19055
+ events: array(KeyEventSchema).readonly()
19056
+ });
18858
19057
  object({
18859
19058
  trackId: string(),
18860
19059
  className: string(),
@@ -19074,6 +19273,47 @@ var RebuildStatusSchema = object({
19074
19273
  /** Present when the pass ended by throwing. */
19075
19274
  error: string().nullable()
19076
19275
  });
19276
+ /**
19277
+ * Acknowledgement that a debug-media reclaim STARTED.
19278
+ *
19279
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19280
+ * it runs detached and this returns immediately. Awaiting it is how the
19281
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19282
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19283
+ */
19284
+ var MediaReclaimStartResultSchema = object({
19285
+ started: boolean(),
19286
+ /** True when a pass was already running; the new request is ignored. */
19287
+ alreadyRunning: boolean()
19288
+ });
19289
+ var MediaReclaimInputSchema = object({
19290
+ mode: _enum(["report", "reclaim"]).default("report"),
19291
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19292
+ deviceIds: array(number().int()).min(1).optional(),
19293
+ restart: boolean().optional(),
19294
+ pageSize: number().int().min(50).max(5e3).optional(),
19295
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19296
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19297
+ maxBytesPerRun: number().int().min(1).optional(),
19298
+ budgetMinutes: number().int().min(1).max(720).optional(),
19299
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19300
+ graceMinutes: number().int().min(1).max(10080).optional()
19301
+ });
19302
+ var MediaReclaimStatusSchema = object({
19303
+ running: boolean(),
19304
+ mode: _enum(["report", "reclaim"]).nullable(),
19305
+ totalExamined: number(),
19306
+ totalEligible: number(),
19307
+ totalReclaimed: number(),
19308
+ totalBytesReclaimed: number(),
19309
+ totalRefused: number(),
19310
+ /** Device+scope windows finished in this pass. */
19311
+ devicesDone: number(),
19312
+ complete: boolean().nullable(),
19313
+ startedAtMs: number().nullable(),
19314
+ finishedAtMs: number().nullable(),
19315
+ error: string().nullable()
19316
+ });
19077
19317
  var ReplayFrameInputSchema = object({
19078
19318
  timestamp: number(),
19079
19319
  frame: PipelineRunResultBridge
@@ -19124,7 +19364,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19124
19364
  until: number().optional(),
19125
19365
  kinds: array(string()).optional(),
19126
19366
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19127
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19367
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19128
19368
  deviceId: number(),
19129
19369
  since: number(),
19130
19370
  until: number(),
@@ -19215,6 +19455,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19215
19455
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19216
19456
  kind: "query",
19217
19457
  auth: "admin"
19458
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19459
+ kind: "mutation",
19460
+ auth: "admin"
19461
+ }), method(object({}), MediaReclaimStatusSchema, {
19462
+ kind: "query",
19463
+ auth: "admin"
19218
19464
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19219
19465
  kind: "query",
19220
19466
  auth: "admin"
@@ -21185,7 +21431,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21185
21431
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21186
21432
  kind: "mutation",
21187
21433
  auth: "admin"
21188
- });
21434
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21435
+ kind: "mutation",
21436
+ auth: "admin"
21437
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21438
+ kind: "mutation",
21439
+ auth: "admin"
21440
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21189
21441
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21190
21442
  providerId: string().min(1),
21191
21443
  displayName: string().min(1),
@@ -26598,6 +26850,33 @@ var RecordingRebalanceInputSchema = object({
26598
26850
  minMoveGb: number().min(0).optional()
26599
26851
  });
26600
26852
  /**
26853
+ * Operator-facing placement of one camera onto a recordings location.
26854
+ *
26855
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26856
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26857
+ * currently write — the plan, which may disagree with the pin when Auto.
26858
+ */
26859
+ var RecordingDevicePlacementSchema = object({
26860
+ deviceId: number().int(),
26861
+ profile: string(),
26862
+ locationId: string()
26863
+ });
26864
+ var RecordingDevicePinSchema = object({
26865
+ deviceId: number().int(),
26866
+ /** Recordings-class location this camera is pinned to. */
26867
+ locationId: string()
26868
+ });
26869
+ var RecordingPlacementViewSchema = object({
26870
+ assignments: array(RecordingDevicePlacementSchema),
26871
+ pins: array(RecordingDevicePinSchema),
26872
+ defaultLocations: record(string(), string())
26873
+ });
26874
+ var RecordingSetDevicePlacementInputSchema = object({
26875
+ deviceId: number().int(),
26876
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26877
+ locationId: string().nullable()
26878
+ });
26879
+ /**
26601
26880
  * Result of locating footage at a wall-clock instant for one device/profile.
26602
26881
  * `segment` carries the covering segment's window; `gap` reports the forward
26603
26882
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26811,6 +27090,9 @@ method(object({
26811
27090
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26812
27091
  kind: "query",
26813
27092
  auth: "admin"
27093
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27094
+ kind: "mutation",
27095
+ auth: "admin"
26814
27096
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26815
27097
  kind: "mutation",
26816
27098
  auth: "admin"
@@ -26820,6 +27102,12 @@ method(object({
26820
27102
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26821
27103
  kind: "mutation",
26822
27104
  auth: "admin"
27105
+ }), method(object({}), RecordingPlacementViewSchema, {
27106
+ kind: "query",
27107
+ auth: "admin"
27108
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27109
+ kind: "mutation",
27110
+ auth: "admin"
26823
27111
  });
26824
27112
  /**
26825
27113
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27202,7 +27490,26 @@ var SceneMonitorStatusSchema = object({
27202
27490
  monitors: array(SceneMonitorSchema),
27203
27491
  lastFetchedAt: number()
27204
27492
  });
27205
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27493
+ /**
27494
+ * One camera's row in a `listScenesBatch` answer.
27495
+ *
27496
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27497
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27498
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27499
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27500
+ * configured. Fanned out per camera the difference was visible — one query
27501
+ * errored while the others resolved — and a batch that returned only the rows
27502
+ * it managed would have destroyed it, silently, by making an unreachable camera
27503
+ * indistinguishable from one that answered `monitors: []`.
27504
+ *
27505
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27506
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27507
+ */
27508
+ var SceneMonitorStatusForDeviceSchema = object({
27509
+ deviceId: number(),
27510
+ status: SceneMonitorStatusSchema.nullable()
27511
+ });
27512
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27206
27513
  deviceId: number(),
27207
27514
  label: string(),
27208
27515
  roi: MaskRectShapeSchema,
@@ -28526,6 +28833,27 @@ var CameraOccupancySnapshotSchema = object({
28526
28833
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28527
28834
  });
28528
28835
  /**
28836
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28837
+ *
28838
+ * THREE outcomes, and the single-camera method could only express two of them
28839
+ * because `snapshot: null` was already spoken for:
28840
+ *
28841
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28842
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28843
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28844
+ * - `read: 'unreadable'` — the owner could not answer for this
28845
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28846
+ *
28847
+ * Collapsing the last two is the failure this field exists to prevent: a
28848
+ * hydration that threw would otherwise render as an empty Stationary section,
28849
+ * which is a definite claim about a camera nobody could read.
28850
+ */
28851
+ var CameraOccupancySnapshotForDeviceSchema = object({
28852
+ deviceId: number(),
28853
+ read: _enum(["read", "unreadable"]),
28854
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28855
+ });
28856
+ /**
28529
28857
  * Time-series resolution. The history methods return one bucket per
28530
28858
  * step over the requested range. Smaller resolutions cost more
28531
28859
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28549,7 +28877,7 @@ var HistoryPointSchema = object({
28549
28877
  /** Object count averaged over the bucket (rounded to nearest integer). */
28550
28878
  count: number().int().nonnegative()
28551
28879
  });
28552
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28880
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28553
28881
  deviceId: number(),
28554
28882
  zoneId: string(),
28555
28883
  className: string().optional()
@@ -31854,6 +32182,18 @@ Object.freeze({
31854
32182
  addonId: null,
31855
32183
  access: "view"
31856
32184
  },
32185
+ "pipelineAnalytics.getKeyEventsBatch": {
32186
+ capName: "pipeline-analytics",
32187
+ capScope: "device",
32188
+ addonId: null,
32189
+ access: "view"
32190
+ },
32191
+ "pipelineAnalytics.getMediaReclaimStatus": {
32192
+ capName: "pipeline-analytics",
32193
+ capScope: "device",
32194
+ addonId: null,
32195
+ access: "view"
32196
+ },
31857
32197
  "pipelineAnalytics.getMotionEvents": {
31858
32198
  capName: "pipeline-analytics",
31859
32199
  capScope: "device",
@@ -32028,6 +32368,12 @@ Object.freeze({
32028
32368
  addonId: null,
32029
32369
  access: "create"
32030
32370
  },
32371
+ "pipelineAnalytics.reclaimDebugMedia": {
32372
+ capName: "pipeline-analytics",
32373
+ capScope: "device",
32374
+ addonId: null,
32375
+ access: "create"
32376
+ },
32031
32377
  "pipelineAnalytics.reconcileFromDisk": {
32032
32378
  capName: "pipeline-analytics",
32033
32379
  capScope: "device",
@@ -32952,6 +33298,12 @@ Object.freeze({
32952
33298
  addonId: null,
32953
33299
  access: "view"
32954
33300
  },
33301
+ "recording.getPlacement": {
33302
+ capName: "recording",
33303
+ capScope: "system",
33304
+ addonId: null,
33305
+ access: "view"
33306
+ },
32955
33307
  "recording.getPlaybackManifest": {
32956
33308
  capName: "recording",
32957
33309
  capScope: "system",
@@ -33030,6 +33382,12 @@ Object.freeze({
33030
33382
  addonId: null,
33031
33383
  access: "view"
33032
33384
  },
33385
+ "recording.reconcileLedgerAgainstDisk": {
33386
+ capName: "recording",
33387
+ capScope: "system",
33388
+ addonId: null,
33389
+ access: "create"
33390
+ },
33033
33391
  "recording.refreshStorageLocationsForMigration": {
33034
33392
  capName: "recording",
33035
33393
  capScope: "system",
@@ -33072,6 +33430,12 @@ Object.freeze({
33072
33430
  addonId: null,
33073
33431
  access: "create"
33074
33432
  },
33433
+ "recording.setDevicePlacement": {
33434
+ capName: "recording",
33435
+ capScope: "system",
33436
+ addonId: null,
33437
+ access: "create"
33438
+ },
33075
33439
  "recording.startStorageMigrationMove": {
33076
33440
  capName: "recording",
33077
33441
  capScope: "system",
@@ -33156,6 +33520,12 @@ Object.freeze({
33156
33520
  addonId: null,
33157
33521
  access: "view"
33158
33522
  },
33523
+ "sceneMonitor.listScenesBatch": {
33524
+ capName: "scene-monitor",
33525
+ capScope: "device",
33526
+ addonId: null,
33527
+ access: "view"
33528
+ },
33159
33529
  "sceneMonitor.recheckNow": {
33160
33530
  capName: "scene-monitor",
33161
33531
  capScope: "device",
@@ -33510,12 +33880,36 @@ Object.freeze({
33510
33880
  addonId: null,
33511
33881
  access: "create"
33512
33882
  },
33883
+ "storageMigration.cleanupCancel": {
33884
+ capName: "storage-migration",
33885
+ capScope: "system",
33886
+ addonId: null,
33887
+ access: "create"
33888
+ },
33889
+ "storageMigration.cleanupStart": {
33890
+ capName: "storage-migration",
33891
+ capScope: "system",
33892
+ addonId: null,
33893
+ access: "create"
33894
+ },
33895
+ "storageMigration.cleanupStatus": {
33896
+ capName: "storage-migration",
33897
+ capScope: "system",
33898
+ addonId: null,
33899
+ access: "view"
33900
+ },
33513
33901
  "storageMigration.drain": {
33514
33902
  capName: "storage-migration",
33515
33903
  capScope: "system",
33516
33904
  addonId: null,
33517
33905
  access: "create"
33518
33906
  },
33907
+ "storageMigration.history": {
33908
+ capName: "storage-migration",
33909
+ capScope: "system",
33910
+ addonId: null,
33911
+ access: "view"
33912
+ },
33519
33913
  "storageMigration.movers": {
33520
33914
  capName: "storage-migration",
33521
33915
  capScope: "system",
@@ -34512,6 +34906,12 @@ Object.freeze({
34512
34906
  addonId: null,
34513
34907
  access: "view"
34514
34908
  },
34909
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34910
+ capName: "zone-analytics",
34911
+ capScope: "device",
34912
+ addonId: null,
34913
+ access: "view"
34914
+ },
34515
34915
  "zoneAnalytics.getUnzonedHistory": {
34516
34916
  capName: "zone-analytics",
34517
34917
  capScope: "device",
@@ -35516,6 +35916,11 @@ Object.freeze({
35516
35916
  form: "single",
35517
35917
  optional: false
35518
35918
  }],
35919
+ "pipelineAnalytics.getKeyEventsBatch": [{
35920
+ name: "deviceIds",
35921
+ form: "array",
35922
+ optional: false
35923
+ }],
35519
35924
  "pipelineAnalytics.getMotionEvents": [{
35520
35925
  name: "deviceId",
35521
35926
  form: "single",
@@ -35621,6 +36026,11 @@ Object.freeze({
35621
36026
  form: "single",
35622
36027
  optional: true
35623
36028
  }],
36029
+ "pipelineAnalytics.reclaimDebugMedia": [{
36030
+ name: "deviceIds",
36031
+ form: "array",
36032
+ optional: true
36033
+ }],
35624
36034
  "pipelineAnalytics.reconcileFromDisk": [{
35625
36035
  name: "deviceId",
35626
36036
  form: "single",
@@ -35956,6 +36366,11 @@ Object.freeze({
35956
36366
  form: "single",
35957
36367
  optional: false
35958
36368
  }],
36369
+ "recording.reconcileLedgerAgainstDisk": [{
36370
+ name: "deviceId",
36371
+ form: "single",
36372
+ optional: true
36373
+ }],
35959
36374
  "recording.relocateFootage": [{
35960
36375
  name: "deviceId",
35961
36376
  form: "single",
@@ -35981,6 +36396,11 @@ Object.freeze({
35981
36396
  form: "single",
35982
36397
  optional: false
35983
36398
  }],
36399
+ "recording.setDevicePlacement": [{
36400
+ name: "deviceId",
36401
+ form: "single",
36402
+ optional: false
36403
+ }],
35984
36404
  "recording.startStorageMigrationMove": [{
35985
36405
  name: "deviceId",
35986
36406
  form: "single",
@@ -36021,6 +36441,11 @@ Object.freeze({
36021
36441
  form: "single",
36022
36442
  optional: false
36023
36443
  }],
36444
+ "sceneMonitor.listScenesBatch": [{
36445
+ name: "deviceIds",
36446
+ form: "array",
36447
+ optional: false
36448
+ }],
36024
36449
  "sceneMonitor.recheckNow": [{
36025
36450
  name: "deviceId",
36026
36451
  form: "single",
@@ -36282,6 +36707,11 @@ Object.freeze({
36282
36707
  form: "single",
36283
36708
  optional: false
36284
36709
  }],
36710
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36711
+ name: "deviceIds",
36712
+ form: "array",
36713
+ optional: false
36714
+ }],
36285
36715
  "zoneAnalytics.getUnzonedHistory": [{
36286
36716
  name: "deviceId",
36287
36717
  form: "single",