@camstack/addon-export-google 0.1.12 → 0.1.13

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.
@@ -8211,6 +8211,20 @@ var RelocateJobSchema = object({
8211
8211
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8212
8212
  */
8213
8213
  rowsReconciled: number().int().nonnegative().optional(),
8214
+ /**
8215
+ * Rows this run FORGOT because the file they name is not on disk.
8216
+ *
8217
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8218
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8219
+ * and the durable row is dropped through the same channel eviction uses. It
8220
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8221
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8222
+ * the same failure as one that quietly skips them (D295).
8223
+ *
8224
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8225
+ * ledger claimed 5.65 GB of footage that no longer existed.
8226
+ */
8227
+ rowsForgotten: number().int().nonnegative().optional(),
8214
8228
  startedAt: number(),
8215
8229
  finishedAt: number().nullable(),
8216
8230
  error: string().nullable()
@@ -8574,6 +8588,91 @@ var RelocateResidueSchema = object({
8574
8588
  segments: number().int().nonnegative(),
8575
8589
  bytes: number().int().nonnegative()
8576
8590
  }).nullable();
8591
+ /**
8592
+ * Ask one location whether its durable hour rows describe the disk — the walk
8593
+ * (D319).
8594
+ *
8595
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8596
+ * missing tool is the question, and the dry run is how they sanity-check the
8597
+ * destructive run before authorising it.
8598
+ */
8599
+ var LedgerWalkInputSchema = object({
8600
+ locationId: string().min(1),
8601
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8602
+ apply: boolean().optional(),
8603
+ /** Narrow to one camera. */
8604
+ deviceId: number().int().positive().optional(),
8605
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8606
+ profiles: array(string().min(1)).optional()
8607
+ });
8608
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8609
+ var LedgerWalkRefusalSchema = _enum([
8610
+ "location-unknown",
8611
+ "source-writable",
8612
+ "no-ledger",
8613
+ "archive-unreadable",
8614
+ "anchor-absent",
8615
+ "anchor-unreadable",
8616
+ "anchor-moved"
8617
+ ]);
8618
+ _enum([
8619
+ "live-tail",
8620
+ "listing-error",
8621
+ "path-mismatch",
8622
+ "durable-refused"
8623
+ ]);
8624
+ /** Every skip reason, always present, always a number — so a reason that never
8625
+ * fired reports as zero rather than absent and the report shape is constant
8626
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8627
+ var LedgerWalkSkipCountsSchema = object({
8628
+ "live-tail": number().int().nonnegative(),
8629
+ "listing-error": number().int().nonnegative(),
8630
+ "path-mismatch": number().int().nonnegative(),
8631
+ "durable-refused": number().int().nonnegative()
8632
+ });
8633
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8634
+ var LedgerWalkDeviceReportSchema = object({
8635
+ deviceId: number().int(),
8636
+ hoursWalked: number().int().nonnegative(),
8637
+ hoursMissing: number().int().nonnegative(),
8638
+ ghostSegments: number().int().nonnegative(),
8639
+ ghostBytes: number().int().nonnegative(),
8640
+ forgottenSegments: number().int().nonnegative(),
8641
+ orphanFiles: number().int().nonnegative()
8642
+ });
8643
+ /**
8644
+ * What one walk claimed, listed, found and (only when armed) forgot.
8645
+ *
8646
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8647
+ * walk that saw a fraction of the location is visible in its own report rather
8648
+ * than in the absence of one.
8649
+ */
8650
+ var LedgerWalkReportSchema = object({
8651
+ locationId: string(),
8652
+ applied: boolean(),
8653
+ refused: LedgerWalkRefusalSchema.nullable(),
8654
+ archiveSegments: number().int().nonnegative().nullable(),
8655
+ archiveBytes: number().int().nonnegative().nullable(),
8656
+ hoursClaimed: number().int().nonnegative(),
8657
+ hoursWalked: number().int().nonnegative(),
8658
+ hoursMissing: number().int().nonnegative(),
8659
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8660
+ listings: number().int().nonnegative(),
8661
+ segmentsClaimed: number().int().nonnegative(),
8662
+ ghostSegments: number().int().nonnegative(),
8663
+ ghostBytes: number().int().nonnegative(),
8664
+ ghostHoursWhole: number().int().nonnegative(),
8665
+ forgottenSegments: number().int().nonnegative(),
8666
+ forgottenBytes: number().int().nonnegative(),
8667
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8668
+ orphanFiles: number().int().nonnegative(),
8669
+ orphanSample: array(string()).readonly(),
8670
+ hoursSkipped: number().int().nonnegative(),
8671
+ skippedByReason: LedgerWalkSkipCountsSchema,
8672
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8673
+ bounded: boolean(),
8674
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8675
+ });
8577
8676
  /** How many rows a media pass would still act on against a given target — the
8578
8677
  * media lane's denominator AND its residue, from ONE derivation so the two can
8579
8678
  * never disagree. `null` = the count could not be taken. */
@@ -18936,13 +19035,15 @@ var ListGroupsPageSchema = object({
18936
19035
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18937
19036
  nextCursor: string().nullable()
18938
19037
  });
19038
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19039
+ var KEY_EVENTS_MAX_LIMIT = 200;
18939
19040
  var KeyEventQueryInput = object({
18940
19041
  deviceId: number(),
18941
19042
  /** Window lower bound (track firstSeen ≥ since). */
18942
19043
  since: number(),
18943
19044
  /** Window upper bound (track firstSeen ≤ until). */
18944
19045
  until: number(),
18945
- limit: number().int().min(1).max(200).default(50),
19046
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18946
19047
  /** Drop tracks scoring below this importance. */
18947
19048
  minImportance: number().min(0).max(1).optional(),
18948
19049
  /** Restrict to a single class (e.g. 'person'). */
@@ -18964,6 +19065,32 @@ var KeyEventSchema = object({
18964
19065
  ...TrackFlagFields,
18965
19066
  ...TrackRetrainFields
18966
19067
  });
19068
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19069
+ var KeyEventBatchQueryInput = object({
19070
+ deviceIds: array(number()).min(1).max(200),
19071
+ since: number(),
19072
+ until: number(),
19073
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19074
+ * across the set, which would let a busy camera starve a quiet one of its
19075
+ * rows and change what the merged feed contains. */
19076
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19077
+ minImportance: number().min(0).max(1).optional(),
19078
+ classFilter: string().optional()
19079
+ });
19080
+ /**
19081
+ * One camera's key events in a batch answer.
19082
+ *
19083
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19084
+ * error rather than throwing, so a camera whose store read failed and one with
19085
+ * no events in the window were ALREADY indistinguishable per camera — the
19086
+ * batch does not make that worse, and the row keeps the deviceId the single
19087
+ * method's output never carried (the caller used to stamp it from the fan-out
19088
+ * key, which only worked because there was one query per camera).
19089
+ */
19090
+ var KeyEventsForDeviceSchema = object({
19091
+ deviceId: number(),
19092
+ events: array(KeyEventSchema).readonly()
19093
+ });
18967
19094
  object({
18968
19095
  trackId: string(),
18969
19096
  className: string(),
@@ -19233,7 +19360,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19233
19360
  until: number().optional(),
19234
19361
  kinds: array(string()).optional(),
19235
19362
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19236
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19363
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19237
19364
  deviceId: number(),
19238
19365
  since: number(),
19239
19366
  until: number(),
@@ -26897,6 +27024,9 @@ method(object({
26897
27024
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26898
27025
  kind: "query",
26899
27026
  auth: "admin"
27027
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27028
+ kind: "mutation",
27029
+ auth: "admin"
26900
27030
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26901
27031
  kind: "mutation",
26902
27032
  auth: "admin"
@@ -27288,7 +27418,26 @@ var SceneMonitorStatusSchema = object({
27288
27418
  monitors: array(SceneMonitorSchema),
27289
27419
  lastFetchedAt: number()
27290
27420
  });
27291
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27421
+ /**
27422
+ * One camera's row in a `listScenesBatch` answer.
27423
+ *
27424
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27425
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27426
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27427
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27428
+ * configured. Fanned out per camera the difference was visible — one query
27429
+ * errored while the others resolved — and a batch that returned only the rows
27430
+ * it managed would have destroyed it, silently, by making an unreachable camera
27431
+ * indistinguishable from one that answered `monitors: []`.
27432
+ *
27433
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27434
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27435
+ */
27436
+ var SceneMonitorStatusForDeviceSchema = object({
27437
+ deviceId: number(),
27438
+ status: SceneMonitorStatusSchema.nullable()
27439
+ });
27440
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27292
27441
  deviceId: number(),
27293
27442
  label: string(),
27294
27443
  roi: MaskRectShapeSchema,
@@ -28619,6 +28768,27 @@ var CameraOccupancySnapshotSchema = object({
28619
28768
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28620
28769
  });
28621
28770
  /**
28771
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28772
+ *
28773
+ * THREE outcomes, and the single-camera method could only express two of them
28774
+ * because `snapshot: null` was already spoken for:
28775
+ *
28776
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28777
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28778
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28779
+ * - `read: 'unreadable'` — the owner could not answer for this
28780
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28781
+ *
28782
+ * Collapsing the last two is the failure this field exists to prevent: a
28783
+ * hydration that threw would otherwise render as an empty Stationary section,
28784
+ * which is a definite claim about a camera nobody could read.
28785
+ */
28786
+ var CameraOccupancySnapshotForDeviceSchema = object({
28787
+ deviceId: number(),
28788
+ read: _enum(["read", "unreadable"]),
28789
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28790
+ });
28791
+ /**
28622
28792
  * Time-series resolution. The history methods return one bucket per
28623
28793
  * step over the requested range. Smaller resolutions cost more
28624
28794
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28642,7 +28812,7 @@ var HistoryPointSchema = object({
28642
28812
  /** Object count averaged over the bucket (rounded to nearest integer). */
28643
28813
  count: number().int().nonnegative()
28644
28814
  });
28645
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28815
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28646
28816
  deviceId: number(),
28647
28817
  zoneId: string(),
28648
28818
  className: string().optional()
@@ -31947,6 +32117,12 @@ Object.freeze({
31947
32117
  addonId: null,
31948
32118
  access: "view"
31949
32119
  },
32120
+ "pipelineAnalytics.getKeyEventsBatch": {
32121
+ capName: "pipeline-analytics",
32122
+ capScope: "device",
32123
+ addonId: null,
32124
+ access: "view"
32125
+ },
31950
32126
  "pipelineAnalytics.getMotionEvents": {
31951
32127
  capName: "pipeline-analytics",
31952
32128
  capScope: "device",
@@ -33123,6 +33299,12 @@ Object.freeze({
33123
33299
  addonId: null,
33124
33300
  access: "view"
33125
33301
  },
33302
+ "recording.reconcileLedgerAgainstDisk": {
33303
+ capName: "recording",
33304
+ capScope: "system",
33305
+ addonId: null,
33306
+ access: "create"
33307
+ },
33126
33308
  "recording.refreshStorageLocationsForMigration": {
33127
33309
  capName: "recording",
33128
33310
  capScope: "system",
@@ -33249,6 +33431,12 @@ Object.freeze({
33249
33431
  addonId: null,
33250
33432
  access: "view"
33251
33433
  },
33434
+ "sceneMonitor.listScenesBatch": {
33435
+ capName: "scene-monitor",
33436
+ capScope: "device",
33437
+ addonId: null,
33438
+ access: "view"
33439
+ },
33252
33440
  "sceneMonitor.recheckNow": {
33253
33441
  capName: "scene-monitor",
33254
33442
  capScope: "device",
@@ -34605,6 +34793,12 @@ Object.freeze({
34605
34793
  addonId: null,
34606
34794
  access: "view"
34607
34795
  },
34796
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34797
+ capName: "zone-analytics",
34798
+ capScope: "device",
34799
+ addonId: null,
34800
+ access: "view"
34801
+ },
34608
34802
  "zoneAnalytics.getUnzonedHistory": {
34609
34803
  capName: "zone-analytics",
34610
34804
  capScope: "device",
@@ -35609,6 +35803,11 @@ Object.freeze({
35609
35803
  form: "single",
35610
35804
  optional: false
35611
35805
  }],
35806
+ "pipelineAnalytics.getKeyEventsBatch": [{
35807
+ name: "deviceIds",
35808
+ form: "array",
35809
+ optional: false
35810
+ }],
35612
35811
  "pipelineAnalytics.getMotionEvents": [{
35613
35812
  name: "deviceId",
35614
35813
  form: "single",
@@ -36049,6 +36248,11 @@ Object.freeze({
36049
36248
  form: "single",
36050
36249
  optional: false
36051
36250
  }],
36251
+ "recording.reconcileLedgerAgainstDisk": [{
36252
+ name: "deviceId",
36253
+ form: "single",
36254
+ optional: true
36255
+ }],
36052
36256
  "recording.relocateFootage": [{
36053
36257
  name: "deviceId",
36054
36258
  form: "single",
@@ -36114,6 +36318,11 @@ Object.freeze({
36114
36318
  form: "single",
36115
36319
  optional: false
36116
36320
  }],
36321
+ "sceneMonitor.listScenesBatch": [{
36322
+ name: "deviceIds",
36323
+ form: "array",
36324
+ optional: false
36325
+ }],
36117
36326
  "sceneMonitor.recheckNow": [{
36118
36327
  name: "deviceId",
36119
36328
  form: "single",
@@ -36375,6 +36584,11 @@ Object.freeze({
36375
36584
  form: "single",
36376
36585
  optional: false
36377
36586
  }],
36587
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36588
+ name: "deviceIds",
36589
+ form: "array",
36590
+ optional: false
36591
+ }],
36378
36592
  "zoneAnalytics.getUnzonedHistory": [{
36379
36593
  name: "deviceId",
36380
36594
  form: "single",
@@ -8207,6 +8207,20 @@ var RelocateJobSchema = object({
8207
8207
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8208
8208
  */
8209
8209
  rowsReconciled: number().int().nonnegative().optional(),
8210
+ /**
8211
+ * Rows this run FORGOT because the file they name is not on disk.
8212
+ *
8213
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8214
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8215
+ * and the durable row is dropped through the same channel eviction uses. It
8216
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8217
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8218
+ * the same failure as one that quietly skips them (D295).
8219
+ *
8220
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8221
+ * ledger claimed 5.65 GB of footage that no longer existed.
8222
+ */
8223
+ rowsForgotten: number().int().nonnegative().optional(),
8210
8224
  startedAt: number(),
8211
8225
  finishedAt: number().nullable(),
8212
8226
  error: string().nullable()
@@ -8570,6 +8584,91 @@ var RelocateResidueSchema = object({
8570
8584
  segments: number().int().nonnegative(),
8571
8585
  bytes: number().int().nonnegative()
8572
8586
  }).nullable();
8587
+ /**
8588
+ * Ask one location whether its durable hour rows describe the disk — the walk
8589
+ * (D319).
8590
+ *
8591
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8592
+ * missing tool is the question, and the dry run is how they sanity-check the
8593
+ * destructive run before authorising it.
8594
+ */
8595
+ var LedgerWalkInputSchema = object({
8596
+ locationId: string().min(1),
8597
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8598
+ apply: boolean().optional(),
8599
+ /** Narrow to one camera. */
8600
+ deviceId: number().int().positive().optional(),
8601
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8602
+ profiles: array(string().min(1)).optional()
8603
+ });
8604
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8605
+ var LedgerWalkRefusalSchema = _enum([
8606
+ "location-unknown",
8607
+ "source-writable",
8608
+ "no-ledger",
8609
+ "archive-unreadable",
8610
+ "anchor-absent",
8611
+ "anchor-unreadable",
8612
+ "anchor-moved"
8613
+ ]);
8614
+ _enum([
8615
+ "live-tail",
8616
+ "listing-error",
8617
+ "path-mismatch",
8618
+ "durable-refused"
8619
+ ]);
8620
+ /** Every skip reason, always present, always a number — so a reason that never
8621
+ * fired reports as zero rather than absent and the report shape is constant
8622
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8623
+ var LedgerWalkSkipCountsSchema = object({
8624
+ "live-tail": number().int().nonnegative(),
8625
+ "listing-error": number().int().nonnegative(),
8626
+ "path-mismatch": number().int().nonnegative(),
8627
+ "durable-refused": number().int().nonnegative()
8628
+ });
8629
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8630
+ var LedgerWalkDeviceReportSchema = object({
8631
+ deviceId: number().int(),
8632
+ hoursWalked: number().int().nonnegative(),
8633
+ hoursMissing: number().int().nonnegative(),
8634
+ ghostSegments: number().int().nonnegative(),
8635
+ ghostBytes: number().int().nonnegative(),
8636
+ forgottenSegments: number().int().nonnegative(),
8637
+ orphanFiles: number().int().nonnegative()
8638
+ });
8639
+ /**
8640
+ * What one walk claimed, listed, found and (only when armed) forgot.
8641
+ *
8642
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8643
+ * walk that saw a fraction of the location is visible in its own report rather
8644
+ * than in the absence of one.
8645
+ */
8646
+ var LedgerWalkReportSchema = object({
8647
+ locationId: string(),
8648
+ applied: boolean(),
8649
+ refused: LedgerWalkRefusalSchema.nullable(),
8650
+ archiveSegments: number().int().nonnegative().nullable(),
8651
+ archiveBytes: number().int().nonnegative().nullable(),
8652
+ hoursClaimed: number().int().nonnegative(),
8653
+ hoursWalked: number().int().nonnegative(),
8654
+ hoursMissing: number().int().nonnegative(),
8655
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8656
+ listings: number().int().nonnegative(),
8657
+ segmentsClaimed: number().int().nonnegative(),
8658
+ ghostSegments: number().int().nonnegative(),
8659
+ ghostBytes: number().int().nonnegative(),
8660
+ ghostHoursWhole: number().int().nonnegative(),
8661
+ forgottenSegments: number().int().nonnegative(),
8662
+ forgottenBytes: number().int().nonnegative(),
8663
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8664
+ orphanFiles: number().int().nonnegative(),
8665
+ orphanSample: array(string()).readonly(),
8666
+ hoursSkipped: number().int().nonnegative(),
8667
+ skippedByReason: LedgerWalkSkipCountsSchema,
8668
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8669
+ bounded: boolean(),
8670
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8671
+ });
8573
8672
  /** How many rows a media pass would still act on against a given target — the
8574
8673
  * media lane's denominator AND its residue, from ONE derivation so the two can
8575
8674
  * never disagree. `null` = the count could not be taken. */
@@ -18932,13 +19031,15 @@ var ListGroupsPageSchema = object({
18932
19031
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18933
19032
  nextCursor: string().nullable()
18934
19033
  });
19034
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19035
+ var KEY_EVENTS_MAX_LIMIT = 200;
18935
19036
  var KeyEventQueryInput = object({
18936
19037
  deviceId: number(),
18937
19038
  /** Window lower bound (track firstSeen ≥ since). */
18938
19039
  since: number(),
18939
19040
  /** Window upper bound (track firstSeen ≤ until). */
18940
19041
  until: number(),
18941
- limit: number().int().min(1).max(200).default(50),
19042
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18942
19043
  /** Drop tracks scoring below this importance. */
18943
19044
  minImportance: number().min(0).max(1).optional(),
18944
19045
  /** Restrict to a single class (e.g. 'person'). */
@@ -18960,6 +19061,32 @@ var KeyEventSchema = object({
18960
19061
  ...TrackFlagFields,
18961
19062
  ...TrackRetrainFields
18962
19063
  });
19064
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19065
+ var KeyEventBatchQueryInput = object({
19066
+ deviceIds: array(number()).min(1).max(200),
19067
+ since: number(),
19068
+ until: number(),
19069
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19070
+ * across the set, which would let a busy camera starve a quiet one of its
19071
+ * rows and change what the merged feed contains. */
19072
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19073
+ minImportance: number().min(0).max(1).optional(),
19074
+ classFilter: string().optional()
19075
+ });
19076
+ /**
19077
+ * One camera's key events in a batch answer.
19078
+ *
19079
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19080
+ * error rather than throwing, so a camera whose store read failed and one with
19081
+ * no events in the window were ALREADY indistinguishable per camera — the
19082
+ * batch does not make that worse, and the row keeps the deviceId the single
19083
+ * method's output never carried (the caller used to stamp it from the fan-out
19084
+ * key, which only worked because there was one query per camera).
19085
+ */
19086
+ var KeyEventsForDeviceSchema = object({
19087
+ deviceId: number(),
19088
+ events: array(KeyEventSchema).readonly()
19089
+ });
18963
19090
  object({
18964
19091
  trackId: string(),
18965
19092
  className: string(),
@@ -19229,7 +19356,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19229
19356
  until: number().optional(),
19230
19357
  kinds: array(string()).optional(),
19231
19358
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19232
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19359
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19233
19360
  deviceId: number(),
19234
19361
  since: number(),
19235
19362
  until: number(),
@@ -26893,6 +27020,9 @@ method(object({
26893
27020
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26894
27021
  kind: "query",
26895
27022
  auth: "admin"
27023
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27024
+ kind: "mutation",
27025
+ auth: "admin"
26896
27026
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26897
27027
  kind: "mutation",
26898
27028
  auth: "admin"
@@ -27284,7 +27414,26 @@ var SceneMonitorStatusSchema = object({
27284
27414
  monitors: array(SceneMonitorSchema),
27285
27415
  lastFetchedAt: number()
27286
27416
  });
27287
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27417
+ /**
27418
+ * One camera's row in a `listScenesBatch` answer.
27419
+ *
27420
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27421
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27422
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27423
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27424
+ * configured. Fanned out per camera the difference was visible — one query
27425
+ * errored while the others resolved — and a batch that returned only the rows
27426
+ * it managed would have destroyed it, silently, by making an unreachable camera
27427
+ * indistinguishable from one that answered `monitors: []`.
27428
+ *
27429
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27430
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27431
+ */
27432
+ var SceneMonitorStatusForDeviceSchema = object({
27433
+ deviceId: number(),
27434
+ status: SceneMonitorStatusSchema.nullable()
27435
+ });
27436
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27288
27437
  deviceId: number(),
27289
27438
  label: string(),
27290
27439
  roi: MaskRectShapeSchema,
@@ -28615,6 +28764,27 @@ var CameraOccupancySnapshotSchema = object({
28615
28764
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28616
28765
  });
28617
28766
  /**
28767
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28768
+ *
28769
+ * THREE outcomes, and the single-camera method could only express two of them
28770
+ * because `snapshot: null` was already spoken for:
28771
+ *
28772
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28773
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28774
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28775
+ * - `read: 'unreadable'` — the owner could not answer for this
28776
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28777
+ *
28778
+ * Collapsing the last two is the failure this field exists to prevent: a
28779
+ * hydration that threw would otherwise render as an empty Stationary section,
28780
+ * which is a definite claim about a camera nobody could read.
28781
+ */
28782
+ var CameraOccupancySnapshotForDeviceSchema = object({
28783
+ deviceId: number(),
28784
+ read: _enum(["read", "unreadable"]),
28785
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28786
+ });
28787
+ /**
28618
28788
  * Time-series resolution. The history methods return one bucket per
28619
28789
  * step over the requested range. Smaller resolutions cost more
28620
28790
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28638,7 +28808,7 @@ var HistoryPointSchema = object({
28638
28808
  /** Object count averaged over the bucket (rounded to nearest integer). */
28639
28809
  count: number().int().nonnegative()
28640
28810
  });
28641
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28811
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28642
28812
  deviceId: number(),
28643
28813
  zoneId: string(),
28644
28814
  className: string().optional()
@@ -31943,6 +32113,12 @@ Object.freeze({
31943
32113
  addonId: null,
31944
32114
  access: "view"
31945
32115
  },
32116
+ "pipelineAnalytics.getKeyEventsBatch": {
32117
+ capName: "pipeline-analytics",
32118
+ capScope: "device",
32119
+ addonId: null,
32120
+ access: "view"
32121
+ },
31946
32122
  "pipelineAnalytics.getMotionEvents": {
31947
32123
  capName: "pipeline-analytics",
31948
32124
  capScope: "device",
@@ -33119,6 +33295,12 @@ Object.freeze({
33119
33295
  addonId: null,
33120
33296
  access: "view"
33121
33297
  },
33298
+ "recording.reconcileLedgerAgainstDisk": {
33299
+ capName: "recording",
33300
+ capScope: "system",
33301
+ addonId: null,
33302
+ access: "create"
33303
+ },
33122
33304
  "recording.refreshStorageLocationsForMigration": {
33123
33305
  capName: "recording",
33124
33306
  capScope: "system",
@@ -33245,6 +33427,12 @@ Object.freeze({
33245
33427
  addonId: null,
33246
33428
  access: "view"
33247
33429
  },
33430
+ "sceneMonitor.listScenesBatch": {
33431
+ capName: "scene-monitor",
33432
+ capScope: "device",
33433
+ addonId: null,
33434
+ access: "view"
33435
+ },
33248
33436
  "sceneMonitor.recheckNow": {
33249
33437
  capName: "scene-monitor",
33250
33438
  capScope: "device",
@@ -34601,6 +34789,12 @@ Object.freeze({
34601
34789
  addonId: null,
34602
34790
  access: "view"
34603
34791
  },
34792
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34793
+ capName: "zone-analytics",
34794
+ capScope: "device",
34795
+ addonId: null,
34796
+ access: "view"
34797
+ },
34604
34798
  "zoneAnalytics.getUnzonedHistory": {
34605
34799
  capName: "zone-analytics",
34606
34800
  capScope: "device",
@@ -35605,6 +35799,11 @@ Object.freeze({
35605
35799
  form: "single",
35606
35800
  optional: false
35607
35801
  }],
35802
+ "pipelineAnalytics.getKeyEventsBatch": [{
35803
+ name: "deviceIds",
35804
+ form: "array",
35805
+ optional: false
35806
+ }],
35608
35807
  "pipelineAnalytics.getMotionEvents": [{
35609
35808
  name: "deviceId",
35610
35809
  form: "single",
@@ -36045,6 +36244,11 @@ Object.freeze({
36045
36244
  form: "single",
36046
36245
  optional: false
36047
36246
  }],
36247
+ "recording.reconcileLedgerAgainstDisk": [{
36248
+ name: "deviceId",
36249
+ form: "single",
36250
+ optional: true
36251
+ }],
36048
36252
  "recording.relocateFootage": [{
36049
36253
  name: "deviceId",
36050
36254
  form: "single",
@@ -36110,6 +36314,11 @@ Object.freeze({
36110
36314
  form: "single",
36111
36315
  optional: false
36112
36316
  }],
36317
+ "sceneMonitor.listScenesBatch": [{
36318
+ name: "deviceIds",
36319
+ form: "array",
36320
+ optional: false
36321
+ }],
36113
36322
  "sceneMonitor.recheckNow": [{
36114
36323
  name: "deviceId",
36115
36324
  form: "single",
@@ -36371,6 +36580,11 @@ Object.freeze({
36371
36580
  form: "single",
36372
36581
  optional: false
36373
36582
  }],
36583
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36584
+ name: "deviceIds",
36585
+ form: "array",
36586
+ optional: false
36587
+ }],
36374
36588
  "zoneAnalytics.getUnzonedHistory": [{
36375
36589
  name: "deviceId",
36376
36590
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-google",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Google Home export — hub-side smart-home fulfillment (SYNC / QUERY / EXECUTE / DISCONNECT) for the non-camera fleet, served over the hub's own OAuth account link. No Google credential is stored, sent or required.",
5
5
  "keywords": [
6
6
  "camstack",