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