@camstack/addon-provider-dreame 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
@@ -58523,6 +58523,20 @@ var RelocateJobSchema = object({
58523
58523
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
58524
58524
  */
58525
58525
  rowsReconciled: number().int().nonnegative().optional(),
58526
+ /**
58527
+ * Rows this run FORGOT because the file they name is not on disk.
58528
+ *
58529
+ * The mover derived the path from the row's own fields and `stat`ed it; an
58530
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
58531
+ * and the durable row is dropped through the same channel eviction uses. It
58532
+ * is reported for the same reason `rowsReconciled` is: this is a durable
58533
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
58534
+ * the same failure as one that quietly skips them (D295).
58535
+ *
58536
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
58537
+ * ledger claimed 5.65 GB of footage that no longer existed.
58538
+ */
58539
+ rowsForgotten: number().int().nonnegative().optional(),
58526
58540
  startedAt: number(),
58527
58541
  finishedAt: number().nullable(),
58528
58542
  error: string().nullable()
@@ -58587,6 +58601,13 @@ var MediaRelocateModeSchema = _enum([
58587
58601
  ]);
58588
58602
  var RelocateMediaInputSchema = object({
58589
58603
  toLocationId: string(),
58604
+ /**
58605
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
58606
+ * every row that is not already on `toLocationId` (the historical
58607
+ * behaviour). A named source is what a from→to migration needs: without it
58608
+ * "move events off disk 2" also emptied disk 1.
58609
+ */
58610
+ fromLocationId: string().optional(),
58590
58611
  throttleMbps: number().min(1).max(1e3).optional(),
58591
58612
  /** Omitted = `move`, the pre-existing behaviour. */
58592
58613
  mode: MediaRelocateModeSchema.optional()
@@ -58654,6 +58675,19 @@ var StorageMigrationDestinationsSchema = object({
58654
58675
  galleryMedia: string().min(1).optional()
58655
58676
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
58656
58677
  /**
58678
+ * Optional named source per class. Omitted = the class's current default
58679
+ * (the historical behaviour). A named source that is NOT the default is a
58680
+ * drain of that disk: bytes move, the default stays, and the source is
58681
+ * disabled when the move finishes.
58682
+ */
58683
+ var StorageMigrationSourcesSchema = object({
58684
+ recordings: string().min(1).optional(),
58685
+ recordingsLow: string().min(1).optional(),
58686
+ eventMedia: string().min(1).optional(),
58687
+ backups: string().min(1).optional(),
58688
+ galleryMedia: string().min(1).optional()
58689
+ }).optional();
58690
+ /**
58657
58691
  * How a migration sequences the cutover against the byte move.
58658
58692
  *
58659
58693
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -58675,6 +58709,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
58675
58709
  /** Shared input for planning and starting an orchestrated storage migration. */
58676
58710
  var StorageMigrationInputSchema = object({
58677
58711
  destinations: StorageMigrationDestinationsSchema,
58712
+ /** Omitted = each class's current default. */
58713
+ sources: StorageMigrationSourcesSchema,
58678
58714
  throttleMbps: number().min(1).max(1e3).optional(),
58679
58715
  /** Omitted = `blocking`, which stays the default. */
58680
58716
  mode: StorageMigrationModeSchema.optional()
@@ -58754,6 +58790,13 @@ var StorageMigrationMoveSchema = object({
58754
58790
  storageClass: StorageMigrationClassSchema,
58755
58791
  fromLocationId: string(),
58756
58792
  toLocationId: string(),
58793
+ /**
58794
+ * True when `from` was NOT the class default at plan time. The move still
58795
+ * copies bytes, but the default is left alone and the source is disabled
58796
+ * once the copy verifies. Absent on jobs planned before this field existed
58797
+ * — those jobs always repointed, which is `false`.
58798
+ */
58799
+ freezeSource: boolean().optional(),
58757
58800
  moverJobId: string().nullable(),
58758
58801
  state: RelocateJobStateSchema.nullable(),
58759
58802
  error: string().nullable(),
@@ -58767,6 +58810,7 @@ var StorageMigrationJobSchema = object({
58767
58810
  * can tell a seconds-long cutover from a thirty-hour one. */
58768
58811
  mode: StorageMigrationModeSchema,
58769
58812
  destinations: StorageMigrationDestinationsSchema,
58813
+ sources: StorageMigrationSourcesSchema,
58770
58814
  throttleMbps: number(),
58771
58815
  moves: array(StorageMigrationMoveSchema),
58772
58816
  pauseLeaseId: string().nullable(),
@@ -58792,6 +58836,7 @@ var StorageMigrationFindingSchema = object({
58792
58836
  });
58793
58837
  var StorageMigrationPlanSchema = object({
58794
58838
  destinations: StorageMigrationDestinationsSchema,
58839
+ sources: StorageMigrationSourcesSchema,
58795
58840
  /** The mode this plan was built for. A plan is only valid for its mode: the
58796
58841
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
58797
58842
  * it. */
@@ -58799,7 +58844,8 @@ var StorageMigrationPlanSchema = object({
58799
58844
  moves: array(object({
58800
58845
  storageClass: StorageMigrationClassSchema,
58801
58846
  fromLocationId: string(),
58802
- toLocationId: string()
58847
+ toLocationId: string(),
58848
+ freezeSource: boolean().optional()
58803
58849
  })),
58804
58850
  findings: array(StorageMigrationFindingSchema)
58805
58851
  });
@@ -58886,16 +58932,142 @@ var RelocateResidueSchema = object({
58886
58932
  segments: number().int().nonnegative(),
58887
58933
  bytes: number().int().nonnegative()
58888
58934
  }).nullable();
58935
+ /**
58936
+ * Ask one location whether its durable hour rows describe the disk — the walk
58937
+ * (D319).
58938
+ *
58939
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
58940
+ * missing tool is the question, and the dry run is how they sanity-check the
58941
+ * destructive run before authorising it.
58942
+ */
58943
+ var LedgerWalkInputSchema = object({
58944
+ locationId: string().min(1),
58945
+ /** Forget the confirmed-absent rows, rather than only counting them. */
58946
+ apply: boolean().optional(),
58947
+ /** Narrow to one camera. */
58948
+ deviceId: number().int().positive().optional(),
58949
+ /** Narrow to these recording profiles; empty/absent = every profile. */
58950
+ profiles: array(string().min(1)).optional()
58951
+ });
58952
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
58953
+ var LedgerWalkRefusalSchema = _enum([
58954
+ "location-unknown",
58955
+ "source-writable",
58956
+ "no-ledger",
58957
+ "archive-unreadable",
58958
+ "anchor-absent",
58959
+ "anchor-unreadable",
58960
+ "anchor-moved"
58961
+ ]);
58962
+ _enum([
58963
+ "live-tail",
58964
+ "listing-error",
58965
+ "path-mismatch",
58966
+ "durable-refused"
58967
+ ]);
58968
+ /** Every skip reason, always present, always a number — so a reason that never
58969
+ * fired reports as zero rather than absent and the report shape is constant
58970
+ * between passes. Spelled out rather than `z.record` for exactly that. */
58971
+ var LedgerWalkSkipCountsSchema = object({
58972
+ "live-tail": number().int().nonnegative(),
58973
+ "listing-error": number().int().nonnegative(),
58974
+ "path-mismatch": number().int().nonnegative(),
58975
+ "durable-refused": number().int().nonnegative()
58976
+ });
58977
+ /** One camera's share of a walk, so a report names cameras and not rows. */
58978
+ var LedgerWalkDeviceReportSchema = object({
58979
+ deviceId: number().int(),
58980
+ hoursWalked: number().int().nonnegative(),
58981
+ hoursMissing: number().int().nonnegative(),
58982
+ ghostSegments: number().int().nonnegative(),
58983
+ ghostBytes: number().int().nonnegative(),
58984
+ forgottenSegments: number().int().nonnegative(),
58985
+ orphanFiles: number().int().nonnegative()
58986
+ });
58987
+ /**
58988
+ * What one walk claimed, listed, found and (only when armed) forgot.
58989
+ *
58990
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
58991
+ * walk that saw a fraction of the location is visible in its own report rather
58992
+ * than in the absence of one.
58993
+ */
58994
+ var LedgerWalkReportSchema = object({
58995
+ locationId: string(),
58996
+ applied: boolean(),
58997
+ refused: LedgerWalkRefusalSchema.nullable(),
58998
+ archiveSegments: number().int().nonnegative().nullable(),
58999
+ archiveBytes: number().int().nonnegative().nullable(),
59000
+ hoursClaimed: number().int().nonnegative(),
59001
+ hoursWalked: number().int().nonnegative(),
59002
+ hoursMissing: number().int().nonnegative(),
59003
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
59004
+ listings: number().int().nonnegative(),
59005
+ segmentsClaimed: number().int().nonnegative(),
59006
+ ghostSegments: number().int().nonnegative(),
59007
+ ghostBytes: number().int().nonnegative(),
59008
+ ghostHoursWhole: number().int().nonnegative(),
59009
+ forgottenSegments: number().int().nonnegative(),
59010
+ forgottenBytes: number().int().nonnegative(),
59011
+ /** Files under a claimed hour that no durable row names. Never deleted. */
59012
+ orphanFiles: number().int().nonnegative(),
59013
+ orphanSample: array(string()).readonly(),
59014
+ hoursSkipped: number().int().nonnegative(),
59015
+ skippedByReason: LedgerWalkSkipCountsSchema,
59016
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
59017
+ bounded: boolean(),
59018
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
59019
+ });
58889
59020
  /** How many rows a media pass would still act on against a given target — the
58890
59021
  * media lane's denominator AND its residue, from ONE derivation so the two can
58891
59022
  * never disagree. `null` = the count could not be taken. */
58892
59023
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
58893
59024
  var RelocatableMediaCountInputSchema = object({
58894
59025
  toLocationId: string().min(1),
59026
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
59027
+ fromLocationId: string().optional(),
58895
59028
  /** Omitted = `move`. */
58896
59029
  mode: MediaRelocateModeSchema.optional()
58897
59030
  });
58898
59031
  /**
59032
+ * Operator cleanup of leftover analytics rows, optional debug media, and
59033
+ * ghost ledger entries on frozen footage locations.
59034
+ *
59035
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
59036
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
59037
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
59038
+ * with no operator-visible status.
59039
+ */
59040
+ var StorageCleanupPhaseSchema = _enum([
59041
+ "orphans",
59042
+ "debug-media",
59043
+ "ghost-ledger",
59044
+ "done",
59045
+ "failed",
59046
+ "cancelled"
59047
+ ]);
59048
+ var StorageCleanupInputSchema = object({
59049
+ /** Also walk motion stills / track filmstrips. Off by default. */
59050
+ includeDebugMedia: boolean().optional() });
59051
+ var StorageCleanupJobSchema = object({
59052
+ jobId: string(),
59053
+ phase: StorageCleanupPhaseSchema,
59054
+ includeDebugMedia: boolean(),
59055
+ orphansReclaimed: number().int().nonnegative(),
59056
+ orphanBytesReclaimed: number().int().nonnegative(),
59057
+ debugMediaReclaimed: number().int().nonnegative(),
59058
+ debugMediaBytesReclaimed: number().int().nonnegative(),
59059
+ ghostsForgotten: number().int().nonnegative(),
59060
+ ghostBytesForgotten: number().int().nonnegative(),
59061
+ /** Short operator-facing line: current collection, pass, or location. */
59062
+ detail: string().nullable(),
59063
+ cancelRequested: boolean(),
59064
+ startedAt: number(),
59065
+ updatedAt: number(),
59066
+ finishedAt: number().nullable(),
59067
+ error: string().nullable()
59068
+ });
59069
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
59070
+ /**
58899
59071
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
58900
59072
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
58901
59073
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -58923,11 +59095,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
58923
59095
  * The default location for a type uses `id === <type>:default` by
58924
59096
  * convention (the bare type ref like `'backups'` resolves to it).
58925
59097
  *
58926
- * `isSystem: true` marks a location as orchestrator-seeded and
58927
- * undeletable. The bootstrap-installed defaults (one per type) carry
58928
- * this flag; operator-added locations don't. Editing the config of
58929
- * a system location is allowed (path migration, provider swap) but
58930
- * deleting it is rejected at the cap level.
59098
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
59099
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
59100
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
59101
+ * / last-enabled, not on this bit.
58931
59102
  */
58932
59103
  var StorageLocationSchema = object({
58933
59104
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -63470,6 +63641,12 @@ method(object({
63470
63641
  }), _void(), {
63471
63642
  kind: "mutation",
63472
63643
  auth: "admin"
63644
+ }), method(object({
63645
+ from: string(),
63646
+ to: string()
63647
+ }), object({ moved: number() }), {
63648
+ kind: "mutation",
63649
+ auth: "admin"
63473
63650
  }), method(object({
63474
63651
  deviceId: number(),
63475
63652
  disabled: boolean()
@@ -69478,53 +69655,15 @@ var RecentTracksPageSchema = object({
69478
69655
  /** Cursor for the next page, or null when this page is the last. */
69479
69656
  nextCursor: string().nullable()
69480
69657
  });
69481
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
69482
- var LIST_GROUPS_MAX_LIMIT = 100;
69483
- var AnalyticsGroupRecordSchema = object({
69484
- id: string(),
69485
- deviceId: number().int(),
69486
- openedAt: number().int(),
69487
- closedAt: number().int(),
69488
- timestamp: number().int(),
69489
- memberCount: number().int(),
69490
- memberTrackIds: array(string()).readonly(),
69491
- className: string(),
69492
- classes: array(string()).readonly(),
69493
- /** Relative event-media path, or null when the group has no picture yet. */
69494
- mediaUrl: string().nullable(),
69495
- singleton: boolean()
69496
- });
69497
- var AnalyticsGroupMemberSchema = object({
69498
- trackId: string(),
69499
- deviceId: number().int(),
69500
- className: string(),
69501
- firstSeen: number().int(),
69502
- lastSeen: number().int(),
69503
- mediaUrl: string().nullable()
69504
- });
69505
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
69506
- var ListGroupsQueryInput = object({
69507
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
69508
- deviceIds: array(number()),
69509
- /** Window lower bound on `closedAt` (inclusive). */
69510
- since: number().optional(),
69511
- /** Window upper bound on `openedAt` (inclusive). */
69512
- until: number().optional(),
69513
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
69514
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
69515
- cursor: string().optional()
69516
- });
69517
- var ListGroupsPageSchema = object({
69518
- groups: array(AnalyticsGroupRecordSchema).readonly(),
69519
- nextCursor: string().nullable()
69520
- });
69658
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
69659
+ var KEY_EVENTS_MAX_LIMIT = 200;
69521
69660
  var KeyEventQueryInput = object({
69522
69661
  deviceId: number(),
69523
69662
  /** Window lower bound (track firstSeen ≥ since). */
69524
69663
  since: number(),
69525
69664
  /** Window upper bound (track firstSeen ≤ until). */
69526
69665
  until: number(),
69527
- limit: number().int().min(1).max(200).default(50),
69666
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
69528
69667
  /** Drop tracks scoring below this importance. */
69529
69668
  minImportance: number().min(0).max(1).optional(),
69530
69669
  /** Restrict to a single class (e.g. 'person'). */
@@ -69546,6 +69685,32 @@ var KeyEventSchema = object({
69546
69685
  ...TrackFlagFields,
69547
69686
  ...TrackRetrainFields
69548
69687
  });
69688
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
69689
+ var KeyEventBatchQueryInput = object({
69690
+ deviceIds: array(number()).min(1).max(200),
69691
+ since: number(),
69692
+ until: number(),
69693
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
69694
+ * across the set, which would let a busy camera starve a quiet one of its
69695
+ * rows and change what the merged feed contains. */
69696
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
69697
+ minImportance: number().min(0).max(1).optional(),
69698
+ classFilter: string().optional()
69699
+ });
69700
+ /**
69701
+ * One camera's key events in a batch answer.
69702
+ *
69703
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
69704
+ * error rather than throwing, so a camera whose store read failed and one with
69705
+ * no events in the window were ALREADY indistinguishable per camera — the
69706
+ * batch does not make that worse, and the row keeps the deviceId the single
69707
+ * method's output never carried (the caller used to stamp it from the fan-out
69708
+ * key, which only worked because there was one query per camera).
69709
+ */
69710
+ var KeyEventsForDeviceSchema = object({
69711
+ deviceId: number(),
69712
+ events: array(KeyEventSchema).readonly()
69713
+ });
69549
69714
  object({
69550
69715
  trackId: string(),
69551
69716
  className: string(),
@@ -69593,9 +69758,7 @@ var TrackCascadeCountsSchema = object({
69593
69758
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
69594
69759
  plates: number().int(),
69595
69760
  /** Per-track CLIP search vectors removed (best-effort). */
69596
- embeddings: number().int(),
69597
- /** Group membership + group rows removed with their last member (best-effort). */
69598
- groups: number().int()
69761
+ embeddings: number().int()
69599
69762
  });
69600
69763
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
69601
69764
  var DiskReconcileCountsSchema = object({
@@ -69765,6 +69928,47 @@ var RebuildStatusSchema = object({
69765
69928
  /** Present when the pass ended by throwing. */
69766
69929
  error: string().nullable()
69767
69930
  });
69931
+ /**
69932
+ * Acknowledgement that a debug-media reclaim STARTED.
69933
+ *
69934
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
69935
+ * it runs detached and this returns immediately. Awaiting it is how the
69936
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
69937
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
69938
+ */
69939
+ var MediaReclaimStartResultSchema = object({
69940
+ started: boolean(),
69941
+ /** True when a pass was already running; the new request is ignored. */
69942
+ alreadyRunning: boolean()
69943
+ });
69944
+ var MediaReclaimInputSchema = object({
69945
+ mode: _enum(["report", "reclaim"]).default("report"),
69946
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
69947
+ deviceIds: array(number().int()).min(1).optional(),
69948
+ restart: boolean().optional(),
69949
+ pageSize: number().int().min(50).max(5e3).optional(),
69950
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
69951
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
69952
+ maxBytesPerRun: number().int().min(1).optional(),
69953
+ budgetMinutes: number().int().min(1).max(720).optional(),
69954
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
69955
+ graceMinutes: number().int().min(1).max(10080).optional()
69956
+ });
69957
+ var MediaReclaimStatusSchema = object({
69958
+ running: boolean(),
69959
+ mode: _enum(["report", "reclaim"]).nullable(),
69960
+ totalExamined: number(),
69961
+ totalEligible: number(),
69962
+ totalReclaimed: number(),
69963
+ totalBytesReclaimed: number(),
69964
+ totalRefused: number(),
69965
+ /** Device+scope windows finished in this pass. */
69966
+ devicesDone: number(),
69967
+ complete: boolean().nullable(),
69968
+ startedAtMs: number().nullable(),
69969
+ finishedAtMs: number().nullable(),
69970
+ error: string().nullable()
69971
+ });
69768
69972
  var ReplayFrameInputSchema = object({
69769
69973
  timestamp: number(),
69770
69974
  frame: PipelineRunResultBridge
@@ -69803,10 +70007,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
69803
70007
  * stationary registry). Default false: the timeline lists passages,
69804
70008
  * not parking records (operator decision, 2026-08-15). */
69805
70009
  includeStationary: boolean().optional()
69806
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
69807
- deviceId: number(),
69808
- groupId: string().min(1)
69809
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
70010
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
69810
70011
  kind: "mutation",
69811
70012
  auth: "admin"
69812
70013
  }), 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({
@@ -69815,7 +70016,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
69815
70016
  until: number().optional(),
69816
70017
  kinds: array(string()).optional(),
69817
70018
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
69818
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
70019
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
69819
70020
  deviceId: number(),
69820
70021
  since: number(),
69821
70022
  until: number(),
@@ -69906,6 +70107,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
69906
70107
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
69907
70108
  kind: "query",
69908
70109
  auth: "admin"
70110
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
70111
+ kind: "mutation",
70112
+ auth: "admin"
70113
+ }), method(object({}), MediaReclaimStatusSchema, {
70114
+ kind: "query",
70115
+ auth: "admin"
69909
70116
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
69910
70117
  kind: "query",
69911
70118
  auth: "admin"
@@ -71876,7 +72083,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
71876
72083
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
71877
72084
  kind: "mutation",
71878
72085
  auth: "admin"
71879
- });
72086
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
72087
+ kind: "mutation",
72088
+ auth: "admin"
72089
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
72090
+ kind: "mutation",
72091
+ auth: "admin"
72092
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
71880
72093
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
71881
72094
  providerId: string().min(1),
71882
72095
  displayName: string().min(1),
@@ -78994,6 +79207,33 @@ var RecordingRebalanceInputSchema = object({
78994
79207
  minMoveGb: number().min(0).optional()
78995
79208
  });
78996
79209
  /**
79210
+ * Operator-facing placement of one camera onto a recordings location.
79211
+ *
79212
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
79213
+ * Auto (the planner may move this camera). `locationId` is where high/mid
79214
+ * currently write — the plan, which may disagree with the pin when Auto.
79215
+ */
79216
+ var RecordingDevicePlacementSchema = object({
79217
+ deviceId: number().int(),
79218
+ profile: string(),
79219
+ locationId: string()
79220
+ });
79221
+ var RecordingDevicePinSchema = object({
79222
+ deviceId: number().int(),
79223
+ /** Recordings-class location this camera is pinned to. */
79224
+ locationId: string()
79225
+ });
79226
+ var RecordingPlacementViewSchema = object({
79227
+ assignments: array(RecordingDevicePlacementSchema),
79228
+ pins: array(RecordingDevicePinSchema),
79229
+ defaultLocations: record(string(), string())
79230
+ });
79231
+ var RecordingSetDevicePlacementInputSchema = object({
79232
+ deviceId: number().int(),
79233
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
79234
+ locationId: string().nullable()
79235
+ });
79236
+ /**
78997
79237
  * Result of locating footage at a wall-clock instant for one device/profile.
78998
79238
  * `segment` carries the covering segment's window; `gap` reports the forward
78999
79239
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -79207,6 +79447,9 @@ method(object({
79207
79447
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
79208
79448
  kind: "query",
79209
79449
  auth: "admin"
79450
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
79451
+ kind: "mutation",
79452
+ auth: "admin"
79210
79453
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
79211
79454
  kind: "mutation",
79212
79455
  auth: "admin"
@@ -79216,6 +79459,12 @@ method(object({
79216
79459
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
79217
79460
  kind: "mutation",
79218
79461
  auth: "admin"
79462
+ }), method(object({}), RecordingPlacementViewSchema, {
79463
+ kind: "query",
79464
+ auth: "admin"
79465
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
79466
+ kind: "mutation",
79467
+ auth: "admin"
79219
79468
  });
79220
79469
  /**
79221
79470
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -79598,6 +79847,25 @@ var SceneMonitorStatusSchema = object({
79598
79847
  monitors: array(SceneMonitorSchema),
79599
79848
  lastFetchedAt: number()
79600
79849
  });
79850
+ /**
79851
+ * One camera's row in a `listScenesBatch` answer.
79852
+ *
79853
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
79854
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
79855
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
79856
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
79857
+ * configured. Fanned out per camera the difference was visible — one query
79858
+ * errored while the others resolved — and a batch that returned only the rows
79859
+ * it managed would have destroyed it, silently, by making an unreachable camera
79860
+ * indistinguishable from one that answered `monitors: []`.
79861
+ *
79862
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
79863
+ * could not be read"; `status.monitors: []` means "read, and it has none".
79864
+ */
79865
+ var SceneMonitorStatusForDeviceSchema = object({
79866
+ deviceId: number(),
79867
+ status: SceneMonitorStatusSchema.nullable()
79868
+ });
79601
79869
  var sceneMonitorCapability = {
79602
79870
  name: "scene-monitor",
79603
79871
  scope: "device",
@@ -79607,6 +79875,22 @@ var sceneMonitorCapability = {
79607
79875
  deviceTypes: [DeviceType.Camera],
79608
79876
  methods: {
79609
79877
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
79878
+ /**
79879
+ * The same answer, for a SET of cameras, in one round trip.
79880
+ *
79881
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
79882
+ * poll behind the push slice. Fanned out client-side that was one query
79883
+ * per camera — 29 round trips through the browser, the hub and the
79884
+ * post-analysis runner every 30 seconds to read an in-memory map the
79885
+ * owner had already merged. The work is unchanged (`statusFor` per
79886
+ * device, all in-process at the owner); what collapses is the transport.
79887
+ *
79888
+ * A camera that cannot answer still gets a row, with `status: null` —
79889
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
79890
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
79891
+ * tell which two are missing, or that any are.
79892
+ */
79893
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
79610
79894
  createScene: method(object({
79611
79895
  deviceId: number(),
79612
79896
  label: string(),
@@ -81442,6 +81726,27 @@ var CameraOccupancySnapshotSchema = object({
81442
81726
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
81443
81727
  });
81444
81728
  /**
81729
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
81730
+ *
81731
+ * THREE outcomes, and the single-camera method could only express two of them
81732
+ * because `snapshot: null` was already spoken for:
81733
+ *
81734
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
81735
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
81736
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
81737
+ * - `read: 'unreadable'` — the owner could not answer for this
81738
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
81739
+ *
81740
+ * Collapsing the last two is the failure this field exists to prevent: a
81741
+ * hydration that threw would otherwise render as an empty Stationary section,
81742
+ * which is a definite claim about a camera nobody could read.
81743
+ */
81744
+ var CameraOccupancySnapshotForDeviceSchema = object({
81745
+ deviceId: number(),
81746
+ read: _enum(["read", "unreadable"]),
81747
+ snapshot: CameraOccupancySnapshotSchema.nullable()
81748
+ });
81749
+ /**
81445
81750
  * Time-series resolution. The history methods return one bucket per
81446
81751
  * step over the requested range. Smaller resolutions cost more
81447
81752
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -81498,6 +81803,20 @@ var zoneAnalyticsCapability = {
81498
81803
  * (no inference result emitted since boot or since binding was
81499
81804
  * activated). */
81500
81805
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
81806
+ /**
81807
+ * The same snapshot, for a SET of cameras, in one round trip.
81808
+ *
81809
+ * The Events page's Stationary section polls this every 15s for every
81810
+ * selected camera. Fanned out client-side that is one query per camera to
81811
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
81812
+ * costs everything. Batched, N transports become one and the per-device
81813
+ * work is unchanged.
81814
+ *
81815
+ * Every requested deviceId gets a row, tagged `read` — see
81816
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
81817
+ * not answer for is `'unreadable'`, never an empty reading.
81818
+ */
81819
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
81501
81820
  /** Time-series object count inside one zone. `className` optional —
81502
81821
  * omit to count every class in the zone. */
81503
81822
  getZoneHistory: method(object({
@@ -84215,6 +84534,12 @@ Object.freeze({
84215
84534
  addonId: null,
84216
84535
  access: "delete"
84217
84536
  },
84537
+ "deviceManager.renameLocation": {
84538
+ capName: "device-manager",
84539
+ capScope: "system",
84540
+ addonId: null,
84541
+ access: "create"
84542
+ },
84218
84543
  "deviceManager.runDeviceAction": {
84219
84544
  capName: "device-manager",
84220
84545
  capScope: "system",
@@ -85907,13 +86232,19 @@ Object.freeze({
85907
86232
  addonId: null,
85908
86233
  access: "view"
85909
86234
  },
85910
- "pipelineAnalytics.getGroup": {
86235
+ "pipelineAnalytics.getKeyEvents": {
85911
86236
  capName: "pipeline-analytics",
85912
86237
  capScope: "device",
85913
86238
  addonId: null,
85914
86239
  access: "view"
85915
86240
  },
85916
- "pipelineAnalytics.getKeyEvents": {
86241
+ "pipelineAnalytics.getKeyEventsBatch": {
86242
+ capName: "pipeline-analytics",
86243
+ capScope: "device",
86244
+ addonId: null,
86245
+ access: "view"
86246
+ },
86247
+ "pipelineAnalytics.getMediaReclaimStatus": {
85917
86248
  capName: "pipeline-analytics",
85918
86249
  capScope: "device",
85919
86250
  addonId: null,
@@ -86003,12 +86334,6 @@ Object.freeze({
86003
86334
  addonId: null,
86004
86335
  access: "view"
86005
86336
  },
86006
- "pipelineAnalytics.listGroups": {
86007
- capName: "pipeline-analytics",
86008
- capScope: "device",
86009
- addonId: null,
86010
- access: "view"
86011
- },
86012
86337
  "pipelineAnalytics.listOpsLog": {
86013
86338
  capName: "pipeline-analytics",
86014
86339
  capScope: "device",
@@ -86093,6 +86418,12 @@ Object.freeze({
86093
86418
  addonId: null,
86094
86419
  access: "create"
86095
86420
  },
86421
+ "pipelineAnalytics.reclaimDebugMedia": {
86422
+ capName: "pipeline-analytics",
86423
+ capScope: "device",
86424
+ addonId: null,
86425
+ access: "create"
86426
+ },
86096
86427
  "pipelineAnalytics.reconcileFromDisk": {
86097
86428
  capName: "pipeline-analytics",
86098
86429
  capScope: "device",
@@ -87017,6 +87348,12 @@ Object.freeze({
87017
87348
  addonId: null,
87018
87349
  access: "view"
87019
87350
  },
87351
+ "recording.getPlacement": {
87352
+ capName: "recording",
87353
+ capScope: "system",
87354
+ addonId: null,
87355
+ access: "view"
87356
+ },
87020
87357
  "recording.getPlaybackManifest": {
87021
87358
  capName: "recording",
87022
87359
  capScope: "system",
@@ -87095,6 +87432,12 @@ Object.freeze({
87095
87432
  addonId: null,
87096
87433
  access: "view"
87097
87434
  },
87435
+ "recording.reconcileLedgerAgainstDisk": {
87436
+ capName: "recording",
87437
+ capScope: "system",
87438
+ addonId: null,
87439
+ access: "create"
87440
+ },
87098
87441
  "recording.refreshStorageLocationsForMigration": {
87099
87442
  capName: "recording",
87100
87443
  capScope: "system",
@@ -87137,6 +87480,12 @@ Object.freeze({
87137
87480
  addonId: null,
87138
87481
  access: "create"
87139
87482
  },
87483
+ "recording.setDevicePlacement": {
87484
+ capName: "recording",
87485
+ capScope: "system",
87486
+ addonId: null,
87487
+ access: "create"
87488
+ },
87140
87489
  "recording.startStorageMigrationMove": {
87141
87490
  capName: "recording",
87142
87491
  capScope: "system",
@@ -87221,6 +87570,12 @@ Object.freeze({
87221
87570
  addonId: null,
87222
87571
  access: "view"
87223
87572
  },
87573
+ "sceneMonitor.listScenesBatch": {
87574
+ capName: "scene-monitor",
87575
+ capScope: "device",
87576
+ addonId: null,
87577
+ access: "view"
87578
+ },
87224
87579
  "sceneMonitor.recheckNow": {
87225
87580
  capName: "scene-monitor",
87226
87581
  capScope: "device",
@@ -87575,12 +87930,36 @@ Object.freeze({
87575
87930
  addonId: null,
87576
87931
  access: "create"
87577
87932
  },
87933
+ "storageMigration.cleanupCancel": {
87934
+ capName: "storage-migration",
87935
+ capScope: "system",
87936
+ addonId: null,
87937
+ access: "create"
87938
+ },
87939
+ "storageMigration.cleanupStart": {
87940
+ capName: "storage-migration",
87941
+ capScope: "system",
87942
+ addonId: null,
87943
+ access: "create"
87944
+ },
87945
+ "storageMigration.cleanupStatus": {
87946
+ capName: "storage-migration",
87947
+ capScope: "system",
87948
+ addonId: null,
87949
+ access: "view"
87950
+ },
87578
87951
  "storageMigration.drain": {
87579
87952
  capName: "storage-migration",
87580
87953
  capScope: "system",
87581
87954
  addonId: null,
87582
87955
  access: "create"
87583
87956
  },
87957
+ "storageMigration.history": {
87958
+ capName: "storage-migration",
87959
+ capScope: "system",
87960
+ addonId: null,
87961
+ access: "view"
87962
+ },
87584
87963
  "storageMigration.movers": {
87585
87964
  capName: "storage-migration",
87586
87965
  capScope: "system",
@@ -88577,6 +88956,12 @@ Object.freeze({
88577
88956
  addonId: null,
88578
88957
  access: "view"
88579
88958
  },
88959
+ "zoneAnalytics.getCurrentSnapshotBatch": {
88960
+ capName: "zone-analytics",
88961
+ capScope: "device",
88962
+ addonId: null,
88963
+ access: "view"
88964
+ },
88580
88965
  "zoneAnalytics.getUnzonedHistory": {
88581
88966
  capName: "zone-analytics",
88582
88967
  capScope: "device",
@@ -89571,14 +89956,14 @@ Object.freeze({
89571
89956
  form: "single",
89572
89957
  optional: true
89573
89958
  }],
89574
- "pipelineAnalytics.getGroup": [{
89959
+ "pipelineAnalytics.getKeyEvents": [{
89575
89960
  name: "deviceId",
89576
89961
  form: "single",
89577
89962
  optional: false
89578
89963
  }],
89579
- "pipelineAnalytics.getKeyEvents": [{
89580
- name: "deviceId",
89581
- form: "single",
89964
+ "pipelineAnalytics.getKeyEventsBatch": [{
89965
+ name: "deviceIds",
89966
+ form: "array",
89582
89967
  optional: false
89583
89968
  }],
89584
89969
  "pipelineAnalytics.getMotionEvents": [{
@@ -89636,11 +90021,6 @@ Object.freeze({
89636
90021
  form: "single",
89637
90022
  optional: false
89638
90023
  }],
89639
- "pipelineAnalytics.listGroups": [{
89640
- name: "deviceIds",
89641
- form: "array",
89642
- optional: false
89643
- }],
89644
90024
  "pipelineAnalytics.listOpsLog": [{
89645
90025
  name: "deviceId",
89646
90026
  form: "single",
@@ -89686,6 +90066,11 @@ Object.freeze({
89686
90066
  form: "single",
89687
90067
  optional: true
89688
90068
  }],
90069
+ "pipelineAnalytics.reclaimDebugMedia": [{
90070
+ name: "deviceIds",
90071
+ form: "array",
90072
+ optional: true
90073
+ }],
89689
90074
  "pipelineAnalytics.reconcileFromDisk": [{
89690
90075
  name: "deviceId",
89691
90076
  form: "single",
@@ -90021,6 +90406,11 @@ Object.freeze({
90021
90406
  form: "single",
90022
90407
  optional: false
90023
90408
  }],
90409
+ "recording.reconcileLedgerAgainstDisk": [{
90410
+ name: "deviceId",
90411
+ form: "single",
90412
+ optional: true
90413
+ }],
90024
90414
  "recording.relocateFootage": [{
90025
90415
  name: "deviceId",
90026
90416
  form: "single",
@@ -90046,6 +90436,11 @@ Object.freeze({
90046
90436
  form: "single",
90047
90437
  optional: false
90048
90438
  }],
90439
+ "recording.setDevicePlacement": [{
90440
+ name: "deviceId",
90441
+ form: "single",
90442
+ optional: false
90443
+ }],
90049
90444
  "recording.startStorageMigrationMove": [{
90050
90445
  name: "deviceId",
90051
90446
  form: "single",
@@ -90086,6 +90481,11 @@ Object.freeze({
90086
90481
  form: "single",
90087
90482
  optional: false
90088
90483
  }],
90484
+ "sceneMonitor.listScenesBatch": [{
90485
+ name: "deviceIds",
90486
+ form: "array",
90487
+ optional: false
90488
+ }],
90089
90489
  "sceneMonitor.recheckNow": [{
90090
90490
  name: "deviceId",
90091
90491
  form: "single",
@@ -90347,6 +90747,11 @@ Object.freeze({
90347
90747
  form: "single",
90348
90748
  optional: false
90349
90749
  }],
90750
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
90751
+ name: "deviceIds",
90752
+ form: "array",
90753
+ optional: false
90754
+ }],
90350
90755
  "zoneAnalytics.getUnzonedHistory": [{
90351
90756
  name: "deviceId",
90352
90757
  form: "single",