@camstack/addon-smtp-nodemailer 1.2.47 → 1.2.49

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.
@@ -8037,6 +8037,20 @@ var RelocateJobSchema = object({
8037
8037
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8038
8038
  */
8039
8039
  rowsReconciled: number().int().nonnegative().optional(),
8040
+ /**
8041
+ * Rows this run FORGOT because the file they name is not on disk.
8042
+ *
8043
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8044
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8045
+ * and the durable row is dropped through the same channel eviction uses. It
8046
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8047
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8048
+ * the same failure as one that quietly skips them (D295).
8049
+ *
8050
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8051
+ * ledger claimed 5.65 GB of footage that no longer existed.
8052
+ */
8053
+ rowsForgotten: number().int().nonnegative().optional(),
8040
8054
  startedAt: number(),
8041
8055
  finishedAt: number().nullable(),
8042
8056
  error: string().nullable()
@@ -8101,6 +8115,13 @@ var MediaRelocateModeSchema = _enum([
8101
8115
  ]);
8102
8116
  var RelocateMediaInputSchema = object({
8103
8117
  toLocationId: string(),
8118
+ /**
8119
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8120
+ * every row that is not already on `toLocationId` (the historical
8121
+ * behaviour). A named source is what a from→to migration needs: without it
8122
+ * "move events off disk 2" also emptied disk 1.
8123
+ */
8124
+ fromLocationId: string().optional(),
8104
8125
  throttleMbps: number().min(1).max(1e3).optional(),
8105
8126
  /** Omitted = `move`, the pre-existing behaviour. */
8106
8127
  mode: MediaRelocateModeSchema.optional()
@@ -8168,6 +8189,19 @@ var StorageMigrationDestinationsSchema = object({
8168
8189
  galleryMedia: string().min(1).optional()
8169
8190
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8170
8191
  /**
8192
+ * Optional named source per class. Omitted = the class's current default
8193
+ * (the historical behaviour). A named source that is NOT the default is a
8194
+ * drain of that disk: bytes move, the default stays, and the source is
8195
+ * disabled when the move finishes.
8196
+ */
8197
+ var StorageMigrationSourcesSchema = object({
8198
+ recordings: string().min(1).optional(),
8199
+ recordingsLow: string().min(1).optional(),
8200
+ eventMedia: string().min(1).optional(),
8201
+ backups: string().min(1).optional(),
8202
+ galleryMedia: string().min(1).optional()
8203
+ }).optional();
8204
+ /**
8171
8205
  * How a migration sequences the cutover against the byte move.
8172
8206
  *
8173
8207
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8189,6 +8223,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8189
8223
  /** Shared input for planning and starting an orchestrated storage migration. */
8190
8224
  var StorageMigrationInputSchema = object({
8191
8225
  destinations: StorageMigrationDestinationsSchema,
8226
+ /** Omitted = each class's current default. */
8227
+ sources: StorageMigrationSourcesSchema,
8192
8228
  throttleMbps: number().min(1).max(1e3).optional(),
8193
8229
  /** Omitted = `blocking`, which stays the default. */
8194
8230
  mode: StorageMigrationModeSchema.optional()
@@ -8268,6 +8304,13 @@ var StorageMigrationMoveSchema = object({
8268
8304
  storageClass: StorageMigrationClassSchema,
8269
8305
  fromLocationId: string(),
8270
8306
  toLocationId: string(),
8307
+ /**
8308
+ * True when `from` was NOT the class default at plan time. The move still
8309
+ * copies bytes, but the default is left alone and the source is disabled
8310
+ * once the copy verifies. Absent on jobs planned before this field existed
8311
+ * — those jobs always repointed, which is `false`.
8312
+ */
8313
+ freezeSource: boolean().optional(),
8271
8314
  moverJobId: string().nullable(),
8272
8315
  state: RelocateJobStateSchema.nullable(),
8273
8316
  error: string().nullable(),
@@ -8281,6 +8324,7 @@ var StorageMigrationJobSchema = object({
8281
8324
  * can tell a seconds-long cutover from a thirty-hour one. */
8282
8325
  mode: StorageMigrationModeSchema,
8283
8326
  destinations: StorageMigrationDestinationsSchema,
8327
+ sources: StorageMigrationSourcesSchema,
8284
8328
  throttleMbps: number(),
8285
8329
  moves: array(StorageMigrationMoveSchema),
8286
8330
  pauseLeaseId: string().nullable(),
@@ -8306,6 +8350,7 @@ var StorageMigrationFindingSchema = object({
8306
8350
  });
8307
8351
  var StorageMigrationPlanSchema = object({
8308
8352
  destinations: StorageMigrationDestinationsSchema,
8353
+ sources: StorageMigrationSourcesSchema,
8309
8354
  /** The mode this plan was built for. A plan is only valid for its mode: the
8310
8355
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8311
8356
  * it. */
@@ -8313,7 +8358,8 @@ var StorageMigrationPlanSchema = object({
8313
8358
  moves: array(object({
8314
8359
  storageClass: StorageMigrationClassSchema,
8315
8360
  fromLocationId: string(),
8316
- toLocationId: string()
8361
+ toLocationId: string(),
8362
+ freezeSource: boolean().optional()
8317
8363
  })),
8318
8364
  findings: array(StorageMigrationFindingSchema)
8319
8365
  });
@@ -8400,16 +8446,142 @@ var RelocateResidueSchema = object({
8400
8446
  segments: number().int().nonnegative(),
8401
8447
  bytes: number().int().nonnegative()
8402
8448
  }).nullable();
8449
+ /**
8450
+ * Ask one location whether its durable hour rows describe the disk — the walk
8451
+ * (D319).
8452
+ *
8453
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8454
+ * missing tool is the question, and the dry run is how they sanity-check the
8455
+ * destructive run before authorising it.
8456
+ */
8457
+ var LedgerWalkInputSchema = object({
8458
+ locationId: string().min(1),
8459
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8460
+ apply: boolean().optional(),
8461
+ /** Narrow to one camera. */
8462
+ deviceId: number().int().positive().optional(),
8463
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8464
+ profiles: array(string().min(1)).optional()
8465
+ });
8466
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8467
+ var LedgerWalkRefusalSchema = _enum([
8468
+ "location-unknown",
8469
+ "source-writable",
8470
+ "no-ledger",
8471
+ "archive-unreadable",
8472
+ "anchor-absent",
8473
+ "anchor-unreadable",
8474
+ "anchor-moved"
8475
+ ]);
8476
+ _enum([
8477
+ "live-tail",
8478
+ "listing-error",
8479
+ "path-mismatch",
8480
+ "durable-refused"
8481
+ ]);
8482
+ /** Every skip reason, always present, always a number — so a reason that never
8483
+ * fired reports as zero rather than absent and the report shape is constant
8484
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8485
+ var LedgerWalkSkipCountsSchema = object({
8486
+ "live-tail": number().int().nonnegative(),
8487
+ "listing-error": number().int().nonnegative(),
8488
+ "path-mismatch": number().int().nonnegative(),
8489
+ "durable-refused": number().int().nonnegative()
8490
+ });
8491
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8492
+ var LedgerWalkDeviceReportSchema = object({
8493
+ deviceId: number().int(),
8494
+ hoursWalked: number().int().nonnegative(),
8495
+ hoursMissing: number().int().nonnegative(),
8496
+ ghostSegments: number().int().nonnegative(),
8497
+ ghostBytes: number().int().nonnegative(),
8498
+ forgottenSegments: number().int().nonnegative(),
8499
+ orphanFiles: number().int().nonnegative()
8500
+ });
8501
+ /**
8502
+ * What one walk claimed, listed, found and (only when armed) forgot.
8503
+ *
8504
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8505
+ * walk that saw a fraction of the location is visible in its own report rather
8506
+ * than in the absence of one.
8507
+ */
8508
+ var LedgerWalkReportSchema = object({
8509
+ locationId: string(),
8510
+ applied: boolean(),
8511
+ refused: LedgerWalkRefusalSchema.nullable(),
8512
+ archiveSegments: number().int().nonnegative().nullable(),
8513
+ archiveBytes: number().int().nonnegative().nullable(),
8514
+ hoursClaimed: number().int().nonnegative(),
8515
+ hoursWalked: number().int().nonnegative(),
8516
+ hoursMissing: number().int().nonnegative(),
8517
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8518
+ listings: number().int().nonnegative(),
8519
+ segmentsClaimed: number().int().nonnegative(),
8520
+ ghostSegments: number().int().nonnegative(),
8521
+ ghostBytes: number().int().nonnegative(),
8522
+ ghostHoursWhole: number().int().nonnegative(),
8523
+ forgottenSegments: number().int().nonnegative(),
8524
+ forgottenBytes: number().int().nonnegative(),
8525
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8526
+ orphanFiles: number().int().nonnegative(),
8527
+ orphanSample: array(string()).readonly(),
8528
+ hoursSkipped: number().int().nonnegative(),
8529
+ skippedByReason: LedgerWalkSkipCountsSchema,
8530
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8531
+ bounded: boolean(),
8532
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8533
+ });
8403
8534
  /** How many rows a media pass would still act on against a given target — the
8404
8535
  * media lane's denominator AND its residue, from ONE derivation so the two can
8405
8536
  * never disagree. `null` = the count could not be taken. */
8406
8537
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8407
8538
  var RelocatableMediaCountInputSchema = object({
8408
8539
  toLocationId: string().min(1),
8540
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8541
+ fromLocationId: string().optional(),
8409
8542
  /** Omitted = `move`. */
8410
8543
  mode: MediaRelocateModeSchema.optional()
8411
8544
  });
8412
8545
  /**
8546
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8547
+ * ghost ledger entries on frozen footage locations.
8548
+ *
8549
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8550
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8551
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8552
+ * with no operator-visible status.
8553
+ */
8554
+ var StorageCleanupPhaseSchema = _enum([
8555
+ "orphans",
8556
+ "debug-media",
8557
+ "ghost-ledger",
8558
+ "done",
8559
+ "failed",
8560
+ "cancelled"
8561
+ ]);
8562
+ var StorageCleanupInputSchema = object({
8563
+ /** Also walk motion stills / track filmstrips. Off by default. */
8564
+ includeDebugMedia: boolean().optional() });
8565
+ var StorageCleanupJobSchema = object({
8566
+ jobId: string(),
8567
+ phase: StorageCleanupPhaseSchema,
8568
+ includeDebugMedia: boolean(),
8569
+ orphansReclaimed: number().int().nonnegative(),
8570
+ orphanBytesReclaimed: number().int().nonnegative(),
8571
+ debugMediaReclaimed: number().int().nonnegative(),
8572
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8573
+ ghostsForgotten: number().int().nonnegative(),
8574
+ ghostBytesForgotten: number().int().nonnegative(),
8575
+ /** Short operator-facing line: current collection, pass, or location. */
8576
+ detail: string().nullable(),
8577
+ cancelRequested: boolean(),
8578
+ startedAt: number(),
8579
+ updatedAt: number(),
8580
+ finishedAt: number().nullable(),
8581
+ error: string().nullable()
8582
+ });
8583
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8584
+ /**
8413
8585
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8414
8586
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8415
8587
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8437,11 +8609,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8437
8609
  * The default location for a type uses `id === <type>:default` by
8438
8610
  * convention (the bare type ref like `'backups'` resolves to it).
8439
8611
  *
8440
- * `isSystem: true` marks a location as orchestrator-seeded and
8441
- * undeletable. The bootstrap-installed defaults (one per type) carry
8442
- * this flag; operator-added locations don't. Editing the config of
8443
- * a system location is allowed (path migration, provider swap) but
8444
- * deleting it is rejected at the cap level.
8612
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8613
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8614
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8615
+ * / last-enabled, not on this bit.
8445
8616
  */
8446
8617
  var StorageLocationSchema = object({
8447
8618
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12703,6 +12874,12 @@ method(object({
12703
12874
  }), _void(), {
12704
12875
  kind: "mutation",
12705
12876
  auth: "admin"
12877
+ }), method(object({
12878
+ from: string(),
12879
+ to: string()
12880
+ }), object({ moved: number() }), {
12881
+ kind: "mutation",
12882
+ auth: "admin"
12706
12883
  }), method(object({
12707
12884
  deviceId: number(),
12708
12885
  disabled: boolean()
@@ -18633,53 +18810,15 @@ var RecentTracksPageSchema = object({
18633
18810
  /** Cursor for the next page, or null when this page is the last. */
18634
18811
  nextCursor: string().nullable()
18635
18812
  });
18636
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18637
- var LIST_GROUPS_MAX_LIMIT = 100;
18638
- var AnalyticsGroupRecordSchema = object({
18639
- id: string(),
18640
- deviceId: number().int(),
18641
- openedAt: number().int(),
18642
- closedAt: number().int(),
18643
- timestamp: number().int(),
18644
- memberCount: number().int(),
18645
- memberTrackIds: array(string()).readonly(),
18646
- className: string(),
18647
- classes: array(string()).readonly(),
18648
- /** Relative event-media path, or null when the group has no picture yet. */
18649
- mediaUrl: string().nullable(),
18650
- singleton: boolean()
18651
- });
18652
- var AnalyticsGroupMemberSchema = object({
18653
- trackId: string(),
18654
- deviceId: number().int(),
18655
- className: string(),
18656
- firstSeen: number().int(),
18657
- lastSeen: number().int(),
18658
- mediaUrl: string().nullable()
18659
- });
18660
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18661
- var ListGroupsQueryInput = object({
18662
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18663
- deviceIds: array(number()),
18664
- /** Window lower bound on `closedAt` (inclusive). */
18665
- since: number().optional(),
18666
- /** Window upper bound on `openedAt` (inclusive). */
18667
- until: number().optional(),
18668
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18669
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
18670
- cursor: string().optional()
18671
- });
18672
- var ListGroupsPageSchema = object({
18673
- groups: array(AnalyticsGroupRecordSchema).readonly(),
18674
- nextCursor: string().nullable()
18675
- });
18813
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
18814
+ var KEY_EVENTS_MAX_LIMIT = 200;
18676
18815
  var KeyEventQueryInput = object({
18677
18816
  deviceId: number(),
18678
18817
  /** Window lower bound (track firstSeen ≥ since). */
18679
18818
  since: number(),
18680
18819
  /** Window upper bound (track firstSeen ≤ until). */
18681
18820
  until: number(),
18682
- limit: number().int().min(1).max(200).default(50),
18821
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18683
18822
  /** Drop tracks scoring below this importance. */
18684
18823
  minImportance: number().min(0).max(1).optional(),
18685
18824
  /** Restrict to a single class (e.g. 'person'). */
@@ -18701,6 +18840,32 @@ var KeyEventSchema = object({
18701
18840
  ...TrackFlagFields,
18702
18841
  ...TrackRetrainFields
18703
18842
  });
18843
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
18844
+ var KeyEventBatchQueryInput = object({
18845
+ deviceIds: array(number()).min(1).max(200),
18846
+ since: number(),
18847
+ until: number(),
18848
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
18849
+ * across the set, which would let a busy camera starve a quiet one of its
18850
+ * rows and change what the merged feed contains. */
18851
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18852
+ minImportance: number().min(0).max(1).optional(),
18853
+ classFilter: string().optional()
18854
+ });
18855
+ /**
18856
+ * One camera's key events in a batch answer.
18857
+ *
18858
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
18859
+ * error rather than throwing, so a camera whose store read failed and one with
18860
+ * no events in the window were ALREADY indistinguishable per camera — the
18861
+ * batch does not make that worse, and the row keeps the deviceId the single
18862
+ * method's output never carried (the caller used to stamp it from the fan-out
18863
+ * key, which only worked because there was one query per camera).
18864
+ */
18865
+ var KeyEventsForDeviceSchema = object({
18866
+ deviceId: number(),
18867
+ events: array(KeyEventSchema).readonly()
18868
+ });
18704
18869
  object({
18705
18870
  trackId: string(),
18706
18871
  className: string(),
@@ -18748,9 +18913,7 @@ var TrackCascadeCountsSchema = object({
18748
18913
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18749
18914
  plates: number().int(),
18750
18915
  /** Per-track CLIP search vectors removed (best-effort). */
18751
- embeddings: number().int(),
18752
- /** Group membership + group rows removed with their last member (best-effort). */
18753
- groups: number().int()
18916
+ embeddings: number().int()
18754
18917
  });
18755
18918
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18756
18919
  var DiskReconcileCountsSchema = object({
@@ -18920,6 +19083,47 @@ var RebuildStatusSchema = object({
18920
19083
  /** Present when the pass ended by throwing. */
18921
19084
  error: string().nullable()
18922
19085
  });
19086
+ /**
19087
+ * Acknowledgement that a debug-media reclaim STARTED.
19088
+ *
19089
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19090
+ * it runs detached and this returns immediately. Awaiting it is how the
19091
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19092
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19093
+ */
19094
+ var MediaReclaimStartResultSchema = object({
19095
+ started: boolean(),
19096
+ /** True when a pass was already running; the new request is ignored. */
19097
+ alreadyRunning: boolean()
19098
+ });
19099
+ var MediaReclaimInputSchema = object({
19100
+ mode: _enum(["report", "reclaim"]).default("report"),
19101
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19102
+ deviceIds: array(number().int()).min(1).optional(),
19103
+ restart: boolean().optional(),
19104
+ pageSize: number().int().min(50).max(5e3).optional(),
19105
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19106
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19107
+ maxBytesPerRun: number().int().min(1).optional(),
19108
+ budgetMinutes: number().int().min(1).max(720).optional(),
19109
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19110
+ graceMinutes: number().int().min(1).max(10080).optional()
19111
+ });
19112
+ var MediaReclaimStatusSchema = object({
19113
+ running: boolean(),
19114
+ mode: _enum(["report", "reclaim"]).nullable(),
19115
+ totalExamined: number(),
19116
+ totalEligible: number(),
19117
+ totalReclaimed: number(),
19118
+ totalBytesReclaimed: number(),
19119
+ totalRefused: number(),
19120
+ /** Device+scope windows finished in this pass. */
19121
+ devicesDone: number(),
19122
+ complete: boolean().nullable(),
19123
+ startedAtMs: number().nullable(),
19124
+ finishedAtMs: number().nullable(),
19125
+ error: string().nullable()
19126
+ });
18923
19127
  var ReplayFrameInputSchema = object({
18924
19128
  timestamp: number(),
18925
19129
  frame: PipelineRunResultBridge
@@ -18958,10 +19162,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18958
19162
  * stationary registry). Default false: the timeline lists passages,
18959
19163
  * not parking records (operator decision, 2026-08-15). */
18960
19164
  includeStationary: boolean().optional()
18961
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18962
- deviceId: number(),
18963
- groupId: string().min(1)
18964
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19165
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18965
19166
  kind: "mutation",
18966
19167
  auth: "admin"
18967
19168
  }), 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({
@@ -18970,7 +19171,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18970
19171
  until: number().optional(),
18971
19172
  kinds: array(string()).optional(),
18972
19173
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18973
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19174
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
18974
19175
  deviceId: number(),
18975
19176
  since: number(),
18976
19177
  until: number(),
@@ -19061,6 +19262,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19061
19262
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19062
19263
  kind: "query",
19063
19264
  auth: "admin"
19265
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19266
+ kind: "mutation",
19267
+ auth: "admin"
19268
+ }), method(object({}), MediaReclaimStatusSchema, {
19269
+ kind: "query",
19270
+ auth: "admin"
19064
19271
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19065
19272
  kind: "query",
19066
19273
  auth: "admin"
@@ -21044,7 +21251,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21044
21251
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21045
21252
  kind: "mutation",
21046
21253
  auth: "admin"
21047
- });
21254
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21255
+ kind: "mutation",
21256
+ auth: "admin"
21257
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21258
+ kind: "mutation",
21259
+ auth: "admin"
21260
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21048
21261
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21049
21262
  providerId: string().min(1),
21050
21263
  displayName: string().min(1),
@@ -26422,6 +26635,33 @@ var RecordingRebalanceInputSchema = object({
26422
26635
  minMoveGb: number().min(0).optional()
26423
26636
  });
26424
26637
  /**
26638
+ * Operator-facing placement of one camera onto a recordings location.
26639
+ *
26640
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26641
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26642
+ * currently write — the plan, which may disagree with the pin when Auto.
26643
+ */
26644
+ var RecordingDevicePlacementSchema = object({
26645
+ deviceId: number().int(),
26646
+ profile: string(),
26647
+ locationId: string()
26648
+ });
26649
+ var RecordingDevicePinSchema = object({
26650
+ deviceId: number().int(),
26651
+ /** Recordings-class location this camera is pinned to. */
26652
+ locationId: string()
26653
+ });
26654
+ var RecordingPlacementViewSchema = object({
26655
+ assignments: array(RecordingDevicePlacementSchema),
26656
+ pins: array(RecordingDevicePinSchema),
26657
+ defaultLocations: record(string(), string())
26658
+ });
26659
+ var RecordingSetDevicePlacementInputSchema = object({
26660
+ deviceId: number().int(),
26661
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26662
+ locationId: string().nullable()
26663
+ });
26664
+ /**
26425
26665
  * Result of locating footage at a wall-clock instant for one device/profile.
26426
26666
  * `segment` carries the covering segment's window; `gap` reports the forward
26427
26667
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26635,6 +26875,9 @@ method(object({
26635
26875
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26636
26876
  kind: "query",
26637
26877
  auth: "admin"
26878
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26879
+ kind: "mutation",
26880
+ auth: "admin"
26638
26881
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26639
26882
  kind: "mutation",
26640
26883
  auth: "admin"
@@ -26644,6 +26887,12 @@ method(object({
26644
26887
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26645
26888
  kind: "mutation",
26646
26889
  auth: "admin"
26890
+ }), method(object({}), RecordingPlacementViewSchema, {
26891
+ kind: "query",
26892
+ auth: "admin"
26893
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
26894
+ kind: "mutation",
26895
+ auth: "admin"
26647
26896
  });
26648
26897
  /**
26649
26898
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27026,7 +27275,26 @@ var SceneMonitorStatusSchema = object({
27026
27275
  monitors: array(SceneMonitorSchema),
27027
27276
  lastFetchedAt: number()
27028
27277
  });
27029
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27278
+ /**
27279
+ * One camera's row in a `listScenesBatch` answer.
27280
+ *
27281
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27282
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27283
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27284
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27285
+ * configured. Fanned out per camera the difference was visible — one query
27286
+ * errored while the others resolved — and a batch that returned only the rows
27287
+ * it managed would have destroyed it, silently, by making an unreachable camera
27288
+ * indistinguishable from one that answered `monitors: []`.
27289
+ *
27290
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27291
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27292
+ */
27293
+ var SceneMonitorStatusForDeviceSchema = object({
27294
+ deviceId: number(),
27295
+ status: SceneMonitorStatusSchema.nullable()
27296
+ });
27297
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27030
27298
  deviceId: number(),
27031
27299
  label: string(),
27032
27300
  roi: MaskRectShapeSchema,
@@ -28350,6 +28618,27 @@ var CameraOccupancySnapshotSchema = object({
28350
28618
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28351
28619
  });
28352
28620
  /**
28621
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28622
+ *
28623
+ * THREE outcomes, and the single-camera method could only express two of them
28624
+ * because `snapshot: null` was already spoken for:
28625
+ *
28626
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28627
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28628
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28629
+ * - `read: 'unreadable'` — the owner could not answer for this
28630
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28631
+ *
28632
+ * Collapsing the last two is the failure this field exists to prevent: a
28633
+ * hydration that threw would otherwise render as an empty Stationary section,
28634
+ * which is a definite claim about a camera nobody could read.
28635
+ */
28636
+ var CameraOccupancySnapshotForDeviceSchema = object({
28637
+ deviceId: number(),
28638
+ read: _enum(["read", "unreadable"]),
28639
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28640
+ });
28641
+ /**
28353
28642
  * Time-series resolution. The history methods return one bucket per
28354
28643
  * step over the requested range. Smaller resolutions cost more
28355
28644
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28373,7 +28662,7 @@ var HistoryPointSchema = object({
28373
28662
  /** Object count averaged over the bucket (rounded to nearest integer). */
28374
28663
  count: number().int().nonnegative()
28375
28664
  });
28376
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28665
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28377
28666
  deviceId: number(),
28378
28667
  zoneId: string(),
28379
28668
  className: string().optional()
@@ -29974,6 +30263,12 @@ Object.freeze({
29974
30263
  addonId: null,
29975
30264
  access: "delete"
29976
30265
  },
30266
+ "deviceManager.renameLocation": {
30267
+ capName: "device-manager",
30268
+ capScope: "system",
30269
+ addonId: null,
30270
+ access: "create"
30271
+ },
29977
30272
  "deviceManager.runDeviceAction": {
29978
30273
  capName: "device-manager",
29979
30274
  capScope: "system",
@@ -31666,13 +31961,19 @@ Object.freeze({
31666
31961
  addonId: null,
31667
31962
  access: "view"
31668
31963
  },
31669
- "pipelineAnalytics.getGroup": {
31964
+ "pipelineAnalytics.getKeyEvents": {
31670
31965
  capName: "pipeline-analytics",
31671
31966
  capScope: "device",
31672
31967
  addonId: null,
31673
31968
  access: "view"
31674
31969
  },
31675
- "pipelineAnalytics.getKeyEvents": {
31970
+ "pipelineAnalytics.getKeyEventsBatch": {
31971
+ capName: "pipeline-analytics",
31972
+ capScope: "device",
31973
+ addonId: null,
31974
+ access: "view"
31975
+ },
31976
+ "pipelineAnalytics.getMediaReclaimStatus": {
31676
31977
  capName: "pipeline-analytics",
31677
31978
  capScope: "device",
31678
31979
  addonId: null,
@@ -31762,12 +32063,6 @@ Object.freeze({
31762
32063
  addonId: null,
31763
32064
  access: "view"
31764
32065
  },
31765
- "pipelineAnalytics.listGroups": {
31766
- capName: "pipeline-analytics",
31767
- capScope: "device",
31768
- addonId: null,
31769
- access: "view"
31770
- },
31771
32066
  "pipelineAnalytics.listOpsLog": {
31772
32067
  capName: "pipeline-analytics",
31773
32068
  capScope: "device",
@@ -31852,6 +32147,12 @@ Object.freeze({
31852
32147
  addonId: null,
31853
32148
  access: "create"
31854
32149
  },
32150
+ "pipelineAnalytics.reclaimDebugMedia": {
32151
+ capName: "pipeline-analytics",
32152
+ capScope: "device",
32153
+ addonId: null,
32154
+ access: "create"
32155
+ },
31855
32156
  "pipelineAnalytics.reconcileFromDisk": {
31856
32157
  capName: "pipeline-analytics",
31857
32158
  capScope: "device",
@@ -32776,6 +33077,12 @@ Object.freeze({
32776
33077
  addonId: null,
32777
33078
  access: "view"
32778
33079
  },
33080
+ "recording.getPlacement": {
33081
+ capName: "recording",
33082
+ capScope: "system",
33083
+ addonId: null,
33084
+ access: "view"
33085
+ },
32779
33086
  "recording.getPlaybackManifest": {
32780
33087
  capName: "recording",
32781
33088
  capScope: "system",
@@ -32854,6 +33161,12 @@ Object.freeze({
32854
33161
  addonId: null,
32855
33162
  access: "view"
32856
33163
  },
33164
+ "recording.reconcileLedgerAgainstDisk": {
33165
+ capName: "recording",
33166
+ capScope: "system",
33167
+ addonId: null,
33168
+ access: "create"
33169
+ },
32857
33170
  "recording.refreshStorageLocationsForMigration": {
32858
33171
  capName: "recording",
32859
33172
  capScope: "system",
@@ -32896,6 +33209,12 @@ Object.freeze({
32896
33209
  addonId: null,
32897
33210
  access: "create"
32898
33211
  },
33212
+ "recording.setDevicePlacement": {
33213
+ capName: "recording",
33214
+ capScope: "system",
33215
+ addonId: null,
33216
+ access: "create"
33217
+ },
32899
33218
  "recording.startStorageMigrationMove": {
32900
33219
  capName: "recording",
32901
33220
  capScope: "system",
@@ -32980,6 +33299,12 @@ Object.freeze({
32980
33299
  addonId: null,
32981
33300
  access: "view"
32982
33301
  },
33302
+ "sceneMonitor.listScenesBatch": {
33303
+ capName: "scene-monitor",
33304
+ capScope: "device",
33305
+ addonId: null,
33306
+ access: "view"
33307
+ },
32983
33308
  "sceneMonitor.recheckNow": {
32984
33309
  capName: "scene-monitor",
32985
33310
  capScope: "device",
@@ -33334,12 +33659,36 @@ Object.freeze({
33334
33659
  addonId: null,
33335
33660
  access: "create"
33336
33661
  },
33662
+ "storageMigration.cleanupCancel": {
33663
+ capName: "storage-migration",
33664
+ capScope: "system",
33665
+ addonId: null,
33666
+ access: "create"
33667
+ },
33668
+ "storageMigration.cleanupStart": {
33669
+ capName: "storage-migration",
33670
+ capScope: "system",
33671
+ addonId: null,
33672
+ access: "create"
33673
+ },
33674
+ "storageMigration.cleanupStatus": {
33675
+ capName: "storage-migration",
33676
+ capScope: "system",
33677
+ addonId: null,
33678
+ access: "view"
33679
+ },
33337
33680
  "storageMigration.drain": {
33338
33681
  capName: "storage-migration",
33339
33682
  capScope: "system",
33340
33683
  addonId: null,
33341
33684
  access: "create"
33342
33685
  },
33686
+ "storageMigration.history": {
33687
+ capName: "storage-migration",
33688
+ capScope: "system",
33689
+ addonId: null,
33690
+ access: "view"
33691
+ },
33343
33692
  "storageMigration.movers": {
33344
33693
  capName: "storage-migration",
33345
33694
  capScope: "system",
@@ -34336,6 +34685,12 @@ Object.freeze({
34336
34685
  addonId: null,
34337
34686
  access: "view"
34338
34687
  },
34688
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34689
+ capName: "zone-analytics",
34690
+ capScope: "device",
34691
+ addonId: null,
34692
+ access: "view"
34693
+ },
34339
34694
  "zoneAnalytics.getUnzonedHistory": {
34340
34695
  capName: "zone-analytics",
34341
34696
  capScope: "device",
@@ -35330,14 +35685,14 @@ Object.freeze({
35330
35685
  form: "single",
35331
35686
  optional: true
35332
35687
  }],
35333
- "pipelineAnalytics.getGroup": [{
35688
+ "pipelineAnalytics.getKeyEvents": [{
35334
35689
  name: "deviceId",
35335
35690
  form: "single",
35336
35691
  optional: false
35337
35692
  }],
35338
- "pipelineAnalytics.getKeyEvents": [{
35339
- name: "deviceId",
35340
- form: "single",
35693
+ "pipelineAnalytics.getKeyEventsBatch": [{
35694
+ name: "deviceIds",
35695
+ form: "array",
35341
35696
  optional: false
35342
35697
  }],
35343
35698
  "pipelineAnalytics.getMotionEvents": [{
@@ -35395,11 +35750,6 @@ Object.freeze({
35395
35750
  form: "single",
35396
35751
  optional: false
35397
35752
  }],
35398
- "pipelineAnalytics.listGroups": [{
35399
- name: "deviceIds",
35400
- form: "array",
35401
- optional: false
35402
- }],
35403
35753
  "pipelineAnalytics.listOpsLog": [{
35404
35754
  name: "deviceId",
35405
35755
  form: "single",
@@ -35445,6 +35795,11 @@ Object.freeze({
35445
35795
  form: "single",
35446
35796
  optional: true
35447
35797
  }],
35798
+ "pipelineAnalytics.reclaimDebugMedia": [{
35799
+ name: "deviceIds",
35800
+ form: "array",
35801
+ optional: true
35802
+ }],
35448
35803
  "pipelineAnalytics.reconcileFromDisk": [{
35449
35804
  name: "deviceId",
35450
35805
  form: "single",
@@ -35780,6 +36135,11 @@ Object.freeze({
35780
36135
  form: "single",
35781
36136
  optional: false
35782
36137
  }],
36138
+ "recording.reconcileLedgerAgainstDisk": [{
36139
+ name: "deviceId",
36140
+ form: "single",
36141
+ optional: true
36142
+ }],
35783
36143
  "recording.relocateFootage": [{
35784
36144
  name: "deviceId",
35785
36145
  form: "single",
@@ -35805,6 +36165,11 @@ Object.freeze({
35805
36165
  form: "single",
35806
36166
  optional: false
35807
36167
  }],
36168
+ "recording.setDevicePlacement": [{
36169
+ name: "deviceId",
36170
+ form: "single",
36171
+ optional: false
36172
+ }],
35808
36173
  "recording.startStorageMigrationMove": [{
35809
36174
  name: "deviceId",
35810
36175
  form: "single",
@@ -35845,6 +36210,11 @@ Object.freeze({
35845
36210
  form: "single",
35846
36211
  optional: false
35847
36212
  }],
36213
+ "sceneMonitor.listScenesBatch": [{
36214
+ name: "deviceIds",
36215
+ form: "array",
36216
+ optional: false
36217
+ }],
35848
36218
  "sceneMonitor.recheckNow": [{
35849
36219
  name: "deviceId",
35850
36220
  form: "single",
@@ -36106,6 +36476,11 @@ Object.freeze({
36106
36476
  form: "single",
36107
36477
  optional: false
36108
36478
  }],
36479
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36480
+ name: "deviceIds",
36481
+ form: "array",
36482
+ optional: false
36483
+ }],
36109
36484
  "zoneAnalytics.getUnzonedHistory": [{
36110
36485
  name: "deviceId",
36111
36486
  form: "single",