@camstack/addon-export-alexa 1.2.54 → 1.2.55

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.
@@ -8186,6 +8186,20 @@ var RelocateJobSchema = object({
8186
8186
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8187
8187
  */
8188
8188
  rowsReconciled: number().int().nonnegative().optional(),
8189
+ /**
8190
+ * Rows this run FORGOT because the file they name is not on disk.
8191
+ *
8192
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8193
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8194
+ * and the durable row is dropped through the same channel eviction uses. It
8195
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8196
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8197
+ * the same failure as one that quietly skips them (D295).
8198
+ *
8199
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8200
+ * ledger claimed 5.65 GB of footage that no longer existed.
8201
+ */
8202
+ rowsForgotten: number().int().nonnegative().optional(),
8189
8203
  startedAt: number(),
8190
8204
  finishedAt: number().nullable(),
8191
8205
  error: string().nullable()
@@ -8549,6 +8563,91 @@ var RelocateResidueSchema = object({
8549
8563
  segments: number().int().nonnegative(),
8550
8564
  bytes: number().int().nonnegative()
8551
8565
  }).nullable();
8566
+ /**
8567
+ * Ask one location whether its durable hour rows describe the disk — the walk
8568
+ * (D319).
8569
+ *
8570
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8571
+ * missing tool is the question, and the dry run is how they sanity-check the
8572
+ * destructive run before authorising it.
8573
+ */
8574
+ var LedgerWalkInputSchema = object({
8575
+ locationId: string().min(1),
8576
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8577
+ apply: boolean().optional(),
8578
+ /** Narrow to one camera. */
8579
+ deviceId: number().int().positive().optional(),
8580
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8581
+ profiles: array(string().min(1)).optional()
8582
+ });
8583
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8584
+ var LedgerWalkRefusalSchema = _enum([
8585
+ "location-unknown",
8586
+ "source-writable",
8587
+ "no-ledger",
8588
+ "archive-unreadable",
8589
+ "anchor-absent",
8590
+ "anchor-unreadable",
8591
+ "anchor-moved"
8592
+ ]);
8593
+ _enum([
8594
+ "live-tail",
8595
+ "listing-error",
8596
+ "path-mismatch",
8597
+ "durable-refused"
8598
+ ]);
8599
+ /** Every skip reason, always present, always a number — so a reason that never
8600
+ * fired reports as zero rather than absent and the report shape is constant
8601
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8602
+ var LedgerWalkSkipCountsSchema = object({
8603
+ "live-tail": number().int().nonnegative(),
8604
+ "listing-error": number().int().nonnegative(),
8605
+ "path-mismatch": number().int().nonnegative(),
8606
+ "durable-refused": number().int().nonnegative()
8607
+ });
8608
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8609
+ var LedgerWalkDeviceReportSchema = object({
8610
+ deviceId: number().int(),
8611
+ hoursWalked: number().int().nonnegative(),
8612
+ hoursMissing: number().int().nonnegative(),
8613
+ ghostSegments: number().int().nonnegative(),
8614
+ ghostBytes: number().int().nonnegative(),
8615
+ forgottenSegments: number().int().nonnegative(),
8616
+ orphanFiles: number().int().nonnegative()
8617
+ });
8618
+ /**
8619
+ * What one walk claimed, listed, found and (only when armed) forgot.
8620
+ *
8621
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8622
+ * walk that saw a fraction of the location is visible in its own report rather
8623
+ * than in the absence of one.
8624
+ */
8625
+ var LedgerWalkReportSchema = object({
8626
+ locationId: string(),
8627
+ applied: boolean(),
8628
+ refused: LedgerWalkRefusalSchema.nullable(),
8629
+ archiveSegments: number().int().nonnegative().nullable(),
8630
+ archiveBytes: number().int().nonnegative().nullable(),
8631
+ hoursClaimed: number().int().nonnegative(),
8632
+ hoursWalked: number().int().nonnegative(),
8633
+ hoursMissing: number().int().nonnegative(),
8634
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8635
+ listings: number().int().nonnegative(),
8636
+ segmentsClaimed: number().int().nonnegative(),
8637
+ ghostSegments: number().int().nonnegative(),
8638
+ ghostBytes: number().int().nonnegative(),
8639
+ ghostHoursWhole: number().int().nonnegative(),
8640
+ forgottenSegments: number().int().nonnegative(),
8641
+ forgottenBytes: number().int().nonnegative(),
8642
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8643
+ orphanFiles: number().int().nonnegative(),
8644
+ orphanSample: array(string()).readonly(),
8645
+ hoursSkipped: number().int().nonnegative(),
8646
+ skippedByReason: LedgerWalkSkipCountsSchema,
8647
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8648
+ bounded: boolean(),
8649
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8650
+ });
8552
8651
  /** How many rows a media pass would still act on against a given target — the
8553
8652
  * media lane's denominator AND its residue, from ONE derivation so the two can
8554
8653
  * never disagree. `null` = the count could not be taken. */
@@ -18965,13 +19064,15 @@ var ListGroupsPageSchema = object({
18965
19064
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18966
19065
  nextCursor: string().nullable()
18967
19066
  });
19067
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19068
+ var KEY_EVENTS_MAX_LIMIT = 200;
18968
19069
  var KeyEventQueryInput = object({
18969
19070
  deviceId: number(),
18970
19071
  /** Window lower bound (track firstSeen ≥ since). */
18971
19072
  since: number(),
18972
19073
  /** Window upper bound (track firstSeen ≤ until). */
18973
19074
  until: number(),
18974
- limit: number().int().min(1).max(200).default(50),
19075
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18975
19076
  /** Drop tracks scoring below this importance. */
18976
19077
  minImportance: number().min(0).max(1).optional(),
18977
19078
  /** Restrict to a single class (e.g. 'person'). */
@@ -18993,6 +19094,32 @@ var KeyEventSchema = object({
18993
19094
  ...TrackFlagFields,
18994
19095
  ...TrackRetrainFields
18995
19096
  });
19097
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19098
+ var KeyEventBatchQueryInput = object({
19099
+ deviceIds: array(number()).min(1).max(200),
19100
+ since: number(),
19101
+ until: number(),
19102
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19103
+ * across the set, which would let a busy camera starve a quiet one of its
19104
+ * rows and change what the merged feed contains. */
19105
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19106
+ minImportance: number().min(0).max(1).optional(),
19107
+ classFilter: string().optional()
19108
+ });
19109
+ /**
19110
+ * One camera's key events in a batch answer.
19111
+ *
19112
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19113
+ * error rather than throwing, so a camera whose store read failed and one with
19114
+ * no events in the window were ALREADY indistinguishable per camera — the
19115
+ * batch does not make that worse, and the row keeps the deviceId the single
19116
+ * method's output never carried (the caller used to stamp it from the fan-out
19117
+ * key, which only worked because there was one query per camera).
19118
+ */
19119
+ var KeyEventsForDeviceSchema = object({
19120
+ deviceId: number(),
19121
+ events: array(KeyEventSchema).readonly()
19122
+ });
18996
19123
  object({
18997
19124
  trackId: string(),
18998
19125
  className: string(),
@@ -19262,7 +19389,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19262
19389
  until: number().optional(),
19263
19390
  kinds: array(string()).optional(),
19264
19391
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19265
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19392
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19266
19393
  deviceId: number(),
19267
19394
  since: number(),
19268
19395
  until: number(),
@@ -26914,6 +27041,9 @@ method(object({
26914
27041
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26915
27042
  kind: "query",
26916
27043
  auth: "admin"
27044
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27045
+ kind: "mutation",
27046
+ auth: "admin"
26917
27047
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26918
27048
  kind: "mutation",
26919
27049
  auth: "admin"
@@ -27305,7 +27435,26 @@ var SceneMonitorStatusSchema = object({
27305
27435
  monitors: array(SceneMonitorSchema),
27306
27436
  lastFetchedAt: number()
27307
27437
  });
27308
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27438
+ /**
27439
+ * One camera's row in a `listScenesBatch` answer.
27440
+ *
27441
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27442
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27443
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27444
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27445
+ * configured. Fanned out per camera the difference was visible — one query
27446
+ * errored while the others resolved — and a batch that returned only the rows
27447
+ * it managed would have destroyed it, silently, by making an unreachable camera
27448
+ * indistinguishable from one that answered `monitors: []`.
27449
+ *
27450
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27451
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27452
+ */
27453
+ var SceneMonitorStatusForDeviceSchema = object({
27454
+ deviceId: number(),
27455
+ status: SceneMonitorStatusSchema.nullable()
27456
+ });
27457
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27309
27458
  deviceId: number(),
27310
27459
  label: string(),
27311
27460
  roi: MaskRectShapeSchema,
@@ -28629,6 +28778,27 @@ var CameraOccupancySnapshotSchema = object({
28629
28778
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28630
28779
  });
28631
28780
  /**
28781
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28782
+ *
28783
+ * THREE outcomes, and the single-camera method could only express two of them
28784
+ * because `snapshot: null` was already spoken for:
28785
+ *
28786
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28787
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28788
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28789
+ * - `read: 'unreadable'` — the owner could not answer for this
28790
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28791
+ *
28792
+ * Collapsing the last two is the failure this field exists to prevent: a
28793
+ * hydration that threw would otherwise render as an empty Stationary section,
28794
+ * which is a definite claim about a camera nobody could read.
28795
+ */
28796
+ var CameraOccupancySnapshotForDeviceSchema = object({
28797
+ deviceId: number(),
28798
+ read: _enum(["read", "unreadable"]),
28799
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28800
+ });
28801
+ /**
28632
28802
  * Time-series resolution. The history methods return one bucket per
28633
28803
  * step over the requested range. Smaller resolutions cost more
28634
28804
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28652,7 +28822,7 @@ var HistoryPointSchema = object({
28652
28822
  /** Object count averaged over the bucket (rounded to nearest integer). */
28653
28823
  count: number().int().nonnegative()
28654
28824
  });
28655
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28825
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28656
28826
  deviceId: number(),
28657
28827
  zoneId: string(),
28658
28828
  className: string().optional()
@@ -31957,6 +32127,12 @@ Object.freeze({
31957
32127
  addonId: null,
31958
32128
  access: "view"
31959
32129
  },
32130
+ "pipelineAnalytics.getKeyEventsBatch": {
32131
+ capName: "pipeline-analytics",
32132
+ capScope: "device",
32133
+ addonId: null,
32134
+ access: "view"
32135
+ },
31960
32136
  "pipelineAnalytics.getMotionEvents": {
31961
32137
  capName: "pipeline-analytics",
31962
32138
  capScope: "device",
@@ -33133,6 +33309,12 @@ Object.freeze({
33133
33309
  addonId: null,
33134
33310
  access: "view"
33135
33311
  },
33312
+ "recording.reconcileLedgerAgainstDisk": {
33313
+ capName: "recording",
33314
+ capScope: "system",
33315
+ addonId: null,
33316
+ access: "create"
33317
+ },
33136
33318
  "recording.refreshStorageLocationsForMigration": {
33137
33319
  capName: "recording",
33138
33320
  capScope: "system",
@@ -33259,6 +33441,12 @@ Object.freeze({
33259
33441
  addonId: null,
33260
33442
  access: "view"
33261
33443
  },
33444
+ "sceneMonitor.listScenesBatch": {
33445
+ capName: "scene-monitor",
33446
+ capScope: "device",
33447
+ addonId: null,
33448
+ access: "view"
33449
+ },
33262
33450
  "sceneMonitor.recheckNow": {
33263
33451
  capName: "scene-monitor",
33264
33452
  capScope: "device",
@@ -34615,6 +34803,12 @@ Object.freeze({
34615
34803
  addonId: null,
34616
34804
  access: "view"
34617
34805
  },
34806
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34807
+ capName: "zone-analytics",
34808
+ capScope: "device",
34809
+ addonId: null,
34810
+ access: "view"
34811
+ },
34618
34812
  "zoneAnalytics.getUnzonedHistory": {
34619
34813
  capName: "zone-analytics",
34620
34814
  capScope: "device",
@@ -35619,6 +35813,11 @@ Object.freeze({
35619
35813
  form: "single",
35620
35814
  optional: false
35621
35815
  }],
35816
+ "pipelineAnalytics.getKeyEventsBatch": [{
35817
+ name: "deviceIds",
35818
+ form: "array",
35819
+ optional: false
35820
+ }],
35622
35821
  "pipelineAnalytics.getMotionEvents": [{
35623
35822
  name: "deviceId",
35624
35823
  form: "single",
@@ -36059,6 +36258,11 @@ Object.freeze({
36059
36258
  form: "single",
36060
36259
  optional: false
36061
36260
  }],
36261
+ "recording.reconcileLedgerAgainstDisk": [{
36262
+ name: "deviceId",
36263
+ form: "single",
36264
+ optional: true
36265
+ }],
36062
36266
  "recording.relocateFootage": [{
36063
36267
  name: "deviceId",
36064
36268
  form: "single",
@@ -36124,6 +36328,11 @@ Object.freeze({
36124
36328
  form: "single",
36125
36329
  optional: false
36126
36330
  }],
36331
+ "sceneMonitor.listScenesBatch": [{
36332
+ name: "deviceIds",
36333
+ form: "array",
36334
+ optional: false
36335
+ }],
36127
36336
  "sceneMonitor.recheckNow": [{
36128
36337
  name: "deviceId",
36129
36338
  form: "single",
@@ -36385,6 +36594,11 @@ Object.freeze({
36385
36594
  form: "single",
36386
36595
  optional: false
36387
36596
  }],
36597
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36598
+ name: "deviceIds",
36599
+ form: "array",
36600
+ optional: false
36601
+ }],
36388
36602
  "zoneAnalytics.getUnzonedHistory": [{
36389
36603
  name: "deviceId",
36390
36604
  form: "single",
@@ -8159,6 +8159,20 @@ var RelocateJobSchema = object({
8159
8159
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8160
8160
  */
8161
8161
  rowsReconciled: number().int().nonnegative().optional(),
8162
+ /**
8163
+ * Rows this run FORGOT because the file they name is not on disk.
8164
+ *
8165
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8166
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8167
+ * and the durable row is dropped through the same channel eviction uses. It
8168
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8169
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8170
+ * the same failure as one that quietly skips them (D295).
8171
+ *
8172
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8173
+ * ledger claimed 5.65 GB of footage that no longer existed.
8174
+ */
8175
+ rowsForgotten: number().int().nonnegative().optional(),
8162
8176
  startedAt: number(),
8163
8177
  finishedAt: number().nullable(),
8164
8178
  error: string().nullable()
@@ -8522,6 +8536,91 @@ var RelocateResidueSchema = object({
8522
8536
  segments: number().int().nonnegative(),
8523
8537
  bytes: number().int().nonnegative()
8524
8538
  }).nullable();
8539
+ /**
8540
+ * Ask one location whether its durable hour rows describe the disk — the walk
8541
+ * (D319).
8542
+ *
8543
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8544
+ * missing tool is the question, and the dry run is how they sanity-check the
8545
+ * destructive run before authorising it.
8546
+ */
8547
+ var LedgerWalkInputSchema = object({
8548
+ locationId: string().min(1),
8549
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8550
+ apply: boolean().optional(),
8551
+ /** Narrow to one camera. */
8552
+ deviceId: number().int().positive().optional(),
8553
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8554
+ profiles: array(string().min(1)).optional()
8555
+ });
8556
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8557
+ var LedgerWalkRefusalSchema = _enum([
8558
+ "location-unknown",
8559
+ "source-writable",
8560
+ "no-ledger",
8561
+ "archive-unreadable",
8562
+ "anchor-absent",
8563
+ "anchor-unreadable",
8564
+ "anchor-moved"
8565
+ ]);
8566
+ _enum([
8567
+ "live-tail",
8568
+ "listing-error",
8569
+ "path-mismatch",
8570
+ "durable-refused"
8571
+ ]);
8572
+ /** Every skip reason, always present, always a number — so a reason that never
8573
+ * fired reports as zero rather than absent and the report shape is constant
8574
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8575
+ var LedgerWalkSkipCountsSchema = object({
8576
+ "live-tail": number().int().nonnegative(),
8577
+ "listing-error": number().int().nonnegative(),
8578
+ "path-mismatch": number().int().nonnegative(),
8579
+ "durable-refused": number().int().nonnegative()
8580
+ });
8581
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8582
+ var LedgerWalkDeviceReportSchema = object({
8583
+ deviceId: number().int(),
8584
+ hoursWalked: number().int().nonnegative(),
8585
+ hoursMissing: number().int().nonnegative(),
8586
+ ghostSegments: number().int().nonnegative(),
8587
+ ghostBytes: number().int().nonnegative(),
8588
+ forgottenSegments: number().int().nonnegative(),
8589
+ orphanFiles: number().int().nonnegative()
8590
+ });
8591
+ /**
8592
+ * What one walk claimed, listed, found and (only when armed) forgot.
8593
+ *
8594
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8595
+ * walk that saw a fraction of the location is visible in its own report rather
8596
+ * than in the absence of one.
8597
+ */
8598
+ var LedgerWalkReportSchema = object({
8599
+ locationId: string(),
8600
+ applied: boolean(),
8601
+ refused: LedgerWalkRefusalSchema.nullable(),
8602
+ archiveSegments: number().int().nonnegative().nullable(),
8603
+ archiveBytes: number().int().nonnegative().nullable(),
8604
+ hoursClaimed: number().int().nonnegative(),
8605
+ hoursWalked: number().int().nonnegative(),
8606
+ hoursMissing: number().int().nonnegative(),
8607
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8608
+ listings: number().int().nonnegative(),
8609
+ segmentsClaimed: number().int().nonnegative(),
8610
+ ghostSegments: number().int().nonnegative(),
8611
+ ghostBytes: number().int().nonnegative(),
8612
+ ghostHoursWhole: number().int().nonnegative(),
8613
+ forgottenSegments: number().int().nonnegative(),
8614
+ forgottenBytes: number().int().nonnegative(),
8615
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8616
+ orphanFiles: number().int().nonnegative(),
8617
+ orphanSample: array(string()).readonly(),
8618
+ hoursSkipped: number().int().nonnegative(),
8619
+ skippedByReason: LedgerWalkSkipCountsSchema,
8620
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8621
+ bounded: boolean(),
8622
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8623
+ });
8525
8624
  /** How many rows a media pass would still act on against a given target — the
8526
8625
  * media lane's denominator AND its residue, from ONE derivation so the two can
8527
8626
  * never disagree. `null` = the count could not be taken. */
@@ -18938,13 +19037,15 @@ var ListGroupsPageSchema = object({
18938
19037
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18939
19038
  nextCursor: string().nullable()
18940
19039
  });
19040
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19041
+ var KEY_EVENTS_MAX_LIMIT = 200;
18941
19042
  var KeyEventQueryInput = object({
18942
19043
  deviceId: number(),
18943
19044
  /** Window lower bound (track firstSeen ≥ since). */
18944
19045
  since: number(),
18945
19046
  /** Window upper bound (track firstSeen ≤ until). */
18946
19047
  until: number(),
18947
- limit: number().int().min(1).max(200).default(50),
19048
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18948
19049
  /** Drop tracks scoring below this importance. */
18949
19050
  minImportance: number().min(0).max(1).optional(),
18950
19051
  /** Restrict to a single class (e.g. 'person'). */
@@ -18966,6 +19067,32 @@ var KeyEventSchema = object({
18966
19067
  ...TrackFlagFields,
18967
19068
  ...TrackRetrainFields
18968
19069
  });
19070
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19071
+ var KeyEventBatchQueryInput = object({
19072
+ deviceIds: array(number()).min(1).max(200),
19073
+ since: number(),
19074
+ until: number(),
19075
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19076
+ * across the set, which would let a busy camera starve a quiet one of its
19077
+ * rows and change what the merged feed contains. */
19078
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19079
+ minImportance: number().min(0).max(1).optional(),
19080
+ classFilter: string().optional()
19081
+ });
19082
+ /**
19083
+ * One camera's key events in a batch answer.
19084
+ *
19085
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19086
+ * error rather than throwing, so a camera whose store read failed and one with
19087
+ * no events in the window were ALREADY indistinguishable per camera — the
19088
+ * batch does not make that worse, and the row keeps the deviceId the single
19089
+ * method's output never carried (the caller used to stamp it from the fan-out
19090
+ * key, which only worked because there was one query per camera).
19091
+ */
19092
+ var KeyEventsForDeviceSchema = object({
19093
+ deviceId: number(),
19094
+ events: array(KeyEventSchema).readonly()
19095
+ });
18969
19096
  object({
18970
19097
  trackId: string(),
18971
19098
  className: string(),
@@ -19235,7 +19362,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19235
19362
  until: number().optional(),
19236
19363
  kinds: array(string()).optional(),
19237
19364
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19238
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19365
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19239
19366
  deviceId: number(),
19240
19367
  since: number(),
19241
19368
  until: number(),
@@ -26887,6 +27014,9 @@ method(object({
26887
27014
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26888
27015
  kind: "query",
26889
27016
  auth: "admin"
27017
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27018
+ kind: "mutation",
27019
+ auth: "admin"
26890
27020
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26891
27021
  kind: "mutation",
26892
27022
  auth: "admin"
@@ -27278,7 +27408,26 @@ var SceneMonitorStatusSchema = object({
27278
27408
  monitors: array(SceneMonitorSchema),
27279
27409
  lastFetchedAt: number()
27280
27410
  });
27281
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27411
+ /**
27412
+ * One camera's row in a `listScenesBatch` answer.
27413
+ *
27414
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27415
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27416
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27417
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27418
+ * configured. Fanned out per camera the difference was visible — one query
27419
+ * errored while the others resolved — and a batch that returned only the rows
27420
+ * it managed would have destroyed it, silently, by making an unreachable camera
27421
+ * indistinguishable from one that answered `monitors: []`.
27422
+ *
27423
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27424
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27425
+ */
27426
+ var SceneMonitorStatusForDeviceSchema = object({
27427
+ deviceId: number(),
27428
+ status: SceneMonitorStatusSchema.nullable()
27429
+ });
27430
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27282
27431
  deviceId: number(),
27283
27432
  label: string(),
27284
27433
  roi: MaskRectShapeSchema,
@@ -28602,6 +28751,27 @@ var CameraOccupancySnapshotSchema = object({
28602
28751
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28603
28752
  });
28604
28753
  /**
28754
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28755
+ *
28756
+ * THREE outcomes, and the single-camera method could only express two of them
28757
+ * because `snapshot: null` was already spoken for:
28758
+ *
28759
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28760
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28761
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28762
+ * - `read: 'unreadable'` — the owner could not answer for this
28763
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28764
+ *
28765
+ * Collapsing the last two is the failure this field exists to prevent: a
28766
+ * hydration that threw would otherwise render as an empty Stationary section,
28767
+ * which is a definite claim about a camera nobody could read.
28768
+ */
28769
+ var CameraOccupancySnapshotForDeviceSchema = object({
28770
+ deviceId: number(),
28771
+ read: _enum(["read", "unreadable"]),
28772
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28773
+ });
28774
+ /**
28605
28775
  * Time-series resolution. The history methods return one bucket per
28606
28776
  * step over the requested range. Smaller resolutions cost more
28607
28777
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28625,7 +28795,7 @@ var HistoryPointSchema = object({
28625
28795
  /** Object count averaged over the bucket (rounded to nearest integer). */
28626
28796
  count: number().int().nonnegative()
28627
28797
  });
28628
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28798
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28629
28799
  deviceId: number(),
28630
28800
  zoneId: string(),
28631
28801
  className: string().optional()
@@ -31930,6 +32100,12 @@ Object.freeze({
31930
32100
  addonId: null,
31931
32101
  access: "view"
31932
32102
  },
32103
+ "pipelineAnalytics.getKeyEventsBatch": {
32104
+ capName: "pipeline-analytics",
32105
+ capScope: "device",
32106
+ addonId: null,
32107
+ access: "view"
32108
+ },
31933
32109
  "pipelineAnalytics.getMotionEvents": {
31934
32110
  capName: "pipeline-analytics",
31935
32111
  capScope: "device",
@@ -33106,6 +33282,12 @@ Object.freeze({
33106
33282
  addonId: null,
33107
33283
  access: "view"
33108
33284
  },
33285
+ "recording.reconcileLedgerAgainstDisk": {
33286
+ capName: "recording",
33287
+ capScope: "system",
33288
+ addonId: null,
33289
+ access: "create"
33290
+ },
33109
33291
  "recording.refreshStorageLocationsForMigration": {
33110
33292
  capName: "recording",
33111
33293
  capScope: "system",
@@ -33232,6 +33414,12 @@ Object.freeze({
33232
33414
  addonId: null,
33233
33415
  access: "view"
33234
33416
  },
33417
+ "sceneMonitor.listScenesBatch": {
33418
+ capName: "scene-monitor",
33419
+ capScope: "device",
33420
+ addonId: null,
33421
+ access: "view"
33422
+ },
33235
33423
  "sceneMonitor.recheckNow": {
33236
33424
  capName: "scene-monitor",
33237
33425
  capScope: "device",
@@ -34588,6 +34776,12 @@ Object.freeze({
34588
34776
  addonId: null,
34589
34777
  access: "view"
34590
34778
  },
34779
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34780
+ capName: "zone-analytics",
34781
+ capScope: "device",
34782
+ addonId: null,
34783
+ access: "view"
34784
+ },
34591
34785
  "zoneAnalytics.getUnzonedHistory": {
34592
34786
  capName: "zone-analytics",
34593
34787
  capScope: "device",
@@ -35592,6 +35786,11 @@ Object.freeze({
35592
35786
  form: "single",
35593
35787
  optional: false
35594
35788
  }],
35789
+ "pipelineAnalytics.getKeyEventsBatch": [{
35790
+ name: "deviceIds",
35791
+ form: "array",
35792
+ optional: false
35793
+ }],
35595
35794
  "pipelineAnalytics.getMotionEvents": [{
35596
35795
  name: "deviceId",
35597
35796
  form: "single",
@@ -36032,6 +36231,11 @@ Object.freeze({
36032
36231
  form: "single",
36033
36232
  optional: false
36034
36233
  }],
36234
+ "recording.reconcileLedgerAgainstDisk": [{
36235
+ name: "deviceId",
36236
+ form: "single",
36237
+ optional: true
36238
+ }],
36035
36239
  "recording.relocateFootage": [{
36036
36240
  name: "deviceId",
36037
36241
  form: "single",
@@ -36097,6 +36301,11 @@ Object.freeze({
36097
36301
  form: "single",
36098
36302
  optional: false
36099
36303
  }],
36304
+ "sceneMonitor.listScenesBatch": [{
36305
+ name: "deviceIds",
36306
+ form: "array",
36307
+ optional: false
36308
+ }],
36100
36309
  "sceneMonitor.recheckNow": [{
36101
36310
  name: "deviceId",
36102
36311
  form: "single",
@@ -36358,6 +36567,11 @@ Object.freeze({
36358
36567
  form: "single",
36359
36568
  optional: false
36360
36569
  }],
36570
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36571
+ name: "deviceIds",
36572
+ form: "array",
36573
+ optional: false
36574
+ }],
36361
36575
  "zoneAnalytics.getUnzonedHistory": [{
36362
36576
  name: "deviceId",
36363
36577
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-alexa",
3
- "version": "1.2.54",
3
+ "version": "1.2.55",
4
4
  "description": "Alexa Smart Home Skill export — hub-side OAuth provider + directive handler. The companion AWS Lambda forwards Alexa directives to this addon's /addon/export-alexa/directive endpoint.",
5
5
  "keywords": [
6
6
  "camstack",