@camstack/addon-import-alexa 0.2.47 → 0.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.
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
@@ -8176,6 +8176,20 @@ var RelocateJobSchema = object({
8176
8176
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8177
8177
  */
8178
8178
  rowsReconciled: number().int().nonnegative().optional(),
8179
+ /**
8180
+ * Rows this run FORGOT because the file they name is not on disk.
8181
+ *
8182
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8183
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8184
+ * and the durable row is dropped through the same channel eviction uses. It
8185
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8186
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8187
+ * the same failure as one that quietly skips them (D295).
8188
+ *
8189
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8190
+ * ledger claimed 5.65 GB of footage that no longer existed.
8191
+ */
8192
+ rowsForgotten: number().int().nonnegative().optional(),
8179
8193
  startedAt: number(),
8180
8194
  finishedAt: number().nullable(),
8181
8195
  error: string().nullable()
@@ -8240,6 +8254,13 @@ var MediaRelocateModeSchema = _enum([
8240
8254
  ]);
8241
8255
  var RelocateMediaInputSchema = object({
8242
8256
  toLocationId: string(),
8257
+ /**
8258
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8259
+ * every row that is not already on `toLocationId` (the historical
8260
+ * behaviour). A named source is what a from→to migration needs: without it
8261
+ * "move events off disk 2" also emptied disk 1.
8262
+ */
8263
+ fromLocationId: string().optional(),
8243
8264
  throttleMbps: number().min(1).max(1e3).optional(),
8244
8265
  /** Omitted = `move`, the pre-existing behaviour. */
8245
8266
  mode: MediaRelocateModeSchema.optional()
@@ -8307,6 +8328,19 @@ var StorageMigrationDestinationsSchema = object({
8307
8328
  galleryMedia: string().min(1).optional()
8308
8329
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8309
8330
  /**
8331
+ * Optional named source per class. Omitted = the class's current default
8332
+ * (the historical behaviour). A named source that is NOT the default is a
8333
+ * drain of that disk: bytes move, the default stays, and the source is
8334
+ * disabled when the move finishes.
8335
+ */
8336
+ var StorageMigrationSourcesSchema = object({
8337
+ recordings: string().min(1).optional(),
8338
+ recordingsLow: string().min(1).optional(),
8339
+ eventMedia: string().min(1).optional(),
8340
+ backups: string().min(1).optional(),
8341
+ galleryMedia: string().min(1).optional()
8342
+ }).optional();
8343
+ /**
8310
8344
  * How a migration sequences the cutover against the byte move.
8311
8345
  *
8312
8346
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8328,6 +8362,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8328
8362
  /** Shared input for planning and starting an orchestrated storage migration. */
8329
8363
  var StorageMigrationInputSchema = object({
8330
8364
  destinations: StorageMigrationDestinationsSchema,
8365
+ /** Omitted = each class's current default. */
8366
+ sources: StorageMigrationSourcesSchema,
8331
8367
  throttleMbps: number().min(1).max(1e3).optional(),
8332
8368
  /** Omitted = `blocking`, which stays the default. */
8333
8369
  mode: StorageMigrationModeSchema.optional()
@@ -8407,6 +8443,13 @@ var StorageMigrationMoveSchema = object({
8407
8443
  storageClass: StorageMigrationClassSchema,
8408
8444
  fromLocationId: string(),
8409
8445
  toLocationId: string(),
8446
+ /**
8447
+ * True when `from` was NOT the class default at plan time. The move still
8448
+ * copies bytes, but the default is left alone and the source is disabled
8449
+ * once the copy verifies. Absent on jobs planned before this field existed
8450
+ * — those jobs always repointed, which is `false`.
8451
+ */
8452
+ freezeSource: boolean().optional(),
8410
8453
  moverJobId: string().nullable(),
8411
8454
  state: RelocateJobStateSchema.nullable(),
8412
8455
  error: string().nullable(),
@@ -8420,6 +8463,7 @@ var StorageMigrationJobSchema = object({
8420
8463
  * can tell a seconds-long cutover from a thirty-hour one. */
8421
8464
  mode: StorageMigrationModeSchema,
8422
8465
  destinations: StorageMigrationDestinationsSchema,
8466
+ sources: StorageMigrationSourcesSchema,
8423
8467
  throttleMbps: number(),
8424
8468
  moves: array(StorageMigrationMoveSchema),
8425
8469
  pauseLeaseId: string().nullable(),
@@ -8445,6 +8489,7 @@ var StorageMigrationFindingSchema = object({
8445
8489
  });
8446
8490
  var StorageMigrationPlanSchema = object({
8447
8491
  destinations: StorageMigrationDestinationsSchema,
8492
+ sources: StorageMigrationSourcesSchema,
8448
8493
  /** The mode this plan was built for. A plan is only valid for its mode: the
8449
8494
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8450
8495
  * it. */
@@ -8452,7 +8497,8 @@ var StorageMigrationPlanSchema = object({
8452
8497
  moves: array(object({
8453
8498
  storageClass: StorageMigrationClassSchema,
8454
8499
  fromLocationId: string(),
8455
- toLocationId: string()
8500
+ toLocationId: string(),
8501
+ freezeSource: boolean().optional()
8456
8502
  })),
8457
8503
  findings: array(StorageMigrationFindingSchema)
8458
8504
  });
@@ -8539,16 +8585,142 @@ var RelocateResidueSchema = object({
8539
8585
  segments: number().int().nonnegative(),
8540
8586
  bytes: number().int().nonnegative()
8541
8587
  }).nullable();
8588
+ /**
8589
+ * Ask one location whether its durable hour rows describe the disk — the walk
8590
+ * (D319).
8591
+ *
8592
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8593
+ * missing tool is the question, and the dry run is how they sanity-check the
8594
+ * destructive run before authorising it.
8595
+ */
8596
+ var LedgerWalkInputSchema = object({
8597
+ locationId: string().min(1),
8598
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8599
+ apply: boolean().optional(),
8600
+ /** Narrow to one camera. */
8601
+ deviceId: number().int().positive().optional(),
8602
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8603
+ profiles: array(string().min(1)).optional()
8604
+ });
8605
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8606
+ var LedgerWalkRefusalSchema = _enum([
8607
+ "location-unknown",
8608
+ "source-writable",
8609
+ "no-ledger",
8610
+ "archive-unreadable",
8611
+ "anchor-absent",
8612
+ "anchor-unreadable",
8613
+ "anchor-moved"
8614
+ ]);
8615
+ _enum([
8616
+ "live-tail",
8617
+ "listing-error",
8618
+ "path-mismatch",
8619
+ "durable-refused"
8620
+ ]);
8621
+ /** Every skip reason, always present, always a number — so a reason that never
8622
+ * fired reports as zero rather than absent and the report shape is constant
8623
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8624
+ var LedgerWalkSkipCountsSchema = object({
8625
+ "live-tail": number().int().nonnegative(),
8626
+ "listing-error": number().int().nonnegative(),
8627
+ "path-mismatch": number().int().nonnegative(),
8628
+ "durable-refused": number().int().nonnegative()
8629
+ });
8630
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8631
+ var LedgerWalkDeviceReportSchema = object({
8632
+ deviceId: number().int(),
8633
+ hoursWalked: number().int().nonnegative(),
8634
+ hoursMissing: number().int().nonnegative(),
8635
+ ghostSegments: number().int().nonnegative(),
8636
+ ghostBytes: number().int().nonnegative(),
8637
+ forgottenSegments: number().int().nonnegative(),
8638
+ orphanFiles: number().int().nonnegative()
8639
+ });
8640
+ /**
8641
+ * What one walk claimed, listed, found and (only when armed) forgot.
8642
+ *
8643
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8644
+ * walk that saw a fraction of the location is visible in its own report rather
8645
+ * than in the absence of one.
8646
+ */
8647
+ var LedgerWalkReportSchema = object({
8648
+ locationId: string(),
8649
+ applied: boolean(),
8650
+ refused: LedgerWalkRefusalSchema.nullable(),
8651
+ archiveSegments: number().int().nonnegative().nullable(),
8652
+ archiveBytes: number().int().nonnegative().nullable(),
8653
+ hoursClaimed: number().int().nonnegative(),
8654
+ hoursWalked: number().int().nonnegative(),
8655
+ hoursMissing: number().int().nonnegative(),
8656
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8657
+ listings: number().int().nonnegative(),
8658
+ segmentsClaimed: number().int().nonnegative(),
8659
+ ghostSegments: number().int().nonnegative(),
8660
+ ghostBytes: number().int().nonnegative(),
8661
+ ghostHoursWhole: number().int().nonnegative(),
8662
+ forgottenSegments: number().int().nonnegative(),
8663
+ forgottenBytes: number().int().nonnegative(),
8664
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8665
+ orphanFiles: number().int().nonnegative(),
8666
+ orphanSample: array(string()).readonly(),
8667
+ hoursSkipped: number().int().nonnegative(),
8668
+ skippedByReason: LedgerWalkSkipCountsSchema,
8669
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8670
+ bounded: boolean(),
8671
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8672
+ });
8542
8673
  /** How many rows a media pass would still act on against a given target — the
8543
8674
  * media lane's denominator AND its residue, from ONE derivation so the two can
8544
8675
  * never disagree. `null` = the count could not be taken. */
8545
8676
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8546
8677
  var RelocatableMediaCountInputSchema = object({
8547
8678
  toLocationId: string().min(1),
8679
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8680
+ fromLocationId: string().optional(),
8548
8681
  /** Omitted = `move`. */
8549
8682
  mode: MediaRelocateModeSchema.optional()
8550
8683
  });
8551
8684
  /**
8685
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8686
+ * ghost ledger entries on frozen footage locations.
8687
+ *
8688
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8689
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8690
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8691
+ * with no operator-visible status.
8692
+ */
8693
+ var StorageCleanupPhaseSchema = _enum([
8694
+ "orphans",
8695
+ "debug-media",
8696
+ "ghost-ledger",
8697
+ "done",
8698
+ "failed",
8699
+ "cancelled"
8700
+ ]);
8701
+ var StorageCleanupInputSchema = object({
8702
+ /** Also walk motion stills / track filmstrips. Off by default. */
8703
+ includeDebugMedia: boolean().optional() });
8704
+ var StorageCleanupJobSchema = object({
8705
+ jobId: string(),
8706
+ phase: StorageCleanupPhaseSchema,
8707
+ includeDebugMedia: boolean(),
8708
+ orphansReclaimed: number().int().nonnegative(),
8709
+ orphanBytesReclaimed: number().int().nonnegative(),
8710
+ debugMediaReclaimed: number().int().nonnegative(),
8711
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8712
+ ghostsForgotten: number().int().nonnegative(),
8713
+ ghostBytesForgotten: number().int().nonnegative(),
8714
+ /** Short operator-facing line: current collection, pass, or location. */
8715
+ detail: string().nullable(),
8716
+ cancelRequested: boolean(),
8717
+ startedAt: number(),
8718
+ updatedAt: number(),
8719
+ finishedAt: number().nullable(),
8720
+ error: string().nullable()
8721
+ });
8722
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8723
+ /**
8552
8724
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8553
8725
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8554
8726
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8576,11 +8748,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8576
8748
  * The default location for a type uses `id === <type>:default` by
8577
8749
  * convention (the bare type ref like `'backups'` resolves to it).
8578
8750
  *
8579
- * `isSystem: true` marks a location as orchestrator-seeded and
8580
- * undeletable. The bootstrap-installed defaults (one per type) carry
8581
- * this flag; operator-added locations don't. Editing the config of
8582
- * a system location is allowed (path migration, provider swap) but
8583
- * deleting it is rejected at the cap level.
8751
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8752
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8753
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8754
+ * / last-enabled, not on this bit.
8584
8755
  */
8585
8756
  var StorageLocationSchema = object({
8586
8757
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13191,6 +13362,12 @@ method(object({
13191
13362
  }), _void(), {
13192
13363
  kind: "mutation",
13193
13364
  auth: "admin"
13365
+ }), method(object({
13366
+ from: string(),
13367
+ to: string()
13368
+ }), object({ moved: number() }), {
13369
+ kind: "mutation",
13370
+ auth: "admin"
13194
13371
  }), method(object({
13195
13372
  deviceId: number(),
13196
13373
  disabled: boolean()
@@ -19199,53 +19376,15 @@ var RecentTracksPageSchema = object({
19199
19376
  /** Cursor for the next page, or null when this page is the last. */
19200
19377
  nextCursor: string().nullable()
19201
19378
  });
19202
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19203
- var LIST_GROUPS_MAX_LIMIT = 100;
19204
- var AnalyticsGroupRecordSchema = object({
19205
- id: string(),
19206
- deviceId: number().int(),
19207
- openedAt: number().int(),
19208
- closedAt: number().int(),
19209
- timestamp: number().int(),
19210
- memberCount: number().int(),
19211
- memberTrackIds: array(string()).readonly(),
19212
- className: string(),
19213
- classes: array(string()).readonly(),
19214
- /** Relative event-media path, or null when the group has no picture yet. */
19215
- mediaUrl: string().nullable(),
19216
- singleton: boolean()
19217
- });
19218
- var AnalyticsGroupMemberSchema = object({
19219
- trackId: string(),
19220
- deviceId: number().int(),
19221
- className: string(),
19222
- firstSeen: number().int(),
19223
- lastSeen: number().int(),
19224
- mediaUrl: string().nullable()
19225
- });
19226
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19227
- var ListGroupsQueryInput = object({
19228
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19229
- deviceIds: array(number()),
19230
- /** Window lower bound on `closedAt` (inclusive). */
19231
- since: number().optional(),
19232
- /** Window upper bound on `openedAt` (inclusive). */
19233
- until: number().optional(),
19234
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19235
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19236
- cursor: string().optional()
19237
- });
19238
- var ListGroupsPageSchema = object({
19239
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19240
- nextCursor: string().nullable()
19241
- });
19379
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19380
+ var KEY_EVENTS_MAX_LIMIT = 200;
19242
19381
  var KeyEventQueryInput = object({
19243
19382
  deviceId: number(),
19244
19383
  /** Window lower bound (track firstSeen ≥ since). */
19245
19384
  since: number(),
19246
19385
  /** Window upper bound (track firstSeen ≤ until). */
19247
19386
  until: number(),
19248
- limit: number().int().min(1).max(200).default(50),
19387
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19249
19388
  /** Drop tracks scoring below this importance. */
19250
19389
  minImportance: number().min(0).max(1).optional(),
19251
19390
  /** Restrict to a single class (e.g. 'person'). */
@@ -19267,6 +19406,32 @@ var KeyEventSchema = object({
19267
19406
  ...TrackFlagFields,
19268
19407
  ...TrackRetrainFields
19269
19408
  });
19409
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19410
+ var KeyEventBatchQueryInput = object({
19411
+ deviceIds: array(number()).min(1).max(200),
19412
+ since: number(),
19413
+ until: number(),
19414
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19415
+ * across the set, which would let a busy camera starve a quiet one of its
19416
+ * rows and change what the merged feed contains. */
19417
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19418
+ minImportance: number().min(0).max(1).optional(),
19419
+ classFilter: string().optional()
19420
+ });
19421
+ /**
19422
+ * One camera's key events in a batch answer.
19423
+ *
19424
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19425
+ * error rather than throwing, so a camera whose store read failed and one with
19426
+ * no events in the window were ALREADY indistinguishable per camera — the
19427
+ * batch does not make that worse, and the row keeps the deviceId the single
19428
+ * method's output never carried (the caller used to stamp it from the fan-out
19429
+ * key, which only worked because there was one query per camera).
19430
+ */
19431
+ var KeyEventsForDeviceSchema = object({
19432
+ deviceId: number(),
19433
+ events: array(KeyEventSchema).readonly()
19434
+ });
19270
19435
  object({
19271
19436
  trackId: string(),
19272
19437
  className: string(),
@@ -19314,9 +19479,7 @@ var TrackCascadeCountsSchema = object({
19314
19479
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19315
19480
  plates: number().int(),
19316
19481
  /** Per-track CLIP search vectors removed (best-effort). */
19317
- embeddings: number().int(),
19318
- /** Group membership + group rows removed with their last member (best-effort). */
19319
- groups: number().int()
19482
+ embeddings: number().int()
19320
19483
  });
19321
19484
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19322
19485
  var DiskReconcileCountsSchema = object({
@@ -19486,6 +19649,47 @@ var RebuildStatusSchema = object({
19486
19649
  /** Present when the pass ended by throwing. */
19487
19650
  error: string().nullable()
19488
19651
  });
19652
+ /**
19653
+ * Acknowledgement that a debug-media reclaim STARTED.
19654
+ *
19655
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19656
+ * it runs detached and this returns immediately. Awaiting it is how the
19657
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19658
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19659
+ */
19660
+ var MediaReclaimStartResultSchema = object({
19661
+ started: boolean(),
19662
+ /** True when a pass was already running; the new request is ignored. */
19663
+ alreadyRunning: boolean()
19664
+ });
19665
+ var MediaReclaimInputSchema = object({
19666
+ mode: _enum(["report", "reclaim"]).default("report"),
19667
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19668
+ deviceIds: array(number().int()).min(1).optional(),
19669
+ restart: boolean().optional(),
19670
+ pageSize: number().int().min(50).max(5e3).optional(),
19671
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19672
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19673
+ maxBytesPerRun: number().int().min(1).optional(),
19674
+ budgetMinutes: number().int().min(1).max(720).optional(),
19675
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19676
+ graceMinutes: number().int().min(1).max(10080).optional()
19677
+ });
19678
+ var MediaReclaimStatusSchema = object({
19679
+ running: boolean(),
19680
+ mode: _enum(["report", "reclaim"]).nullable(),
19681
+ totalExamined: number(),
19682
+ totalEligible: number(),
19683
+ totalReclaimed: number(),
19684
+ totalBytesReclaimed: number(),
19685
+ totalRefused: number(),
19686
+ /** Device+scope windows finished in this pass. */
19687
+ devicesDone: number(),
19688
+ complete: boolean().nullable(),
19689
+ startedAtMs: number().nullable(),
19690
+ finishedAtMs: number().nullable(),
19691
+ error: string().nullable()
19692
+ });
19489
19693
  var ReplayFrameInputSchema = object({
19490
19694
  timestamp: number(),
19491
19695
  frame: PipelineRunResultBridge
@@ -19524,10 +19728,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19524
19728
  * stationary registry). Default false: the timeline lists passages,
19525
19729
  * not parking records (operator decision, 2026-08-15). */
19526
19730
  includeStationary: boolean().optional()
19527
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19528
- deviceId: number(),
19529
- groupId: string().min(1)
19530
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19731
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
19531
19732
  kind: "mutation",
19532
19733
  auth: "admin"
19533
19734
  }), 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({
@@ -19536,7 +19737,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19536
19737
  until: number().optional(),
19537
19738
  kinds: array(string()).optional(),
19538
19739
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19539
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19740
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19540
19741
  deviceId: number(),
19541
19742
  since: number(),
19542
19743
  until: number(),
@@ -19627,6 +19828,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19627
19828
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19628
19829
  kind: "query",
19629
19830
  auth: "admin"
19831
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19832
+ kind: "mutation",
19833
+ auth: "admin"
19834
+ }), method(object({}), MediaReclaimStatusSchema, {
19835
+ kind: "query",
19836
+ auth: "admin"
19630
19837
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19631
19838
  kind: "query",
19632
19839
  auth: "admin"
@@ -21597,7 +21804,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21597
21804
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21598
21805
  kind: "mutation",
21599
21806
  auth: "admin"
21600
- });
21807
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21808
+ kind: "mutation",
21809
+ auth: "admin"
21810
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21811
+ kind: "mutation",
21812
+ auth: "admin"
21813
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21601
21814
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21602
21815
  providerId: string().min(1),
21603
21816
  displayName: string().min(1),
@@ -28707,6 +28920,33 @@ var RecordingRebalanceInputSchema = object({
28707
28920
  minMoveGb: number().min(0).optional()
28708
28921
  });
28709
28922
  /**
28923
+ * Operator-facing placement of one camera onto a recordings location.
28924
+ *
28925
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
28926
+ * Auto (the planner may move this camera). `locationId` is where high/mid
28927
+ * currently write — the plan, which may disagree with the pin when Auto.
28928
+ */
28929
+ var RecordingDevicePlacementSchema = object({
28930
+ deviceId: number().int(),
28931
+ profile: string(),
28932
+ locationId: string()
28933
+ });
28934
+ var RecordingDevicePinSchema = object({
28935
+ deviceId: number().int(),
28936
+ /** Recordings-class location this camera is pinned to. */
28937
+ locationId: string()
28938
+ });
28939
+ var RecordingPlacementViewSchema = object({
28940
+ assignments: array(RecordingDevicePlacementSchema),
28941
+ pins: array(RecordingDevicePinSchema),
28942
+ defaultLocations: record(string(), string())
28943
+ });
28944
+ var RecordingSetDevicePlacementInputSchema = object({
28945
+ deviceId: number().int(),
28946
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
28947
+ locationId: string().nullable()
28948
+ });
28949
+ /**
28710
28950
  * Result of locating footage at a wall-clock instant for one device/profile.
28711
28951
  * `segment` carries the covering segment's window; `gap` reports the forward
28712
28952
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -28920,6 +29160,9 @@ method(object({
28920
29160
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
28921
29161
  kind: "query",
28922
29162
  auth: "admin"
29163
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
29164
+ kind: "mutation",
29165
+ auth: "admin"
28923
29166
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
28924
29167
  kind: "mutation",
28925
29168
  auth: "admin"
@@ -28929,6 +29172,12 @@ method(object({
28929
29172
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
28930
29173
  kind: "mutation",
28931
29174
  auth: "admin"
29175
+ }), method(object({}), RecordingPlacementViewSchema, {
29176
+ kind: "query",
29177
+ auth: "admin"
29178
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
29179
+ kind: "mutation",
29180
+ auth: "admin"
28932
29181
  });
28933
29182
  /**
28934
29183
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -29311,6 +29560,25 @@ var SceneMonitorStatusSchema = object({
29311
29560
  monitors: array(SceneMonitorSchema),
29312
29561
  lastFetchedAt: number()
29313
29562
  });
29563
+ /**
29564
+ * One camera's row in a `listScenesBatch` answer.
29565
+ *
29566
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
29567
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
29568
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
29569
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
29570
+ * configured. Fanned out per camera the difference was visible — one query
29571
+ * errored while the others resolved — and a batch that returned only the rows
29572
+ * it managed would have destroyed it, silently, by making an unreachable camera
29573
+ * indistinguishable from one that answered `monitors: []`.
29574
+ *
29575
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
29576
+ * could not be read"; `status.monitors: []` means "read, and it has none".
29577
+ */
29578
+ var SceneMonitorStatusForDeviceSchema = object({
29579
+ deviceId: number(),
29580
+ status: SceneMonitorStatusSchema.nullable()
29581
+ });
29314
29582
  var sceneMonitorCapability = {
29315
29583
  name: "scene-monitor",
29316
29584
  scope: "device",
@@ -29320,6 +29588,22 @@ var sceneMonitorCapability = {
29320
29588
  deviceTypes: [DeviceType.Camera],
29321
29589
  methods: {
29322
29590
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
29591
+ /**
29592
+ * The same answer, for a SET of cameras, in one round trip.
29593
+ *
29594
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
29595
+ * poll behind the push slice. Fanned out client-side that was one query
29596
+ * per camera — 29 round trips through the browser, the hub and the
29597
+ * post-analysis runner every 30 seconds to read an in-memory map the
29598
+ * owner had already merged. The work is unchanged (`statusFor` per
29599
+ * device, all in-process at the owner); what collapses is the transport.
29600
+ *
29601
+ * A camera that cannot answer still gets a row, with `status: null` —
29602
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
29603
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
29604
+ * tell which two are missing, or that any are.
29605
+ */
29606
+ listScenesBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()),
29323
29607
  createScene: method(object({
29324
29608
  deviceId: number(),
29325
29609
  label: string(),
@@ -31155,6 +31439,27 @@ var CameraOccupancySnapshotSchema = object({
31155
31439
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
31156
31440
  });
31157
31441
  /**
31442
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
31443
+ *
31444
+ * THREE outcomes, and the single-camera method could only express two of them
31445
+ * because `snapshot: null` was already spoken for:
31446
+ *
31447
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
31448
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
31449
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
31450
+ * - `read: 'unreadable'` — the owner could not answer for this
31451
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
31452
+ *
31453
+ * Collapsing the last two is the failure this field exists to prevent: a
31454
+ * hydration that threw would otherwise render as an empty Stationary section,
31455
+ * which is a definite claim about a camera nobody could read.
31456
+ */
31457
+ var CameraOccupancySnapshotForDeviceSchema = object({
31458
+ deviceId: number(),
31459
+ read: _enum(["read", "unreadable"]),
31460
+ snapshot: CameraOccupancySnapshotSchema.nullable()
31461
+ });
31462
+ /**
31158
31463
  * Time-series resolution. The history methods return one bucket per
31159
31464
  * step over the requested range. Smaller resolutions cost more
31160
31465
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -31211,6 +31516,20 @@ var zoneAnalyticsCapability = {
31211
31516
  * (no inference result emitted since boot or since binding was
31212
31517
  * activated). */
31213
31518
  getCurrentSnapshot: method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()),
31519
+ /**
31520
+ * The same snapshot, for a SET of cameras, in one round trip.
31521
+ *
31522
+ * The Events page's Stationary section polls this every 15s for every
31523
+ * selected camera. Fanned out client-side that is one query per camera to
31524
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
31525
+ * costs everything. Batched, N transports become one and the per-device
31526
+ * work is unchanged.
31527
+ *
31528
+ * Every requested deviceId gets a row, tagged `read` — see
31529
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
31530
+ * not answer for is `'unreadable'`, never an empty reading.
31531
+ */
31532
+ getCurrentSnapshotBatch: method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()),
31214
31533
  /** Time-series object count inside one zone. `className` optional —
31215
31534
  * omit to count every class in the zone. */
31216
31535
  getZoneHistory: method(object({
@@ -33928,6 +34247,12 @@ Object.freeze({
33928
34247
  addonId: null,
33929
34248
  access: "delete"
33930
34249
  },
34250
+ "deviceManager.renameLocation": {
34251
+ capName: "device-manager",
34252
+ capScope: "system",
34253
+ addonId: null,
34254
+ access: "create"
34255
+ },
33931
34256
  "deviceManager.runDeviceAction": {
33932
34257
  capName: "device-manager",
33933
34258
  capScope: "system",
@@ -35620,13 +35945,19 @@ Object.freeze({
35620
35945
  addonId: null,
35621
35946
  access: "view"
35622
35947
  },
35623
- "pipelineAnalytics.getGroup": {
35948
+ "pipelineAnalytics.getKeyEvents": {
35949
+ capName: "pipeline-analytics",
35950
+ capScope: "device",
35951
+ addonId: null,
35952
+ access: "view"
35953
+ },
35954
+ "pipelineAnalytics.getKeyEventsBatch": {
35624
35955
  capName: "pipeline-analytics",
35625
35956
  capScope: "device",
35626
35957
  addonId: null,
35627
35958
  access: "view"
35628
35959
  },
35629
- "pipelineAnalytics.getKeyEvents": {
35960
+ "pipelineAnalytics.getMediaReclaimStatus": {
35630
35961
  capName: "pipeline-analytics",
35631
35962
  capScope: "device",
35632
35963
  addonId: null,
@@ -35716,12 +36047,6 @@ Object.freeze({
35716
36047
  addonId: null,
35717
36048
  access: "view"
35718
36049
  },
35719
- "pipelineAnalytics.listGroups": {
35720
- capName: "pipeline-analytics",
35721
- capScope: "device",
35722
- addonId: null,
35723
- access: "view"
35724
- },
35725
36050
  "pipelineAnalytics.listOpsLog": {
35726
36051
  capName: "pipeline-analytics",
35727
36052
  capScope: "device",
@@ -35806,6 +36131,12 @@ Object.freeze({
35806
36131
  addonId: null,
35807
36132
  access: "create"
35808
36133
  },
36134
+ "pipelineAnalytics.reclaimDebugMedia": {
36135
+ capName: "pipeline-analytics",
36136
+ capScope: "device",
36137
+ addonId: null,
36138
+ access: "create"
36139
+ },
35809
36140
  "pipelineAnalytics.reconcileFromDisk": {
35810
36141
  capName: "pipeline-analytics",
35811
36142
  capScope: "device",
@@ -36730,6 +37061,12 @@ Object.freeze({
36730
37061
  addonId: null,
36731
37062
  access: "view"
36732
37063
  },
37064
+ "recording.getPlacement": {
37065
+ capName: "recording",
37066
+ capScope: "system",
37067
+ addonId: null,
37068
+ access: "view"
37069
+ },
36733
37070
  "recording.getPlaybackManifest": {
36734
37071
  capName: "recording",
36735
37072
  capScope: "system",
@@ -36808,6 +37145,12 @@ Object.freeze({
36808
37145
  addonId: null,
36809
37146
  access: "view"
36810
37147
  },
37148
+ "recording.reconcileLedgerAgainstDisk": {
37149
+ capName: "recording",
37150
+ capScope: "system",
37151
+ addonId: null,
37152
+ access: "create"
37153
+ },
36811
37154
  "recording.refreshStorageLocationsForMigration": {
36812
37155
  capName: "recording",
36813
37156
  capScope: "system",
@@ -36850,6 +37193,12 @@ Object.freeze({
36850
37193
  addonId: null,
36851
37194
  access: "create"
36852
37195
  },
37196
+ "recording.setDevicePlacement": {
37197
+ capName: "recording",
37198
+ capScope: "system",
37199
+ addonId: null,
37200
+ access: "create"
37201
+ },
36853
37202
  "recording.startStorageMigrationMove": {
36854
37203
  capName: "recording",
36855
37204
  capScope: "system",
@@ -36934,6 +37283,12 @@ Object.freeze({
36934
37283
  addonId: null,
36935
37284
  access: "view"
36936
37285
  },
37286
+ "sceneMonitor.listScenesBatch": {
37287
+ capName: "scene-monitor",
37288
+ capScope: "device",
37289
+ addonId: null,
37290
+ access: "view"
37291
+ },
36937
37292
  "sceneMonitor.recheckNow": {
36938
37293
  capName: "scene-monitor",
36939
37294
  capScope: "device",
@@ -37288,12 +37643,36 @@ Object.freeze({
37288
37643
  addonId: null,
37289
37644
  access: "create"
37290
37645
  },
37646
+ "storageMigration.cleanupCancel": {
37647
+ capName: "storage-migration",
37648
+ capScope: "system",
37649
+ addonId: null,
37650
+ access: "create"
37651
+ },
37652
+ "storageMigration.cleanupStart": {
37653
+ capName: "storage-migration",
37654
+ capScope: "system",
37655
+ addonId: null,
37656
+ access: "create"
37657
+ },
37658
+ "storageMigration.cleanupStatus": {
37659
+ capName: "storage-migration",
37660
+ capScope: "system",
37661
+ addonId: null,
37662
+ access: "view"
37663
+ },
37291
37664
  "storageMigration.drain": {
37292
37665
  capName: "storage-migration",
37293
37666
  capScope: "system",
37294
37667
  addonId: null,
37295
37668
  access: "create"
37296
37669
  },
37670
+ "storageMigration.history": {
37671
+ capName: "storage-migration",
37672
+ capScope: "system",
37673
+ addonId: null,
37674
+ access: "view"
37675
+ },
37297
37676
  "storageMigration.movers": {
37298
37677
  capName: "storage-migration",
37299
37678
  capScope: "system",
@@ -38290,6 +38669,12 @@ Object.freeze({
38290
38669
  addonId: null,
38291
38670
  access: "view"
38292
38671
  },
38672
+ "zoneAnalytics.getCurrentSnapshotBatch": {
38673
+ capName: "zone-analytics",
38674
+ capScope: "device",
38675
+ addonId: null,
38676
+ access: "view"
38677
+ },
38293
38678
  "zoneAnalytics.getUnzonedHistory": {
38294
38679
  capName: "zone-analytics",
38295
38680
  capScope: "device",
@@ -39284,14 +39669,14 @@ Object.freeze({
39284
39669
  form: "single",
39285
39670
  optional: true
39286
39671
  }],
39287
- "pipelineAnalytics.getGroup": [{
39672
+ "pipelineAnalytics.getKeyEvents": [{
39288
39673
  name: "deviceId",
39289
39674
  form: "single",
39290
39675
  optional: false
39291
39676
  }],
39292
- "pipelineAnalytics.getKeyEvents": [{
39293
- name: "deviceId",
39294
- form: "single",
39677
+ "pipelineAnalytics.getKeyEventsBatch": [{
39678
+ name: "deviceIds",
39679
+ form: "array",
39295
39680
  optional: false
39296
39681
  }],
39297
39682
  "pipelineAnalytics.getMotionEvents": [{
@@ -39349,11 +39734,6 @@ Object.freeze({
39349
39734
  form: "single",
39350
39735
  optional: false
39351
39736
  }],
39352
- "pipelineAnalytics.listGroups": [{
39353
- name: "deviceIds",
39354
- form: "array",
39355
- optional: false
39356
- }],
39357
39737
  "pipelineAnalytics.listOpsLog": [{
39358
39738
  name: "deviceId",
39359
39739
  form: "single",
@@ -39399,6 +39779,11 @@ Object.freeze({
39399
39779
  form: "single",
39400
39780
  optional: true
39401
39781
  }],
39782
+ "pipelineAnalytics.reclaimDebugMedia": [{
39783
+ name: "deviceIds",
39784
+ form: "array",
39785
+ optional: true
39786
+ }],
39402
39787
  "pipelineAnalytics.reconcileFromDisk": [{
39403
39788
  name: "deviceId",
39404
39789
  form: "single",
@@ -39734,6 +40119,11 @@ Object.freeze({
39734
40119
  form: "single",
39735
40120
  optional: false
39736
40121
  }],
40122
+ "recording.reconcileLedgerAgainstDisk": [{
40123
+ name: "deviceId",
40124
+ form: "single",
40125
+ optional: true
40126
+ }],
39737
40127
  "recording.relocateFootage": [{
39738
40128
  name: "deviceId",
39739
40129
  form: "single",
@@ -39759,6 +40149,11 @@ Object.freeze({
39759
40149
  form: "single",
39760
40150
  optional: false
39761
40151
  }],
40152
+ "recording.setDevicePlacement": [{
40153
+ name: "deviceId",
40154
+ form: "single",
40155
+ optional: false
40156
+ }],
39762
40157
  "recording.startStorageMigrationMove": [{
39763
40158
  name: "deviceId",
39764
40159
  form: "single",
@@ -39799,6 +40194,11 @@ Object.freeze({
39799
40194
  form: "single",
39800
40195
  optional: false
39801
40196
  }],
40197
+ "sceneMonitor.listScenesBatch": [{
40198
+ name: "deviceIds",
40199
+ form: "array",
40200
+ optional: false
40201
+ }],
39802
40202
  "sceneMonitor.recheckNow": [{
39803
40203
  name: "deviceId",
39804
40204
  form: "single",
@@ -40060,6 +40460,11 @@ Object.freeze({
40060
40460
  form: "single",
40061
40461
  optional: false
40062
40462
  }],
40463
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
40464
+ name: "deviceIds",
40465
+ form: "array",
40466
+ optional: false
40467
+ }],
40063
40468
  "zoneAnalytics.getUnzonedHistory": [{
40064
40469
  name: "deviceId",
40065
40470
  form: "single",