@camstack/addon-tailscale 1.2.48 → 1.2.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8035,6 +8035,20 @@ var RelocateJobSchema = object({
8035
8035
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8036
8036
  */
8037
8037
  rowsReconciled: number().int().nonnegative().optional(),
8038
+ /**
8039
+ * Rows this run FORGOT because the file they name is not on disk.
8040
+ *
8041
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8042
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8043
+ * and the durable row is dropped through the same channel eviction uses. It
8044
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8045
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8046
+ * the same failure as one that quietly skips them (D295).
8047
+ *
8048
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8049
+ * ledger claimed 5.65 GB of footage that no longer existed.
8050
+ */
8051
+ rowsForgotten: number().int().nonnegative().optional(),
8038
8052
  startedAt: number(),
8039
8053
  finishedAt: number().nullable(),
8040
8054
  error: string().nullable()
@@ -8099,6 +8113,13 @@ var MediaRelocateModeSchema = _enum([
8099
8113
  ]);
8100
8114
  var RelocateMediaInputSchema = object({
8101
8115
  toLocationId: string(),
8116
+ /**
8117
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8118
+ * every row that is not already on `toLocationId` (the historical
8119
+ * behaviour). A named source is what a from→to migration needs: without it
8120
+ * "move events off disk 2" also emptied disk 1.
8121
+ */
8122
+ fromLocationId: string().optional(),
8102
8123
  throttleMbps: number().min(1).max(1e3).optional(),
8103
8124
  /** Omitted = `move`, the pre-existing behaviour. */
8104
8125
  mode: MediaRelocateModeSchema.optional()
@@ -8166,6 +8187,19 @@ var StorageMigrationDestinationsSchema = object({
8166
8187
  galleryMedia: string().min(1).optional()
8167
8188
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8168
8189
  /**
8190
+ * Optional named source per class. Omitted = the class's current default
8191
+ * (the historical behaviour). A named source that is NOT the default is a
8192
+ * drain of that disk: bytes move, the default stays, and the source is
8193
+ * disabled when the move finishes.
8194
+ */
8195
+ var StorageMigrationSourcesSchema = object({
8196
+ recordings: string().min(1).optional(),
8197
+ recordingsLow: string().min(1).optional(),
8198
+ eventMedia: string().min(1).optional(),
8199
+ backups: string().min(1).optional(),
8200
+ galleryMedia: string().min(1).optional()
8201
+ }).optional();
8202
+ /**
8169
8203
  * How a migration sequences the cutover against the byte move.
8170
8204
  *
8171
8205
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8187,6 +8221,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8187
8221
  /** Shared input for planning and starting an orchestrated storage migration. */
8188
8222
  var StorageMigrationInputSchema = object({
8189
8223
  destinations: StorageMigrationDestinationsSchema,
8224
+ /** Omitted = each class's current default. */
8225
+ sources: StorageMigrationSourcesSchema,
8190
8226
  throttleMbps: number().min(1).max(1e3).optional(),
8191
8227
  /** Omitted = `blocking`, which stays the default. */
8192
8228
  mode: StorageMigrationModeSchema.optional()
@@ -8266,6 +8302,13 @@ var StorageMigrationMoveSchema = object({
8266
8302
  storageClass: StorageMigrationClassSchema,
8267
8303
  fromLocationId: string(),
8268
8304
  toLocationId: string(),
8305
+ /**
8306
+ * True when `from` was NOT the class default at plan time. The move still
8307
+ * copies bytes, but the default is left alone and the source is disabled
8308
+ * once the copy verifies. Absent on jobs planned before this field existed
8309
+ * — those jobs always repointed, which is `false`.
8310
+ */
8311
+ freezeSource: boolean().optional(),
8269
8312
  moverJobId: string().nullable(),
8270
8313
  state: RelocateJobStateSchema.nullable(),
8271
8314
  error: string().nullable(),
@@ -8279,6 +8322,7 @@ var StorageMigrationJobSchema = object({
8279
8322
  * can tell a seconds-long cutover from a thirty-hour one. */
8280
8323
  mode: StorageMigrationModeSchema,
8281
8324
  destinations: StorageMigrationDestinationsSchema,
8325
+ sources: StorageMigrationSourcesSchema,
8282
8326
  throttleMbps: number(),
8283
8327
  moves: array(StorageMigrationMoveSchema),
8284
8328
  pauseLeaseId: string().nullable(),
@@ -8304,6 +8348,7 @@ var StorageMigrationFindingSchema = object({
8304
8348
  });
8305
8349
  var StorageMigrationPlanSchema = object({
8306
8350
  destinations: StorageMigrationDestinationsSchema,
8351
+ sources: StorageMigrationSourcesSchema,
8307
8352
  /** The mode this plan was built for. A plan is only valid for its mode: the
8308
8353
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8309
8354
  * it. */
@@ -8311,7 +8356,8 @@ var StorageMigrationPlanSchema = object({
8311
8356
  moves: array(object({
8312
8357
  storageClass: StorageMigrationClassSchema,
8313
8358
  fromLocationId: string(),
8314
- toLocationId: string()
8359
+ toLocationId: string(),
8360
+ freezeSource: boolean().optional()
8315
8361
  })),
8316
8362
  findings: array(StorageMigrationFindingSchema)
8317
8363
  });
@@ -8398,16 +8444,142 @@ var RelocateResidueSchema = object({
8398
8444
  segments: number().int().nonnegative(),
8399
8445
  bytes: number().int().nonnegative()
8400
8446
  }).nullable();
8447
+ /**
8448
+ * Ask one location whether its durable hour rows describe the disk — the walk
8449
+ * (D319).
8450
+ *
8451
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8452
+ * missing tool is the question, and the dry run is how they sanity-check the
8453
+ * destructive run before authorising it.
8454
+ */
8455
+ var LedgerWalkInputSchema = object({
8456
+ locationId: string().min(1),
8457
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8458
+ apply: boolean().optional(),
8459
+ /** Narrow to one camera. */
8460
+ deviceId: number().int().positive().optional(),
8461
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8462
+ profiles: array(string().min(1)).optional()
8463
+ });
8464
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8465
+ var LedgerWalkRefusalSchema = _enum([
8466
+ "location-unknown",
8467
+ "source-writable",
8468
+ "no-ledger",
8469
+ "archive-unreadable",
8470
+ "anchor-absent",
8471
+ "anchor-unreadable",
8472
+ "anchor-moved"
8473
+ ]);
8474
+ _enum([
8475
+ "live-tail",
8476
+ "listing-error",
8477
+ "path-mismatch",
8478
+ "durable-refused"
8479
+ ]);
8480
+ /** Every skip reason, always present, always a number — so a reason that never
8481
+ * fired reports as zero rather than absent and the report shape is constant
8482
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8483
+ var LedgerWalkSkipCountsSchema = object({
8484
+ "live-tail": number().int().nonnegative(),
8485
+ "listing-error": number().int().nonnegative(),
8486
+ "path-mismatch": number().int().nonnegative(),
8487
+ "durable-refused": number().int().nonnegative()
8488
+ });
8489
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8490
+ var LedgerWalkDeviceReportSchema = object({
8491
+ deviceId: number().int(),
8492
+ hoursWalked: number().int().nonnegative(),
8493
+ hoursMissing: number().int().nonnegative(),
8494
+ ghostSegments: number().int().nonnegative(),
8495
+ ghostBytes: number().int().nonnegative(),
8496
+ forgottenSegments: number().int().nonnegative(),
8497
+ orphanFiles: number().int().nonnegative()
8498
+ });
8499
+ /**
8500
+ * What one walk claimed, listed, found and (only when armed) forgot.
8501
+ *
8502
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8503
+ * walk that saw a fraction of the location is visible in its own report rather
8504
+ * than in the absence of one.
8505
+ */
8506
+ var LedgerWalkReportSchema = object({
8507
+ locationId: string(),
8508
+ applied: boolean(),
8509
+ refused: LedgerWalkRefusalSchema.nullable(),
8510
+ archiveSegments: number().int().nonnegative().nullable(),
8511
+ archiveBytes: number().int().nonnegative().nullable(),
8512
+ hoursClaimed: number().int().nonnegative(),
8513
+ hoursWalked: number().int().nonnegative(),
8514
+ hoursMissing: number().int().nonnegative(),
8515
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8516
+ listings: number().int().nonnegative(),
8517
+ segmentsClaimed: number().int().nonnegative(),
8518
+ ghostSegments: number().int().nonnegative(),
8519
+ ghostBytes: number().int().nonnegative(),
8520
+ ghostHoursWhole: number().int().nonnegative(),
8521
+ forgottenSegments: number().int().nonnegative(),
8522
+ forgottenBytes: number().int().nonnegative(),
8523
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8524
+ orphanFiles: number().int().nonnegative(),
8525
+ orphanSample: array(string()).readonly(),
8526
+ hoursSkipped: number().int().nonnegative(),
8527
+ skippedByReason: LedgerWalkSkipCountsSchema,
8528
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8529
+ bounded: boolean(),
8530
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8531
+ });
8401
8532
  /** How many rows a media pass would still act on against a given target — the
8402
8533
  * media lane's denominator AND its residue, from ONE derivation so the two can
8403
8534
  * never disagree. `null` = the count could not be taken. */
8404
8535
  var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8405
8536
  var RelocatableMediaCountInputSchema = object({
8406
8537
  toLocationId: string().min(1),
8538
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8539
+ fromLocationId: string().optional(),
8407
8540
  /** Omitted = `move`. */
8408
8541
  mode: MediaRelocateModeSchema.optional()
8409
8542
  });
8410
8543
  /**
8544
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8545
+ * ghost ledger entries on frozen footage locations.
8546
+ *
8547
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8548
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8549
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8550
+ * with no operator-visible status.
8551
+ */
8552
+ var StorageCleanupPhaseSchema = _enum([
8553
+ "orphans",
8554
+ "debug-media",
8555
+ "ghost-ledger",
8556
+ "done",
8557
+ "failed",
8558
+ "cancelled"
8559
+ ]);
8560
+ var StorageCleanupInputSchema = object({
8561
+ /** Also walk motion stills / track filmstrips. Off by default. */
8562
+ includeDebugMedia: boolean().optional() });
8563
+ var StorageCleanupJobSchema = object({
8564
+ jobId: string(),
8565
+ phase: StorageCleanupPhaseSchema,
8566
+ includeDebugMedia: boolean(),
8567
+ orphansReclaimed: number().int().nonnegative(),
8568
+ orphanBytesReclaimed: number().int().nonnegative(),
8569
+ debugMediaReclaimed: number().int().nonnegative(),
8570
+ debugMediaBytesReclaimed: number().int().nonnegative(),
8571
+ ghostsForgotten: number().int().nonnegative(),
8572
+ ghostBytesForgotten: number().int().nonnegative(),
8573
+ /** Short operator-facing line: current collection, pass, or location. */
8574
+ detail: string().nullable(),
8575
+ cancelRequested: boolean(),
8576
+ startedAt: number(),
8577
+ updatedAt: number(),
8578
+ finishedAt: number().nullable(),
8579
+ error: string().nullable()
8580
+ });
8581
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8582
+ /**
8411
8583
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8412
8584
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8413
8585
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8435,11 +8607,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8435
8607
  * The default location for a type uses `id === <type>:default` by
8436
8608
  * convention (the bare type ref like `'backups'` resolves to it).
8437
8609
  *
8438
- * `isSystem: true` marks a location as orchestrator-seeded and
8439
- * undeletable. The bootstrap-installed defaults (one per type) carry
8440
- * this flag; operator-added locations don't. Editing the config of
8441
- * a system location is allowed (path migration, provider swap) but
8442
- * deleting it is rejected at the cap level.
8610
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8611
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8612
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8613
+ * / last-enabled, not on this bit.
8443
8614
  */
8444
8615
  var StorageLocationSchema = object({
8445
8616
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12701,6 +12872,12 @@ method(object({
12701
12872
  }), _void(), {
12702
12873
  kind: "mutation",
12703
12874
  auth: "admin"
12875
+ }), method(object({
12876
+ from: string(),
12877
+ to: string()
12878
+ }), object({ moved: number() }), {
12879
+ kind: "mutation",
12880
+ auth: "admin"
12704
12881
  }), method(object({
12705
12882
  deviceId: number(),
12706
12883
  disabled: boolean()
@@ -18648,53 +18825,15 @@ var RecentTracksPageSchema = object({
18648
18825
  /** Cursor for the next page, or null when this page is the last. */
18649
18826
  nextCursor: string().nullable()
18650
18827
  });
18651
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
18652
- var LIST_GROUPS_MAX_LIMIT = 100;
18653
- var AnalyticsGroupRecordSchema = object({
18654
- id: string(),
18655
- deviceId: number().int(),
18656
- openedAt: number().int(),
18657
- closedAt: number().int(),
18658
- timestamp: number().int(),
18659
- memberCount: number().int(),
18660
- memberTrackIds: array(string()).readonly(),
18661
- className: string(),
18662
- classes: array(string()).readonly(),
18663
- /** Relative event-media path, or null when the group has no picture yet. */
18664
- mediaUrl: string().nullable(),
18665
- singleton: boolean()
18666
- });
18667
- var AnalyticsGroupMemberSchema = object({
18668
- trackId: string(),
18669
- deviceId: number().int(),
18670
- className: string(),
18671
- firstSeen: number().int(),
18672
- lastSeen: number().int(),
18673
- mediaUrl: string().nullable()
18674
- });
18675
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
18676
- var ListGroupsQueryInput = object({
18677
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
18678
- deviceIds: array(number()),
18679
- /** Window lower bound on `closedAt` (inclusive). */
18680
- since: number().optional(),
18681
- /** Window upper bound on `openedAt` (inclusive). */
18682
- until: number().optional(),
18683
- limit: number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
18684
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
18685
- cursor: string().optional()
18686
- });
18687
- var ListGroupsPageSchema = object({
18688
- groups: array(AnalyticsGroupRecordSchema).readonly(),
18689
- nextCursor: string().nullable()
18690
- });
18828
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
18829
+ var KEY_EVENTS_MAX_LIMIT = 200;
18691
18830
  var KeyEventQueryInput = object({
18692
18831
  deviceId: number(),
18693
18832
  /** Window lower bound (track firstSeen ≥ since). */
18694
18833
  since: number(),
18695
18834
  /** Window upper bound (track firstSeen ≤ until). */
18696
18835
  until: number(),
18697
- limit: number().int().min(1).max(200).default(50),
18836
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18698
18837
  /** Drop tracks scoring below this importance. */
18699
18838
  minImportance: number().min(0).max(1).optional(),
18700
18839
  /** Restrict to a single class (e.g. 'person'). */
@@ -18716,6 +18855,32 @@ var KeyEventSchema = object({
18716
18855
  ...TrackFlagFields,
18717
18856
  ...TrackRetrainFields
18718
18857
  });
18858
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
18859
+ var KeyEventBatchQueryInput = object({
18860
+ deviceIds: array(number()).min(1).max(200),
18861
+ since: number(),
18862
+ until: number(),
18863
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
18864
+ * across the set, which would let a busy camera starve a quiet one of its
18865
+ * rows and change what the merged feed contains. */
18866
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18867
+ minImportance: number().min(0).max(1).optional(),
18868
+ classFilter: string().optional()
18869
+ });
18870
+ /**
18871
+ * One camera's key events in a batch answer.
18872
+ *
18873
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
18874
+ * error rather than throwing, so a camera whose store read failed and one with
18875
+ * no events in the window were ALREADY indistinguishable per camera — the
18876
+ * batch does not make that worse, and the row keeps the deviceId the single
18877
+ * method's output never carried (the caller used to stamp it from the fan-out
18878
+ * key, which only worked because there was one query per camera).
18879
+ */
18880
+ var KeyEventsForDeviceSchema = object({
18881
+ deviceId: number(),
18882
+ events: array(KeyEventSchema).readonly()
18883
+ });
18719
18884
  object({
18720
18885
  trackId: string(),
18721
18886
  className: string(),
@@ -18763,9 +18928,7 @@ var TrackCascadeCountsSchema = object({
18763
18928
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
18764
18929
  plates: number().int(),
18765
18930
  /** Per-track CLIP search vectors removed (best-effort). */
18766
- embeddings: number().int(),
18767
- /** Group membership + group rows removed with their last member (best-effort). */
18768
- groups: number().int()
18931
+ embeddings: number().int()
18769
18932
  });
18770
18933
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
18771
18934
  var DiskReconcileCountsSchema = object({
@@ -18935,6 +19098,47 @@ var RebuildStatusSchema = object({
18935
19098
  /** Present when the pass ended by throwing. */
18936
19099
  error: string().nullable()
18937
19100
  });
19101
+ /**
19102
+ * Acknowledgement that a debug-media reclaim STARTED.
19103
+ *
19104
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19105
+ * it runs detached and this returns immediately. Awaiting it is how the
19106
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19107
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19108
+ */
19109
+ var MediaReclaimStartResultSchema = object({
19110
+ started: boolean(),
19111
+ /** True when a pass was already running; the new request is ignored. */
19112
+ alreadyRunning: boolean()
19113
+ });
19114
+ var MediaReclaimInputSchema = object({
19115
+ mode: _enum(["report", "reclaim"]).default("report"),
19116
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19117
+ deviceIds: array(number().int()).min(1).optional(),
19118
+ restart: boolean().optional(),
19119
+ pageSize: number().int().min(50).max(5e3).optional(),
19120
+ maxRowsPerDevice: number().int().min(1).max(5e5).optional(),
19121
+ maxReclaimPerDevice: number().int().min(1).max(5e5).optional(),
19122
+ maxBytesPerRun: number().int().min(1).optional(),
19123
+ budgetMinutes: number().int().min(1).max(720).optional(),
19124
+ throttleBytesPerSec: number().int().min(64 * 1024).optional(),
19125
+ graceMinutes: number().int().min(1).max(10080).optional()
19126
+ });
19127
+ var MediaReclaimStatusSchema = object({
19128
+ running: boolean(),
19129
+ mode: _enum(["report", "reclaim"]).nullable(),
19130
+ totalExamined: number(),
19131
+ totalEligible: number(),
19132
+ totalReclaimed: number(),
19133
+ totalBytesReclaimed: number(),
19134
+ totalRefused: number(),
19135
+ /** Device+scope windows finished in this pass. */
19136
+ devicesDone: number(),
19137
+ complete: boolean().nullable(),
19138
+ startedAtMs: number().nullable(),
19139
+ finishedAtMs: number().nullable(),
19140
+ error: string().nullable()
19141
+ });
18938
19142
  var ReplayFrameInputSchema = object({
18939
19143
  timestamp: number(),
18940
19144
  frame: PipelineRunResultBridge
@@ -18973,10 +19177,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18973
19177
  * stationary registry). Default false: the timeline lists passages,
18974
19178
  * not parking records (operator decision, 2026-08-15). */
18975
19179
  includeStationary: boolean().optional()
18976
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
18977
- deviceId: number(),
18978
- groupId: string().min(1)
18979
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
19180
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18980
19181
  kind: "mutation",
18981
19182
  auth: "admin"
18982
19183
  }), 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({
@@ -18985,7 +19186,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18985
19186
  until: number().optional(),
18986
19187
  kinds: array(string()).optional(),
18987
19188
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18988
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19189
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
18989
19190
  deviceId: number(),
18990
19191
  since: number(),
18991
19192
  until: number(),
@@ -19076,6 +19277,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19076
19277
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19077
19278
  kind: "query",
19078
19279
  auth: "admin"
19280
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19281
+ kind: "mutation",
19282
+ auth: "admin"
19283
+ }), method(object({}), MediaReclaimStatusSchema, {
19284
+ kind: "query",
19285
+ auth: "admin"
19079
19286
  }), method(object({ deviceIds: array(number()).optional() }), TrainingExportSummarySchema, {
19080
19287
  kind: "query",
19081
19288
  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),
@@ -26485,6 +26698,33 @@ var RecordingRebalanceInputSchema = object({
26485
26698
  minMoveGb: number().min(0).optional()
26486
26699
  });
26487
26700
  /**
26701
+ * Operator-facing placement of one camera onto a recordings location.
26702
+ *
26703
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26704
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26705
+ * currently write — the plan, which may disagree with the pin when Auto.
26706
+ */
26707
+ var RecordingDevicePlacementSchema = object({
26708
+ deviceId: number().int(),
26709
+ profile: string(),
26710
+ locationId: string()
26711
+ });
26712
+ var RecordingDevicePinSchema = object({
26713
+ deviceId: number().int(),
26714
+ /** Recordings-class location this camera is pinned to. */
26715
+ locationId: string()
26716
+ });
26717
+ var RecordingPlacementViewSchema = object({
26718
+ assignments: array(RecordingDevicePlacementSchema),
26719
+ pins: array(RecordingDevicePinSchema),
26720
+ defaultLocations: record(string(), string())
26721
+ });
26722
+ var RecordingSetDevicePlacementInputSchema = object({
26723
+ deviceId: number().int(),
26724
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26725
+ locationId: string().nullable()
26726
+ });
26727
+ /**
26488
26728
  * Result of locating footage at a wall-clock instant for one device/profile.
26489
26729
  * `segment` carries the covering segment's window; `gap` reports the forward
26490
26730
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -26698,6 +26938,9 @@ method(object({
26698
26938
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26699
26939
  kind: "query",
26700
26940
  auth: "admin"
26941
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26942
+ kind: "mutation",
26943
+ auth: "admin"
26701
26944
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26702
26945
  kind: "mutation",
26703
26946
  auth: "admin"
@@ -26707,6 +26950,12 @@ method(object({
26707
26950
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
26708
26951
  kind: "mutation",
26709
26952
  auth: "admin"
26953
+ }), method(object({}), RecordingPlacementViewSchema, {
26954
+ kind: "query",
26955
+ auth: "admin"
26956
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
26957
+ kind: "mutation",
26958
+ auth: "admin"
26710
26959
  });
26711
26960
  /**
26712
26961
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -27089,7 +27338,26 @@ var SceneMonitorStatusSchema = object({
27089
27338
  monitors: array(SceneMonitorSchema),
27090
27339
  lastFetchedAt: number()
27091
27340
  });
27092
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27341
+ /**
27342
+ * One camera's row in a `listScenesBatch` answer.
27343
+ *
27344
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27345
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27346
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27347
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27348
+ * configured. Fanned out per camera the difference was visible — one query
27349
+ * errored while the others resolved — and a batch that returned only the rows
27350
+ * it managed would have destroyed it, silently, by making an unreachable camera
27351
+ * indistinguishable from one that answered `monitors: []`.
27352
+ *
27353
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27354
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27355
+ */
27356
+ var SceneMonitorStatusForDeviceSchema = object({
27357
+ deviceId: number(),
27358
+ status: SceneMonitorStatusSchema.nullable()
27359
+ });
27360
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27093
27361
  deviceId: number(),
27094
27362
  label: string(),
27095
27363
  roi: MaskRectShapeSchema,
@@ -28413,6 +28681,27 @@ var CameraOccupancySnapshotSchema = object({
28413
28681
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28414
28682
  });
28415
28683
  /**
28684
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28685
+ *
28686
+ * THREE outcomes, and the single-camera method could only express two of them
28687
+ * because `snapshot: null` was already spoken for:
28688
+ *
28689
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28690
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28691
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28692
+ * - `read: 'unreadable'` — the owner could not answer for this
28693
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28694
+ *
28695
+ * Collapsing the last two is the failure this field exists to prevent: a
28696
+ * hydration that threw would otherwise render as an empty Stationary section,
28697
+ * which is a definite claim about a camera nobody could read.
28698
+ */
28699
+ var CameraOccupancySnapshotForDeviceSchema = object({
28700
+ deviceId: number(),
28701
+ read: _enum(["read", "unreadable"]),
28702
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28703
+ });
28704
+ /**
28416
28705
  * Time-series resolution. The history methods return one bucket per
28417
28706
  * step over the requested range. Smaller resolutions cost more
28418
28707
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28436,7 +28725,7 @@ var HistoryPointSchema = object({
28436
28725
  /** Object count averaged over the bucket (rounded to nearest integer). */
28437
28726
  count: number().int().nonnegative()
28438
28727
  });
28439
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28728
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28440
28729
  deviceId: number(),
28441
28730
  zoneId: string(),
28442
28731
  className: string().optional()
@@ -30037,6 +30326,12 @@ Object.freeze({
30037
30326
  addonId: null,
30038
30327
  access: "delete"
30039
30328
  },
30329
+ "deviceManager.renameLocation": {
30330
+ capName: "device-manager",
30331
+ capScope: "system",
30332
+ addonId: null,
30333
+ access: "create"
30334
+ },
30040
30335
  "deviceManager.runDeviceAction": {
30041
30336
  capName: "device-manager",
30042
30337
  capScope: "system",
@@ -31729,13 +32024,19 @@ Object.freeze({
31729
32024
  addonId: null,
31730
32025
  access: "view"
31731
32026
  },
31732
- "pipelineAnalytics.getGroup": {
32027
+ "pipelineAnalytics.getKeyEvents": {
31733
32028
  capName: "pipeline-analytics",
31734
32029
  capScope: "device",
31735
32030
  addonId: null,
31736
32031
  access: "view"
31737
32032
  },
31738
- "pipelineAnalytics.getKeyEvents": {
32033
+ "pipelineAnalytics.getKeyEventsBatch": {
32034
+ capName: "pipeline-analytics",
32035
+ capScope: "device",
32036
+ addonId: null,
32037
+ access: "view"
32038
+ },
32039
+ "pipelineAnalytics.getMediaReclaimStatus": {
31739
32040
  capName: "pipeline-analytics",
31740
32041
  capScope: "device",
31741
32042
  addonId: null,
@@ -31825,12 +32126,6 @@ Object.freeze({
31825
32126
  addonId: null,
31826
32127
  access: "view"
31827
32128
  },
31828
- "pipelineAnalytics.listGroups": {
31829
- capName: "pipeline-analytics",
31830
- capScope: "device",
31831
- addonId: null,
31832
- access: "view"
31833
- },
31834
32129
  "pipelineAnalytics.listOpsLog": {
31835
32130
  capName: "pipeline-analytics",
31836
32131
  capScope: "device",
@@ -31915,6 +32210,12 @@ Object.freeze({
31915
32210
  addonId: null,
31916
32211
  access: "create"
31917
32212
  },
32213
+ "pipelineAnalytics.reclaimDebugMedia": {
32214
+ capName: "pipeline-analytics",
32215
+ capScope: "device",
32216
+ addonId: null,
32217
+ access: "create"
32218
+ },
31918
32219
  "pipelineAnalytics.reconcileFromDisk": {
31919
32220
  capName: "pipeline-analytics",
31920
32221
  capScope: "device",
@@ -32839,6 +33140,12 @@ Object.freeze({
32839
33140
  addonId: null,
32840
33141
  access: "view"
32841
33142
  },
33143
+ "recording.getPlacement": {
33144
+ capName: "recording",
33145
+ capScope: "system",
33146
+ addonId: null,
33147
+ access: "view"
33148
+ },
32842
33149
  "recording.getPlaybackManifest": {
32843
33150
  capName: "recording",
32844
33151
  capScope: "system",
@@ -32917,6 +33224,12 @@ Object.freeze({
32917
33224
  addonId: null,
32918
33225
  access: "view"
32919
33226
  },
33227
+ "recording.reconcileLedgerAgainstDisk": {
33228
+ capName: "recording",
33229
+ capScope: "system",
33230
+ addonId: null,
33231
+ access: "create"
33232
+ },
32920
33233
  "recording.refreshStorageLocationsForMigration": {
32921
33234
  capName: "recording",
32922
33235
  capScope: "system",
@@ -32959,6 +33272,12 @@ Object.freeze({
32959
33272
  addonId: null,
32960
33273
  access: "create"
32961
33274
  },
33275
+ "recording.setDevicePlacement": {
33276
+ capName: "recording",
33277
+ capScope: "system",
33278
+ addonId: null,
33279
+ access: "create"
33280
+ },
32962
33281
  "recording.startStorageMigrationMove": {
32963
33282
  capName: "recording",
32964
33283
  capScope: "system",
@@ -33043,6 +33362,12 @@ Object.freeze({
33043
33362
  addonId: null,
33044
33363
  access: "view"
33045
33364
  },
33365
+ "sceneMonitor.listScenesBatch": {
33366
+ capName: "scene-monitor",
33367
+ capScope: "device",
33368
+ addonId: null,
33369
+ access: "view"
33370
+ },
33046
33371
  "sceneMonitor.recheckNow": {
33047
33372
  capName: "scene-monitor",
33048
33373
  capScope: "device",
@@ -33397,12 +33722,36 @@ Object.freeze({
33397
33722
  addonId: null,
33398
33723
  access: "create"
33399
33724
  },
33725
+ "storageMigration.cleanupCancel": {
33726
+ capName: "storage-migration",
33727
+ capScope: "system",
33728
+ addonId: null,
33729
+ access: "create"
33730
+ },
33731
+ "storageMigration.cleanupStart": {
33732
+ capName: "storage-migration",
33733
+ capScope: "system",
33734
+ addonId: null,
33735
+ access: "create"
33736
+ },
33737
+ "storageMigration.cleanupStatus": {
33738
+ capName: "storage-migration",
33739
+ capScope: "system",
33740
+ addonId: null,
33741
+ access: "view"
33742
+ },
33400
33743
  "storageMigration.drain": {
33401
33744
  capName: "storage-migration",
33402
33745
  capScope: "system",
33403
33746
  addonId: null,
33404
33747
  access: "create"
33405
33748
  },
33749
+ "storageMigration.history": {
33750
+ capName: "storage-migration",
33751
+ capScope: "system",
33752
+ addonId: null,
33753
+ access: "view"
33754
+ },
33406
33755
  "storageMigration.movers": {
33407
33756
  capName: "storage-migration",
33408
33757
  capScope: "system",
@@ -34399,6 +34748,12 @@ Object.freeze({
34399
34748
  addonId: null,
34400
34749
  access: "view"
34401
34750
  },
34751
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34752
+ capName: "zone-analytics",
34753
+ capScope: "device",
34754
+ addonId: null,
34755
+ access: "view"
34756
+ },
34402
34757
  "zoneAnalytics.getUnzonedHistory": {
34403
34758
  capName: "zone-analytics",
34404
34759
  capScope: "device",
@@ -35393,14 +35748,14 @@ Object.freeze({
35393
35748
  form: "single",
35394
35749
  optional: true
35395
35750
  }],
35396
- "pipelineAnalytics.getGroup": [{
35751
+ "pipelineAnalytics.getKeyEvents": [{
35397
35752
  name: "deviceId",
35398
35753
  form: "single",
35399
35754
  optional: false
35400
35755
  }],
35401
- "pipelineAnalytics.getKeyEvents": [{
35402
- name: "deviceId",
35403
- form: "single",
35756
+ "pipelineAnalytics.getKeyEventsBatch": [{
35757
+ name: "deviceIds",
35758
+ form: "array",
35404
35759
  optional: false
35405
35760
  }],
35406
35761
  "pipelineAnalytics.getMotionEvents": [{
@@ -35458,11 +35813,6 @@ Object.freeze({
35458
35813
  form: "single",
35459
35814
  optional: false
35460
35815
  }],
35461
- "pipelineAnalytics.listGroups": [{
35462
- name: "deviceIds",
35463
- form: "array",
35464
- optional: false
35465
- }],
35466
35816
  "pipelineAnalytics.listOpsLog": [{
35467
35817
  name: "deviceId",
35468
35818
  form: "single",
@@ -35508,6 +35858,11 @@ Object.freeze({
35508
35858
  form: "single",
35509
35859
  optional: true
35510
35860
  }],
35861
+ "pipelineAnalytics.reclaimDebugMedia": [{
35862
+ name: "deviceIds",
35863
+ form: "array",
35864
+ optional: true
35865
+ }],
35511
35866
  "pipelineAnalytics.reconcileFromDisk": [{
35512
35867
  name: "deviceId",
35513
35868
  form: "single",
@@ -35843,6 +36198,11 @@ Object.freeze({
35843
36198
  form: "single",
35844
36199
  optional: false
35845
36200
  }],
36201
+ "recording.reconcileLedgerAgainstDisk": [{
36202
+ name: "deviceId",
36203
+ form: "single",
36204
+ optional: true
36205
+ }],
35846
36206
  "recording.relocateFootage": [{
35847
36207
  name: "deviceId",
35848
36208
  form: "single",
@@ -35868,6 +36228,11 @@ Object.freeze({
35868
36228
  form: "single",
35869
36229
  optional: false
35870
36230
  }],
36231
+ "recording.setDevicePlacement": [{
36232
+ name: "deviceId",
36233
+ form: "single",
36234
+ optional: false
36235
+ }],
35871
36236
  "recording.startStorageMigrationMove": [{
35872
36237
  name: "deviceId",
35873
36238
  form: "single",
@@ -35908,6 +36273,11 @@ Object.freeze({
35908
36273
  form: "single",
35909
36274
  optional: false
35910
36275
  }],
36276
+ "sceneMonitor.listScenesBatch": [{
36277
+ name: "deviceIds",
36278
+ form: "array",
36279
+ optional: false
36280
+ }],
35911
36281
  "sceneMonitor.recheckNow": [{
35912
36282
  name: "deviceId",
35913
36283
  form: "single",
@@ -36169,6 +36539,11 @@ Object.freeze({
36169
36539
  form: "single",
36170
36540
  optional: false
36171
36541
  }],
36542
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36543
+ name: "deviceIds",
36544
+ form: "array",
36545
+ optional: false
36546
+ }],
36172
36547
  "zoneAnalytics.getUnzonedHistory": [{
36173
36548
  name: "deviceId",
36174
36549
  form: "single",