@camstack/addon-provider-reolink 1.2.71 → 1.2.73

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
@@ -8308,6 +8308,20 @@ var RelocateJobSchema = object({
8308
8308
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8309
8309
  */
8310
8310
  rowsReconciled: number().int().nonnegative().optional(),
8311
+ /**
8312
+ * Rows this run FORGOT because the file they name is not on disk.
8313
+ *
8314
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8315
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8316
+ * and the durable row is dropped through the same channel eviction uses. It
8317
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8318
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8319
+ * the same failure as one that quietly skips them (D295).
8320
+ *
8321
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8322
+ * ledger claimed 5.65 GB of footage that no longer existed.
8323
+ */
8324
+ rowsForgotten: number().int().nonnegative().optional(),
8311
8325
  startedAt: number(),
8312
8326
  finishedAt: number().nullable(),
8313
8327
  error: string().nullable()
@@ -8372,6 +8386,13 @@ var MediaRelocateModeSchema = _enum([
8372
8386
  ]);
8373
8387
  var RelocateMediaInputSchema = object({
8374
8388
  toLocationId: string(),
8389
+ /**
8390
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8391
+ * every row that is not already on `toLocationId` (the historical
8392
+ * behaviour). A named source is what a from→to migration needs: without it
8393
+ * "move events off disk 2" also emptied disk 1.
8394
+ */
8395
+ fromLocationId: string().optional(),
8375
8396
  throttleMbps: number().min(1).max(1e3).optional(),
8376
8397
  /** Omitted = `move`, the pre-existing behaviour. */
8377
8398
  mode: MediaRelocateModeSchema.optional()
@@ -8439,6 +8460,19 @@ var StorageMigrationDestinationsSchema = object({
8439
8460
  galleryMedia: string().min(1).optional()
8440
8461
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8441
8462
  /**
8463
+ * Optional named source per class. Omitted = the class's current default
8464
+ * (the historical behaviour). A named source that is NOT the default is a
8465
+ * drain of that disk: bytes move, the default stays, and the source is
8466
+ * disabled when the move finishes.
8467
+ */
8468
+ var StorageMigrationSourcesSchema = object({
8469
+ recordings: string().min(1).optional(),
8470
+ recordingsLow: string().min(1).optional(),
8471
+ eventMedia: string().min(1).optional(),
8472
+ backups: string().min(1).optional(),
8473
+ galleryMedia: string().min(1).optional()
8474
+ }).optional();
8475
+ /**
8442
8476
  * How a migration sequences the cutover against the byte move.
8443
8477
  *
8444
8478
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8460,6 +8494,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8460
8494
  /** Shared input for planning and starting an orchestrated storage migration. */
8461
8495
  var StorageMigrationInputSchema = object({
8462
8496
  destinations: StorageMigrationDestinationsSchema,
8497
+ /** Omitted = each class's current default. */
8498
+ sources: StorageMigrationSourcesSchema,
8463
8499
  throttleMbps: number().min(1).max(1e3).optional(),
8464
8500
  /** Omitted = `blocking`, which stays the default. */
8465
8501
  mode: StorageMigrationModeSchema.optional()
@@ -8539,6 +8575,13 @@ var StorageMigrationMoveSchema = object({
8539
8575
  storageClass: StorageMigrationClassSchema,
8540
8576
  fromLocationId: string(),
8541
8577
  toLocationId: string(),
8578
+ /**
8579
+ * True when `from` was NOT the class default at plan time. The move still
8580
+ * copies bytes, but the default is left alone and the source is disabled
8581
+ * once the copy verifies. Absent on jobs planned before this field existed
8582
+ * — those jobs always repointed, which is `false`.
8583
+ */
8584
+ freezeSource: boolean().optional(),
8542
8585
  moverJobId: string().nullable(),
8543
8586
  state: RelocateJobStateSchema.nullable(),
8544
8587
  error: string().nullable(),
@@ -8552,6 +8595,7 @@ var StorageMigrationJobSchema = object({
8552
8595
  * can tell a seconds-long cutover from a thirty-hour one. */
8553
8596
  mode: StorageMigrationModeSchema,
8554
8597
  destinations: StorageMigrationDestinationsSchema,
8598
+ sources: StorageMigrationSourcesSchema,
8555
8599
  throttleMbps: number(),
8556
8600
  moves: array(StorageMigrationMoveSchema),
8557
8601
  pauseLeaseId: string().nullable(),
@@ -8577,6 +8621,7 @@ var StorageMigrationFindingSchema = object({
8577
8621
  });
8578
8622
  var StorageMigrationPlanSchema = object({
8579
8623
  destinations: StorageMigrationDestinationsSchema,
8624
+ sources: StorageMigrationSourcesSchema,
8580
8625
  /** The mode this plan was built for. A plan is only valid for its mode: the
8581
8626
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8582
8627
  * it. */
@@ -8584,7 +8629,8 @@ var StorageMigrationPlanSchema = object({
8584
8629
  moves: array(object({
8585
8630
  storageClass: StorageMigrationClassSchema,
8586
8631
  fromLocationId: string(),
8587
- toLocationId: string()
8632
+ toLocationId: string(),
8633
+ freezeSource: boolean().optional()
8588
8634
  })),
8589
8635
  findings: array(StorageMigrationFindingSchema)
8590
8636
  });
@@ -8671,16 +8717,142 @@ var RelocateResidueSchema = object({
8671
8717
  segments: number().int().nonnegative(),
8672
8718
  bytes: number().int().nonnegative()
8673
8719
  }).nullable();
8720
+ /**
8721
+ * Ask one location whether its durable hour rows describe the disk — the walk
8722
+ * (D319).
8723
+ *
8724
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8725
+ * missing tool is the question, and the dry run is how they sanity-check the
8726
+ * destructive run before authorising it.
8727
+ */
8728
+ var LedgerWalkInputSchema = object({
8729
+ locationId: string().min(1),
8730
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8731
+ apply: boolean().optional(),
8732
+ /** Narrow to one camera. */
8733
+ deviceId: number().int().positive().optional(),
8734
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8735
+ profiles: array(string().min(1)).optional()
8736
+ });
8737
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8738
+ var LedgerWalkRefusalSchema = _enum([
8739
+ "location-unknown",
8740
+ "source-writable",
8741
+ "no-ledger",
8742
+ "archive-unreadable",
8743
+ "anchor-absent",
8744
+ "anchor-unreadable",
8745
+ "anchor-moved"
8746
+ ]);
8747
+ _enum([
8748
+ "live-tail",
8749
+ "listing-error",
8750
+ "path-mismatch",
8751
+ "durable-refused"
8752
+ ]);
8753
+ /** Every skip reason, always present, always a number — so a reason that never
8754
+ * fired reports as zero rather than absent and the report shape is constant
8755
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8756
+ var LedgerWalkSkipCountsSchema = object({
8757
+ "live-tail": number().int().nonnegative(),
8758
+ "listing-error": number().int().nonnegative(),
8759
+ "path-mismatch": number().int().nonnegative(),
8760
+ "durable-refused": number().int().nonnegative()
8761
+ });
8762
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8763
+ var LedgerWalkDeviceReportSchema = object({
8764
+ deviceId: number().int(),
8765
+ hoursWalked: number().int().nonnegative(),
8766
+ hoursMissing: number().int().nonnegative(),
8767
+ ghostSegments: number().int().nonnegative(),
8768
+ ghostBytes: number().int().nonnegative(),
8769
+ forgottenSegments: number().int().nonnegative(),
8770
+ orphanFiles: number().int().nonnegative()
8771
+ });
8772
+ /**
8773
+ * What one walk claimed, listed, found and (only when armed) forgot.
8774
+ *
8775
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8776
+ * walk that saw a fraction of the location is visible in its own report rather
8777
+ * than in the absence of one.
8778
+ */
8779
+ var LedgerWalkReportSchema = object({
8780
+ locationId: string(),
8781
+ applied: boolean(),
8782
+ refused: LedgerWalkRefusalSchema.nullable(),
8783
+ archiveSegments: number().int().nonnegative().nullable(),
8784
+ archiveBytes: number().int().nonnegative().nullable(),
8785
+ hoursClaimed: number().int().nonnegative(),
8786
+ hoursWalked: number().int().nonnegative(),
8787
+ hoursMissing: number().int().nonnegative(),
8788
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8789
+ listings: number().int().nonnegative(),
8790
+ segmentsClaimed: number().int().nonnegative(),
8791
+ ghostSegments: number().int().nonnegative(),
8792
+ ghostBytes: number().int().nonnegative(),
8793
+ ghostHoursWhole: number().int().nonnegative(),
8794
+ forgottenSegments: number().int().nonnegative(),
8795
+ forgottenBytes: number().int().nonnegative(),
8796
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8797
+ orphanFiles: number().int().nonnegative(),
8798
+ orphanSample: array(string()).readonly(),
8799
+ hoursSkipped: number().int().nonnegative(),
8800
+ skippedByReason: LedgerWalkSkipCountsSchema,
8801
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8802
+ bounded: boolean(),
8803
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8804
+ });
8674
8805
  /** How many rows a media pass would still act on against a given target — the
8675
8806
  * media lane's denominator AND its residue, from ONE derivation so the two can
8676
8807
  * never disagree. `null` = the count could not be taken. */
8677
8808
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8678
8809
  var RelocatableMediaCountInputSchema = object({
8679
8810
  toLocationId: string().min(1),
8811
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8812
+ fromLocationId: string().optional(),
8680
8813
  /** Omitted = `move`. */
8681
8814
  mode: MediaRelocateModeSchema.optional()
8682
8815
  });
8683
8816
  /**
8817
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8818
+ * ghost ledger entries on frozen footage locations.
8819
+ *
8820
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8821
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8822
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8823
+ * with no operator-visible status.
8824
+ */
8825
+ var StorageCleanupPhaseSchema = _enum([
8826
+ "orphans",
8827
+ "debug-media",
8828
+ "ghost-ledger",
8829
+ "done",
8830
+ "failed",
8831
+ "cancelled"
8832
+ ]);
8833
+ var StorageCleanupInputSchema = object({
8834
+ /** Also walk motion stills / track filmstrips. Off by default. */
8835
+ includeDebugMedia: boolean().optional() });
8836
+ var StorageCleanupJobSchema = object({
8837
+ jobId: string(),
8838
+ phase: StorageCleanupPhaseSchema,
8839
+ includeDebugMedia: boolean(),
8840
+ orphansReclaimed: number().int().nonnegative(),
8841
+ orphanBytesReclaimed: number().int().nonnegative(),
8842
+ debugMediaReclaimed: number().int().nonnegative(),
8843
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8844
+ ghostsForgotten: number().int().nonnegative(),
8845
+ ghostBytesForgotten: number().int().nonnegative(),
8846
+ /** Short operator-facing line: current collection, pass, or location. */
8847
+ detail: string().nullable(),
8848
+ cancelRequested: boolean(),
8849
+ startedAt: number(),
8850
+ updatedAt: number(),
8851
+ finishedAt: number().nullable(),
8852
+ error: string().nullable()
8853
+ });
8854
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8855
+ /**
8684
8856
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8685
8857
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8686
8858
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8708,11 +8880,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8708
8880
  * The default location for a type uses `id === <type>:default` by
8709
8881
  * convention (the bare type ref like `'backups'` resolves to it).
8710
8882
  *
8711
- * `isSystem: true` marks a location as orchestrator-seeded and
8712
- * undeletable. The bootstrap-installed defaults (one per type) carry
8713
- * this flag; operator-added locations don't. Editing the config of
8714
- * a system location is allowed (path migration, provider swap) but
8715
- * deleting it is rejected at the cap level.
8883
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8884
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8885
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8886
+ * / last-enabled, not on this bit.
8716
8887
  */
8717
8888
  var StorageLocationSchema = object({
8718
8889
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13531,6 +13702,12 @@ method(object({
13531
13702
  }), _void(), {
13532
13703
  kind: "mutation",
13533
13704
  auth: "admin"
13705
+ }), method(object({
13706
+ from: string(),
13707
+ to: string()
13708
+ }), object({ moved: number() }), {
13709
+ kind: "mutation",
13710
+ auth: "admin"
13534
13711
  }), method(object({
13535
13712
  deviceId: number(),
13536
13713
  disabled: boolean()
@@ -19579,53 +19756,15 @@ var RecentTracksPageSchema = object({
19579
19756
  /** Cursor for the next page, or null when this page is the last. */
19580
19757
  nextCursor: string().nullable()
19581
19758
  });
19582
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19583
- var LIST_GROUPS_MAX_LIMIT = 100;
19584
- var AnalyticsGroupRecordSchema = object({
19585
- id: string(),
19586
- deviceId: number().int(),
19587
- openedAt: number().int(),
19588
- closedAt: number().int(),
19589
- timestamp: number().int(),
19590
- memberCount: number().int(),
19591
- memberTrackIds: array(string()).readonly(),
19592
- className: string(),
19593
- classes: array(string()).readonly(),
19594
- /** Relative event-media path, or null when the group has no picture yet. */
19595
- mediaUrl: string().nullable(),
19596
- singleton: boolean()
19597
- });
19598
- var AnalyticsGroupMemberSchema = object({
19599
- trackId: string(),
19600
- deviceId: number().int(),
19601
- className: string(),
19602
- firstSeen: number().int(),
19603
- lastSeen: number().int(),
19604
- mediaUrl: string().nullable()
19605
- });
19606
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19607
- var ListGroupsQueryInput = object({
19608
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19609
- deviceIds: array(number()),
19610
- /** Window lower bound on `closedAt` (inclusive). */
19611
- since: number().optional(),
19612
- /** Window upper bound on `openedAt` (inclusive). */
19613
- until: number().optional(),
19614
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19615
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19616
- cursor: string().optional()
19617
- });
19618
- var ListGroupsPageSchema = object({
19619
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19620
- nextCursor: string().nullable()
19621
- });
19759
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19760
+ var KEY_EVENTS_MAX_LIMIT = 200;
19622
19761
  var KeyEventQueryInput = object({
19623
19762
  deviceId: number(),
19624
19763
  /** Window lower bound (track firstSeen ≥ since). */
19625
19764
  since: number(),
19626
19765
  /** Window upper bound (track firstSeen ≤ until). */
19627
19766
  until: number(),
19628
- limit: number().int().min(1).max(200).default(50),
19767
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19629
19768
  /** Drop tracks scoring below this importance. */
19630
19769
  minImportance: number().min(0).max(1).optional(),
19631
19770
  /** Restrict to a single class (e.g. 'person'). */
@@ -19647,6 +19786,32 @@ var KeyEventSchema = object({
19647
19786
  ...TrackFlagFields,
19648
19787
  ...TrackRetrainFields
19649
19788
  });
19789
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19790
+ var KeyEventBatchQueryInput = object({
19791
+ deviceIds: array(number()).min(1).max(200),
19792
+ since: number(),
19793
+ until: number(),
19794
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19795
+ * across the set, which would let a busy camera starve a quiet one of its
19796
+ * rows and change what the merged feed contains. */
19797
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19798
+ minImportance: number().min(0).max(1).optional(),
19799
+ classFilter: string().optional()
19800
+ });
19801
+ /**
19802
+ * One camera's key events in a batch answer.
19803
+ *
19804
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19805
+ * error rather than throwing, so a camera whose store read failed and one with
19806
+ * no events in the window were ALREADY indistinguishable per camera — the
19807
+ * batch does not make that worse, and the row keeps the deviceId the single
19808
+ * method's output never carried (the caller used to stamp it from the fan-out
19809
+ * key, which only worked because there was one query per camera).
19810
+ */
19811
+ var KeyEventsForDeviceSchema = object({
19812
+ deviceId: number(),
19813
+ events: array(KeyEventSchema).readonly()
19814
+ });
19650
19815
  object({
19651
19816
  trackId: string(),
19652
19817
  className: string(),
@@ -19694,9 +19859,7 @@ var TrackCascadeCountsSchema = object({
19694
19859
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19695
19860
  plates: number().int(),
19696
19861
  /** Per-track CLIP search vectors removed (best-effort). */
19697
- embeddings: number().int(),
19698
- /** Group membership + group rows removed with their last member (best-effort). */
19699
- groups: number().int()
19862
+ embeddings: number().int()
19700
19863
  });
19701
19864
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19702
19865
  var DiskReconcileCountsSchema = object({
@@ -19866,6 +20029,47 @@ var RebuildStatusSchema = object({
19866
20029
  /** Present when the pass ended by throwing. */
19867
20030
  error: string().nullable()
19868
20031
  });
20032
+ /**
20033
+ * Acknowledgement that a debug-media reclaim STARTED.
20034
+ *
20035
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
20036
+ * it runs detached and this returns immediately. Awaiting it is how the
20037
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
20038
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
20039
+ */
20040
+ var MediaReclaimStartResultSchema = object({
20041
+ started: boolean(),
20042
+ /** True when a pass was already running; the new request is ignored. */
20043
+ alreadyRunning: boolean()
20044
+ });
20045
+ var MediaReclaimInputSchema = object({
20046
+ mode: _enum(["report", "reclaim"]).default("report"),
20047
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
20048
+ deviceIds: array(number().int()).min(1).optional(),
20049
+ restart: boolean().optional(),
20050
+ pageSize: number().int().min(50).max(5e3).optional(),
20051
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
20052
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
20053
+ maxBytesPerRun: number().int().min(1).optional(),
20054
+ budgetMinutes: number().int().min(1).max(720).optional(),
20055
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
20056
+ graceMinutes: number().int().min(1).max(10080).optional()
20057
+ });
20058
+ var MediaReclaimStatusSchema = object({
20059
+ running: boolean(),
20060
+ mode: _enum(["report", "reclaim"]).nullable(),
20061
+ totalExamined: number(),
20062
+ totalEligible: number(),
20063
+ totalReclaimed: number(),
20064
+ totalBytesReclaimed: number(),
20065
+ totalRefused: number(),
20066
+ /** Device+scope windows finished in this pass. */
20067
+ devicesDone: number(),
20068
+ complete: boolean().nullable(),
20069
+ startedAtMs: number().nullable(),
20070
+ finishedAtMs: number().nullable(),
20071
+ error: string().nullable()
20072
+ });
19869
20073
  var ReplayFrameInputSchema = object({
19870
20074
  timestamp: number(),
19871
20075
  frame: PipelineRunResultBridge
@@ -19904,10 +20108,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19904
20108
  * stationary registry). Default false: the timeline lists passages,
19905
20109
  * not parking records (operator decision, 2026-08-15). */
19906
20110
  includeStationary: boolean().optional()
19907
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19908
- deviceId: number(),
19909
- groupId: string().min(1)
19910
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20111
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19911
20112
  kind: "mutation",
19912
20113
  auth: "admin"
19913
20114
  }), 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({
@@ -19916,7 +20117,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19916
20117
  until: number().optional(),
19917
20118
  kinds: array(string()).optional(),
19918
20119
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19919
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
20120
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19920
20121
  deviceId: number(),
19921
20122
  since: number(),
19922
20123
  until: number(),
@@ -20007,6 +20208,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20007
20208
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
20008
20209
  kind: "query",
20009
20210
  auth: "admin"
20211
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
20212
+ kind: "mutation",
20213
+ auth: "admin"
20214
+ }), method(object({}), MediaReclaimStatusSchema, {
20215
+ kind: "query",
20216
+ auth: "admin"
20010
20217
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
20011
20218
  kind: "query",
20012
20219
  auth: "admin"
@@ -22081,7 +22288,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
22081
22288
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
22082
22289
  kind: "mutation",
22083
22290
  auth: "admin"
22084
- });
22291
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
22292
+ kind: "mutation",
22293
+ auth: "admin"
22294
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22295
+ kind: "mutation",
22296
+ auth: "admin"
22297
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
22085
22298
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
22086
22299
  providerId: string().min(1),
22087
22300
  displayName: string().min(1),
@@ -29261,6 +29474,33 @@ var RecordingRebalanceInputSchema = object({
29261
29474
  minMoveGb: number().min(0).optional()
29262
29475
  });
29263
29476
  /**
29477
+ * Operator-facing placement of one camera onto a recordings location.
29478
+ *
29479
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
29480
+ * Auto (the planner may move this camera). `locationId` is where high/mid
29481
+ * currently write — the plan, which may disagree with the pin when Auto.
29482
+ */
29483
+ var RecordingDevicePlacementSchema = object({
29484
+ deviceId: number().int(),
29485
+ profile: string(),
29486
+ locationId: string()
29487
+ });
29488
+ var RecordingDevicePinSchema = object({
29489
+ deviceId: number().int(),
29490
+ /** Recordings-class location this camera is pinned to. */
29491
+ locationId: string()
29492
+ });
29493
+ var RecordingPlacementViewSchema = object({
29494
+ assignments: array(RecordingDevicePlacementSchema),
29495
+ pins: array(RecordingDevicePinSchema),
29496
+ defaultLocations: record(string(), string())
29497
+ });
29498
+ var RecordingSetDevicePlacementInputSchema = object({
29499
+ deviceId: number().int(),
29500
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
29501
+ locationId: string().nullable()
29502
+ });
29503
+ /**
29264
29504
  * Result of locating footage at a wall-clock instant for one device/profile.
29265
29505
  * `segment` carries the covering segment's window; `gap` reports the forward
29266
29506
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -29474,6 +29714,9 @@ method(object({
29474
29714
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29475
29715
  kind: "query",
29476
29716
  auth: "admin"
29717
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
29718
+ kind: "mutation",
29719
+ auth: "admin"
29477
29720
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29478
29721
  kind: "mutation",
29479
29722
  auth: "admin"
@@ -29483,6 +29726,12 @@ method(object({
29483
29726
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
29484
29727
  kind: "mutation",
29485
29728
  auth: "admin"
29729
+ }), method(object({}), RecordingPlacementViewSchema, {
29730
+ kind: "query",
29731
+ auth: "admin"
29732
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
29733
+ kind: "mutation",
29734
+ auth: "admin"
29486
29735
  });
29487
29736
  /**
29488
29737
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -29865,6 +30114,25 @@ var SceneMonitorStatusSchema = object({
29865
30114
  monitors: array(SceneMonitorSchema),
29866
30115
  lastFetchedAt: number()
29867
30116
  });
30117
+ /**
30118
+ * One camera's row in a `listScenesBatch` answer.
30119
+ *
30120
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
30121
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
30122
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
30123
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
30124
+ * configured. Fanned out per camera the difference was visible — one query
30125
+ * errored while the others resolved — and a batch that returned only the rows
30126
+ * it managed would have destroyed it, silently, by making an unreachable camera
30127
+ * indistinguishable from one that answered `monitors: []`.
30128
+ *
30129
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
30130
+ * could not be read"; `status.monitors: []` means "read, and it has none".
30131
+ */
30132
+ var SceneMonitorStatusForDeviceSchema = object({
30133
+ deviceId: number(),
30134
+ status: SceneMonitorStatusSchema.nullable()
30135
+ });
29868
30136
  var sceneMonitorCapability = {
29869
30137
  name: "scene-monitor",
29870
30138
  scope: "device",
@@ -29874,6 +30142,22 @@ var sceneMonitorCapability = {
29874
30142
  deviceTypes: [DeviceType.Camera],
29875
30143
  methods: {
29876
30144
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
30145
+ /**
30146
+ * The same answer, for a SET of cameras, in one round trip.
30147
+ *
30148
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
30149
+ * poll behind the push slice. Fanned out client-side that was one query
30150
+ * per camera — 29 round trips through the browser, the hub and the
30151
+ * post-analysis runner every 30 seconds to read an in-memory map the
30152
+ * owner had already merged. The work is unchanged (`statusFor` per
30153
+ * device, all in-process at the owner); what collapses is the transport.
30154
+ *
30155
+ * A camera that cannot answer still gets a row, with `status: null` —
30156
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
30157
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
30158
+ * tell which two are missing, or that any are.
30159
+ */
30160
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
29877
30161
  createScene: method(object({
29878
30162
  deviceId: number(),
29879
30163
  label: string(),
@@ -31907,6 +32191,27 @@ var CameraOccupancySnapshotSchema = object({
31907
32191
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
31908
32192
  });
31909
32193
  /**
32194
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
32195
+ *
32196
+ * THREE outcomes, and the single-camera method could only express two of them
32197
+ * because `snapshot: null` was already spoken for:
32198
+ *
32199
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
32200
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
32201
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
32202
+ * - `read: 'unreadable'` — the owner could not answer for this
32203
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
32204
+ *
32205
+ * Collapsing the last two is the failure this field exists to prevent: a
32206
+ * hydration that threw would otherwise render as an empty Stationary section,
32207
+ * which is a definite claim about a camera nobody could read.
32208
+ */
32209
+ var CameraOccupancySnapshotForDeviceSchema = object({
32210
+ deviceId: number(),
32211
+ read: _enum(["read", "unreadable"]),
32212
+ snapshot: CameraOccupancySnapshotSchema.nullable()
32213
+ });
32214
+ /**
31910
32215
  * Time-series resolution. The history methods return one bucket per
31911
32216
  * step over the requested range. Smaller resolutions cost more
31912
32217
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -31963,6 +32268,20 @@ var zoneAnalyticsCapability = {
31963
32268
  * (no inference result emitted since boot or since binding was
31964
32269
  * activated). */
31965
32270
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
32271
+ /**
32272
+ * The same snapshot, for a SET of cameras, in one round trip.
32273
+ *
32274
+ * The Events page's Stationary section polls this every 15s for every
32275
+ * selected camera. Fanned out client-side that is one query per camera to
32276
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
32277
+ * costs everything. Batched, N transports become one and the per-device
32278
+ * work is unchanged.
32279
+ *
32280
+ * Every requested deviceId gets a row, tagged `read` — see
32281
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
32282
+ * not answer for is `'unreadable'`, never an empty reading.
32283
+ */
32284
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
31966
32285
  /** Time-series object count inside one zone. `className` optional —
31967
32286
  * omit to count every class in the zone. */
31968
32287
  getZoneHistory: method(object({
@@ -34847,6 +35166,12 @@ Object.freeze({
34847
35166
  addonId: null,
34848
35167
  access: "delete"
34849
35168
  },
35169
+ "deviceManager.renameLocation": {
35170
+ capName: "device-manager",
35171
+ capScope: "system",
35172
+ addonId: null,
35173
+ access: "create"
35174
+ },
34850
35175
  "deviceManager.runDeviceAction": {
34851
35176
  capName: "device-manager",
34852
35177
  capScope: "system",
@@ -36539,13 +36864,19 @@ Object.freeze({
36539
36864
  addonId: null,
36540
36865
  access: "view"
36541
36866
  },
36542
- "pipelineAnalytics.getGroup": {
36867
+ "pipelineAnalytics.getKeyEvents": {
36543
36868
  capName: "pipeline-analytics",
36544
36869
  capScope: "device",
36545
36870
  addonId: null,
36546
36871
  access: "view"
36547
36872
  },
36548
- "pipelineAnalytics.getKeyEvents": {
36873
+ "pipelineAnalytics.getKeyEventsBatch": {
36874
+ capName: "pipeline-analytics",
36875
+ capScope: "device",
36876
+ addonId: null,
36877
+ access: "view"
36878
+ },
36879
+ "pipelineAnalytics.getMediaReclaimStatus": {
36549
36880
  capName: "pipeline-analytics",
36550
36881
  capScope: "device",
36551
36882
  addonId: null,
@@ -36635,12 +36966,6 @@ Object.freeze({
36635
36966
  addonId: null,
36636
36967
  access: "view"
36637
36968
  },
36638
- "pipelineAnalytics.listGroups": {
36639
- capName: "pipeline-analytics",
36640
- capScope: "device",
36641
- addonId: null,
36642
- access: "view"
36643
- },
36644
36969
  "pipelineAnalytics.listOpsLog": {
36645
36970
  capName: "pipeline-analytics",
36646
36971
  capScope: "device",
@@ -36725,6 +37050,12 @@ Object.freeze({
36725
37050
  addonId: null,
36726
37051
  access: "create"
36727
37052
  },
37053
+ "pipelineAnalytics.reclaimDebugMedia": {
37054
+ capName: "pipeline-analytics",
37055
+ capScope: "device",
37056
+ addonId: null,
37057
+ access: "create"
37058
+ },
36728
37059
  "pipelineAnalytics.reconcileFromDisk": {
36729
37060
  capName: "pipeline-analytics",
36730
37061
  capScope: "device",
@@ -37649,6 +37980,12 @@ Object.freeze({
37649
37980
  addonId: null,
37650
37981
  access: "view"
37651
37982
  },
37983
+ "recording.getPlacement": {
37984
+ capName: "recording",
37985
+ capScope: "system",
37986
+ addonId: null,
37987
+ access: "view"
37988
+ },
37652
37989
  "recording.getPlaybackManifest": {
37653
37990
  capName: "recording",
37654
37991
  capScope: "system",
@@ -37727,6 +38064,12 @@ Object.freeze({
37727
38064
  addonId: null,
37728
38065
  access: "view"
37729
38066
  },
38067
+ "recording.reconcileLedgerAgainstDisk": {
38068
+ capName: "recording",
38069
+ capScope: "system",
38070
+ addonId: null,
38071
+ access: "create"
38072
+ },
37730
38073
  "recording.refreshStorageLocationsForMigration": {
37731
38074
  capName: "recording",
37732
38075
  capScope: "system",
@@ -37769,6 +38112,12 @@ Object.freeze({
37769
38112
  addonId: null,
37770
38113
  access: "create"
37771
38114
  },
38115
+ "recording.setDevicePlacement": {
38116
+ capName: "recording",
38117
+ capScope: "system",
38118
+ addonId: null,
38119
+ access: "create"
38120
+ },
37772
38121
  "recording.startStorageMigrationMove": {
37773
38122
  capName: "recording",
37774
38123
  capScope: "system",
@@ -37853,6 +38202,12 @@ Object.freeze({
37853
38202
  addonId: null,
37854
38203
  access: "view"
37855
38204
  },
38205
+ "sceneMonitor.listScenesBatch": {
38206
+ capName: "scene-monitor",
38207
+ capScope: "device",
38208
+ addonId: null,
38209
+ access: "view"
38210
+ },
37856
38211
  "sceneMonitor.recheckNow": {
37857
38212
  capName: "scene-monitor",
37858
38213
  capScope: "device",
@@ -38207,12 +38562,36 @@ Object.freeze({
38207
38562
  addonId: null,
38208
38563
  access: "create"
38209
38564
  },
38565
+ "storageMigration.cleanupCancel": {
38566
+ capName: "storage-migration",
38567
+ capScope: "system",
38568
+ addonId: null,
38569
+ access: "create"
38570
+ },
38571
+ "storageMigration.cleanupStart": {
38572
+ capName: "storage-migration",
38573
+ capScope: "system",
38574
+ addonId: null,
38575
+ access: "create"
38576
+ },
38577
+ "storageMigration.cleanupStatus": {
38578
+ capName: "storage-migration",
38579
+ capScope: "system",
38580
+ addonId: null,
38581
+ access: "view"
38582
+ },
38210
38583
  "storageMigration.drain": {
38211
38584
  capName: "storage-migration",
38212
38585
  capScope: "system",
38213
38586
  addonId: null,
38214
38587
  access: "create"
38215
38588
  },
38589
+ "storageMigration.history": {
38590
+ capName: "storage-migration",
38591
+ capScope: "system",
38592
+ addonId: null,
38593
+ access: "view"
38594
+ },
38216
38595
  "storageMigration.movers": {
38217
38596
  capName: "storage-migration",
38218
38597
  capScope: "system",
@@ -39209,6 +39588,12 @@ Object.freeze({
39209
39588
  addonId: null,
39210
39589
  access: "view"
39211
39590
  },
39591
+ "zoneAnalytics.getCurrentSnapshotBatch": {
39592
+ capName: "zone-analytics",
39593
+ capScope: "device",
39594
+ addonId: null,
39595
+ access: "view"
39596
+ },
39212
39597
  "zoneAnalytics.getUnzonedHistory": {
39213
39598
  capName: "zone-analytics",
39214
39599
  capScope: "device",
@@ -40203,14 +40588,14 @@ Object.freeze({
40203
40588
  form: "single",
40204
40589
  optional: true
40205
40590
  }],
40206
- "pipelineAnalytics.getGroup": [{
40591
+ "pipelineAnalytics.getKeyEvents": [{
40207
40592
  name: "deviceId",
40208
40593
  form: "single",
40209
40594
  optional: false
40210
40595
  }],
40211
- "pipelineAnalytics.getKeyEvents": [{
40212
- name: "deviceId",
40213
- form: "single",
40596
+ "pipelineAnalytics.getKeyEventsBatch": [{
40597
+ name: "deviceIds",
40598
+ form: "array",
40214
40599
  optional: false
40215
40600
  }],
40216
40601
  "pipelineAnalytics.getMotionEvents": [{
@@ -40268,11 +40653,6 @@ Object.freeze({
40268
40653
  form: "single",
40269
40654
  optional: false
40270
40655
  }],
40271
- "pipelineAnalytics.listGroups": [{
40272
- name: "deviceIds",
40273
- form: "array",
40274
- optional: false
40275
- }],
40276
40656
  "pipelineAnalytics.listOpsLog": [{
40277
40657
  name: "deviceId",
40278
40658
  form: "single",
@@ -40318,6 +40698,11 @@ Object.freeze({
40318
40698
  form: "single",
40319
40699
  optional: true
40320
40700
  }],
40701
+ "pipelineAnalytics.reclaimDebugMedia": [{
40702
+ name: "deviceIds",
40703
+ form: "array",
40704
+ optional: true
40705
+ }],
40321
40706
  "pipelineAnalytics.reconcileFromDisk": [{
40322
40707
  name: "deviceId",
40323
40708
  form: "single",
@@ -40653,6 +41038,11 @@ Object.freeze({
40653
41038
  form: "single",
40654
41039
  optional: false
40655
41040
  }],
41041
+ "recording.reconcileLedgerAgainstDisk": [{
41042
+ name: "deviceId",
41043
+ form: "single",
41044
+ optional: true
41045
+ }],
40656
41046
  "recording.relocateFootage": [{
40657
41047
  name: "deviceId",
40658
41048
  form: "single",
@@ -40678,6 +41068,11 @@ Object.freeze({
40678
41068
  form: "single",
40679
41069
  optional: false
40680
41070
  }],
41071
+ "recording.setDevicePlacement": [{
41072
+ name: "deviceId",
41073
+ form: "single",
41074
+ optional: false
41075
+ }],
40681
41076
  "recording.startStorageMigrationMove": [{
40682
41077
  name: "deviceId",
40683
41078
  form: "single",
@@ -40718,6 +41113,11 @@ Object.freeze({
40718
41113
  form: "single",
40719
41114
  optional: false
40720
41115
  }],
41116
+ "sceneMonitor.listScenesBatch": [{
41117
+ name: "deviceIds",
41118
+ form: "array",
41119
+ optional: false
41120
+ }],
40721
41121
  "sceneMonitor.recheckNow": [{
40722
41122
  name: "deviceId",
40723
41123
  form: "single",
@@ -40979,6 +41379,11 @@ Object.freeze({
40979
41379
  form: "single",
40980
41380
  optional: false
40981
41381
  }],
41382
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
41383
+ name: "deviceIds",
41384
+ form: "array",
41385
+ optional: false
41386
+ }],
40982
41387
  "zoneAnalytics.getUnzonedHistory": [{
40983
41388
  name: "deviceId",
40984
41389
  form: "single",