@camstack/system 1.2.161 → 1.2.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  41. package/dist/builtins/storage-orchestrator/location-secrets.d.ts +42 -2
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +7 -1
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +67 -41
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +67 -41
  45. package/dist/builtins/system-config/system-config.addon.js +1 -1
  46. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  47. package/dist/builtins/winston-logging/index.js +1 -1
  48. package/dist/builtins/winston-logging/index.mjs +1 -1
  49. package/dist/{dist-Keu5TDO7.js → dist-B29Skpzo.js} +359 -1
  50. package/dist/{dist-D4xNBlJM.mjs → dist-DxFjna2a.mjs} +359 -1
  51. package/dist/index.js +74 -11
  52. package/dist/index.mjs +74 -11
  53. package/dist/{manifest-system-deps-CiCuev2R.mjs → manifest-system-deps-CG9ESHhv.mjs} +1 -1
  54. package/dist/{manifest-system-deps-DYv4ZPo2.js → manifest-system-deps-DBje540e.js} +1 -1
  55. package/dist/{retired-settings-keys-BBohF-eC.js → retired-settings-keys-CRL0qOnU.js} +1 -1
  56. package/dist/{retired-settings-keys-COQMNlOJ.mjs → retired-settings-keys-N0NZzDE3.mjs} +1 -1
  57. package/package.json +1 -1
@@ -4192,6 +4192,20 @@ var RelocateJobSchema = zod.z.object({
4192
4192
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
4193
4193
  */
4194
4194
  rowsReconciled: zod.z.number().int().nonnegative().optional(),
4195
+ /**
4196
+ * Rows this run FORGOT because the file they name is not on disk.
4197
+ *
4198
+ * The mover derived the path from the row's own fields and `stat`ed it; an
4199
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
4200
+ * and the durable row is dropped through the same channel eviction uses. It
4201
+ * is reported for the same reason `rowsReconciled` is: this is a durable
4202
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
4203
+ * the same failure as one that quietly skips them (D295).
4204
+ *
4205
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
4206
+ * ledger claimed 5.65 GB of footage that no longer existed.
4207
+ */
4208
+ rowsForgotten: zod.z.number().int().nonnegative().optional(),
4195
4209
  startedAt: zod.z.number(),
4196
4210
  finishedAt: zod.z.number().nullable(),
4197
4211
  error: zod.z.string().nullable()
@@ -4587,6 +4601,91 @@ var RelocateResidueSchema = zod.z.object({
4587
4601
  segments: zod.z.number().int().nonnegative(),
4588
4602
  bytes: zod.z.number().int().nonnegative()
4589
4603
  }).nullable();
4604
+ /**
4605
+ * Ask one location whether its durable hour rows describe the disk — the walk
4606
+ * (D319).
4607
+ *
4608
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
4609
+ * missing tool is the question, and the dry run is how they sanity-check the
4610
+ * destructive run before authorising it.
4611
+ */
4612
+ var LedgerWalkInputSchema = zod.z.object({
4613
+ locationId: zod.z.string().min(1),
4614
+ /** Forget the confirmed-absent rows, rather than only counting them. */
4615
+ apply: zod.z.boolean().optional(),
4616
+ /** Narrow to one camera. */
4617
+ deviceId: zod.z.number().int().positive().optional(),
4618
+ /** Narrow to these recording profiles; empty/absent = every profile. */
4619
+ profiles: zod.z.array(zod.z.string().min(1)).optional()
4620
+ });
4621
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
4622
+ var LedgerWalkRefusalSchema = zod.z.enum([
4623
+ "location-unknown",
4624
+ "source-writable",
4625
+ "no-ledger",
4626
+ "archive-unreadable",
4627
+ "anchor-absent",
4628
+ "anchor-unreadable",
4629
+ "anchor-moved"
4630
+ ]);
4631
+ zod.z.enum([
4632
+ "live-tail",
4633
+ "listing-error",
4634
+ "path-mismatch",
4635
+ "durable-refused"
4636
+ ]);
4637
+ /** Every skip reason, always present, always a number — so a reason that never
4638
+ * fired reports as zero rather than absent and the report shape is constant
4639
+ * between passes. Spelled out rather than `z.record` for exactly that. */
4640
+ var LedgerWalkSkipCountsSchema = zod.z.object({
4641
+ "live-tail": zod.z.number().int().nonnegative(),
4642
+ "listing-error": zod.z.number().int().nonnegative(),
4643
+ "path-mismatch": zod.z.number().int().nonnegative(),
4644
+ "durable-refused": zod.z.number().int().nonnegative()
4645
+ });
4646
+ /** One camera's share of a walk, so a report names cameras and not rows. */
4647
+ var LedgerWalkDeviceReportSchema = zod.z.object({
4648
+ deviceId: zod.z.number().int(),
4649
+ hoursWalked: zod.z.number().int().nonnegative(),
4650
+ hoursMissing: zod.z.number().int().nonnegative(),
4651
+ ghostSegments: zod.z.number().int().nonnegative(),
4652
+ ghostBytes: zod.z.number().int().nonnegative(),
4653
+ forgottenSegments: zod.z.number().int().nonnegative(),
4654
+ orphanFiles: zod.z.number().int().nonnegative()
4655
+ });
4656
+ /**
4657
+ * What one walk claimed, listed, found and (only when armed) forgot.
4658
+ *
4659
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
4660
+ * walk that saw a fraction of the location is visible in its own report rather
4661
+ * than in the absence of one.
4662
+ */
4663
+ var LedgerWalkReportSchema = zod.z.object({
4664
+ locationId: zod.z.string(),
4665
+ applied: zod.z.boolean(),
4666
+ refused: LedgerWalkRefusalSchema.nullable(),
4667
+ archiveSegments: zod.z.number().int().nonnegative().nullable(),
4668
+ archiveBytes: zod.z.number().int().nonnegative().nullable(),
4669
+ hoursClaimed: zod.z.number().int().nonnegative(),
4670
+ hoursWalked: zod.z.number().int().nonnegative(),
4671
+ hoursMissing: zod.z.number().int().nonnegative(),
4672
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
4673
+ listings: zod.z.number().int().nonnegative(),
4674
+ segmentsClaimed: zod.z.number().int().nonnegative(),
4675
+ ghostSegments: zod.z.number().int().nonnegative(),
4676
+ ghostBytes: zod.z.number().int().nonnegative(),
4677
+ ghostHoursWhole: zod.z.number().int().nonnegative(),
4678
+ forgottenSegments: zod.z.number().int().nonnegative(),
4679
+ forgottenBytes: zod.z.number().int().nonnegative(),
4680
+ /** Files under a claimed hour that no durable row names. Never deleted. */
4681
+ orphanFiles: zod.z.number().int().nonnegative(),
4682
+ orphanSample: zod.z.array(zod.z.string()).readonly(),
4683
+ hoursSkipped: zod.z.number().int().nonnegative(),
4684
+ skippedByReason: LedgerWalkSkipCountsSchema,
4685
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
4686
+ bounded: zod.z.boolean(),
4687
+ byDevice: zod.z.array(LedgerWalkDeviceReportSchema).readonly()
4688
+ });
4590
4689
  /** How many rows a media pass would still act on against a given target — the
4591
4690
  * media lane's denominator AND its residue, from ONE derivation so the two can
4592
4691
  * never disagree. `null` = the count could not be taken. */
@@ -17777,13 +17876,15 @@ var ListGroupsPageSchema = zod.z.object({
17777
17876
  groups: zod.z.array(AnalyticsGroupRecordSchema).readonly(),
17778
17877
  nextCursor: zod.z.string().nullable()
17779
17878
  });
17879
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
17880
+ var KEY_EVENTS_MAX_LIMIT = 200;
17780
17881
  var KeyEventQueryInput = zod.z.object({
17781
17882
  deviceId: zod.z.number(),
17782
17883
  /** Window lower bound (track firstSeen ≥ since). */
17783
17884
  since: zod.z.number(),
17784
17885
  /** Window upper bound (track firstSeen ≤ until). */
17785
17886
  until: zod.z.number(),
17786
- limit: zod.z.number().int().min(1).max(200).default(50),
17887
+ limit: zod.z.number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
17787
17888
  /** Drop tracks scoring below this importance. */
17788
17889
  minImportance: zod.z.number().min(0).max(1).optional(),
17789
17890
  /** Restrict to a single class (e.g. 'person'). */
@@ -17805,6 +17906,32 @@ var KeyEventSchema = zod.z.object({
17805
17906
  ...TrackFlagFields,
17806
17907
  ...TrackRetrainFields
17807
17908
  });
17909
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
17910
+ var KeyEventBatchQueryInput = zod.z.object({
17911
+ deviceIds: zod.z.array(zod.z.number()).min(1).max(200),
17912
+ since: zod.z.number(),
17913
+ until: zod.z.number(),
17914
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
17915
+ * across the set, which would let a busy camera starve a quiet one of its
17916
+ * rows and change what the merged feed contains. */
17917
+ limit: zod.z.number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
17918
+ minImportance: zod.z.number().min(0).max(1).optional(),
17919
+ classFilter: zod.z.string().optional()
17920
+ });
17921
+ /**
17922
+ * One camera's key events in a batch answer.
17923
+ *
17924
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
17925
+ * error rather than throwing, so a camera whose store read failed and one with
17926
+ * no events in the window were ALREADY indistinguishable per camera — the
17927
+ * batch does not make that worse, and the row keeps the deviceId the single
17928
+ * method's output never carried (the caller used to stamp it from the fan-out
17929
+ * key, which only worked because there was one query per camera).
17930
+ */
17931
+ var KeyEventsForDeviceSchema = zod.z.object({
17932
+ deviceId: zod.z.number(),
17933
+ events: zod.z.array(KeyEventSchema).readonly()
17934
+ });
17808
17935
  zod.z.object({
17809
17936
  trackId: zod.z.string(),
17810
17937
  className: zod.z.string(),
@@ -17876,6 +18003,50 @@ var EventStoreFootprintSchema = zod.z.object({
17876
18003
  totalBytes: zod.z.number().int(),
17877
18004
  devices: zod.z.array(EventStoreDeviceFootprintSchema).readonly()
17878
18005
  });
18006
+ /** Event-media footprint for one {@link MediaFileKind}. */
18007
+ var EventMediaKindFootprintSchema = zod.z.object({
18008
+ kind: MediaFileKindEnum,
18009
+ /** Media rows of this kind. */
18010
+ rows: zod.z.number().int(),
18011
+ /** Bytes on disk held by those rows. */
18012
+ bytes: zod.z.number().int()
18013
+ });
18014
+ /**
18015
+ * The media footprint broken down by KIND — the axis a deletion decision
18016
+ * actually turns on.
18017
+ *
18018
+ * A byte total says how much there is; it cannot say what is safe to remove.
18019
+ * The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
18020
+ * motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
18021
+ * `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
18022
+ * buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
18023
+ * nothing else, so sizing a deletion means summing per kind.
18024
+ *
18025
+ * ## Why `unaccounted*` exists
18026
+ *
18027
+ * `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
18028
+ * writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
18029
+ * from a SEPARATE unfiltered aggregate over the same rows, never from adding
18030
+ * `kinds` up. A row whose stored `kind` is not in the enum (written by a
18031
+ * retired code path, or by a version that knew a kind this one does not) would
18032
+ * otherwise vanish from the total silently, and an operator would delete
18033
+ * against a denominator smaller than the disk.
18034
+ *
18035
+ * `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
18036
+ * zero; a non-zero value is a real finding and must be shown, not rounded away.
18037
+ */
18038
+ var EventMediaKindBreakdownSchema = zod.z.object({
18039
+ /** Every media row in scope, from one unfiltered aggregate. */
18040
+ totalRows: zod.z.number().int(),
18041
+ /** Every media byte in scope, from that same aggregate. */
18042
+ totalBytes: zod.z.number().int(),
18043
+ /** Per-kind footprint, ordered by bytes descending. */
18044
+ kinds: zod.z.array(EventMediaKindFootprintSchema).readonly(),
18045
+ /** `totalRows` minus the summed `kinds` rows — see the schema note. */
18046
+ unaccountedRows: zod.z.number().int(),
18047
+ /** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
18048
+ unaccountedBytes: zod.z.number().int()
18049
+ });
17879
18050
  /** Per-kind counts returned by the event-prune / device-delete mutations. */
17880
18051
  var EventPruneCountsSchema = zod.z.object({
17881
18052
  motion: zod.z.number().int(),
@@ -18110,6 +18281,21 @@ var pipelineAnalyticsCapability = {
18110
18281
  * scored on-read (no write). Degrades to `[]` on error.
18111
18282
  */
18112
18283
  getKeyEvents: method(KeyEventQueryInput, zod.z.array(KeyEventSchema).readonly()),
18284
+ /**
18285
+ * The same ranking, for a SET of cameras, in one round trip.
18286
+ *
18287
+ * The Detection Intelligence events feed asks this of every selected
18288
+ * camera and re-asks on a 30s timer. Fanned out client-side that is N
18289
+ * round trips — browser → hub → post-analysis — for N independent,
18290
+ * already-indexed store queries. Batched, the queries are unchanged and
18291
+ * run concurrently INSIDE the owner; only the transport collapses.
18292
+ *
18293
+ * Deliberately per-device rather than pre-merged: `limit` stays per
18294
+ * camera (a total would let a busy camera starve a quiet one), and a
18295
+ * caller that renders one camera's lane needs to know which camera a row
18296
+ * came from. `getKeyEvents` stays for single-device callers.
18297
+ */
18298
+ getKeyEventsBatch: method(KeyEventBatchQueryInput, zod.z.array(KeyEventsForDeviceSchema).readonly()),
18113
18299
  /** Server-side bucketed event counts for the 24-hour timeline.
18114
18300
  * Returns one entry per non-empty bucket; empty buckets are omitted. */
18115
18301
  getEventDensity: method(zod.z.object({
@@ -18258,6 +18444,22 @@ var pipelineAnalyticsCapability = {
18258
18444
  auth: "admin"
18259
18445
  }),
18260
18446
  /**
18447
+ * The same media footprint, broken down by {@link MediaFileKind} instead of
18448
+ * by camera — fleet-wide, or for one camera with `deviceId`.
18449
+ *
18450
+ * Separate from {@link getEventStoreFootprint} rather than a field on it:
18451
+ * the two answer different questions on different axes, the per-camera one
18452
+ * is what the management table renders on every open, and nothing should
18453
+ * pay for a per-kind pass to draw it. See
18454
+ * {@link EventMediaKindBreakdownSchema} for why `unaccounted*` exists —
18455
+ * `kinds` is enumerated, `totalBytes` is not, and the gap must be visible
18456
+ * to anyone sizing a deletion against it.
18457
+ */
18458
+ getEventMediaFootprintByKind: method(zod.z.object({ deviceId: zod.z.number().int().optional() }), EventMediaKindBreakdownSchema, {
18459
+ kind: "query",
18460
+ auth: "admin"
18461
+ }),
18462
+ /**
18261
18463
  * Cluster-wide prune of events older than `olderThanMs` (exclusive) across
18262
18464
  * every camera, deleting each event's media in lockstep. Logged to the
18263
18465
  * events ops-log with `reason` (default `'retention'`). Returns the summed
@@ -21255,6 +21457,20 @@ var storageCapability = {
21255
21457
  listProviders: method(zod.z.void(), zod.z.array(ProviderListEntrySchema).readonly()),
21256
21458
  testConfig: method(zod.z.object({
21257
21459
  providerId: zod.z.string(),
21460
+ /**
21461
+ * The location this config is an UNSAVED edit of, when there is one.
21462
+ *
21463
+ * `listLocations` replaces every declared secret with the redaction
21464
+ * sentinel, so the edit modal's form state holds the sentinel for any
21465
+ * credential the operator did not retype — and posting that here
21466
+ * without a way to resolve it makes the provider try to authenticate
21467
+ * as `__camstack_redacted__` and report the operator's own working
21468
+ * password as wrong. Given this id, the orchestrator restores each
21469
+ * sentinel from the stored config (same rule as `upsertLocation`)
21470
+ * before dispatching. Omitted by the "Add location" wizard, where
21471
+ * every value was typed just now and nothing is stored yet.
21472
+ */
21473
+ locationId: zod.z.string().optional(),
21258
21474
  config: zod.z.record(zod.z.string(), zod.z.unknown())
21259
21475
  }), zod.z.object({
21260
21476
  ok: zod.z.boolean(),
@@ -29949,6 +30165,23 @@ var recordingCapability = {
29949
30165
  kind: "query",
29950
30166
  auth: "admin"
29951
30167
  }),
30168
+ /**
30169
+ * Does this location's LEDGER tell the truth about its disk? (D319)
30170
+ *
30171
+ * One `readdir` per claimed hour, diffed both ways: durable rows whose file
30172
+ * is not there, and files no durable row names. `apply` defaults to FALSE —
30173
+ * the report is the product, and the dry run is how an operator
30174
+ * sanity-checks the destructive run before authorising it.
30175
+ *
30176
+ * REFUSES a source that is still a write target: a listing of a live
30177
+ * location is a lower bound, which is not the strong evidence that lets
30178
+ * this pass forget without a budget. A live location is reconciled by the
30179
+ * mover, under D318's bound.
30180
+ */
30181
+ reconcileLedgerAgainstDisk: method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
30182
+ kind: "mutation",
30183
+ auth: "admin"
30184
+ }),
29952
30185
  /** Cancel a running or queued relocate job. A queued job never runs. */
29953
30186
  cancelRelocateJob: method(zod.z.object({ jobId: zod.z.string() }), zod.z.object({ cancelled: zod.z.boolean() }), {
29954
30187
  kind: "mutation",
@@ -30383,6 +30616,25 @@ var SceneMonitorStatusSchema = zod.z.object({
30383
30616
  monitors: zod.z.array(SceneMonitorSchema),
30384
30617
  lastFetchedAt: zod.z.number()
30385
30618
  });
30619
+ /**
30620
+ * One camera's row in a `listScenesBatch` answer.
30621
+ *
30622
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
30623
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
30624
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
30625
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
30626
+ * configured. Fanned out per camera the difference was visible — one query
30627
+ * errored while the others resolved — and a batch that returned only the rows
30628
+ * it managed would have destroyed it, silently, by making an unreachable camera
30629
+ * indistinguishable from one that answered `monitors: []`.
30630
+ *
30631
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
30632
+ * could not be read"; `status.monitors: []` means "read, and it has none".
30633
+ */
30634
+ var SceneMonitorStatusForDeviceSchema = zod.z.object({
30635
+ deviceId: zod.z.number(),
30636
+ status: SceneMonitorStatusSchema.nullable()
30637
+ });
30386
30638
  var sceneMonitorCapability = {
30387
30639
  name: "scene-monitor",
30388
30640
  scope: "device",
@@ -30392,6 +30644,22 @@ var sceneMonitorCapability = {
30392
30644
  deviceTypes: [DeviceType.Camera],
30393
30645
  methods: {
30394
30646
  listScenes: method(zod.z.object({ deviceId: zod.z.number() }), SceneMonitorStatusSchema),
30647
+ /**
30648
+ * The same answer, for a SET of cameras, in one round trip.
30649
+ *
30650
+ * `/scenes` renders every camera and re-reads them on a 30s safety-net
30651
+ * poll behind the push slice. Fanned out client-side that was one query
30652
+ * per camera — 29 round trips through the browser, the hub and the
30653
+ * post-analysis runner every 30 seconds to read an in-memory map the
30654
+ * owner had already merged. The work is unchanged (`statusFor` per
30655
+ * device, all in-process at the owner); what collapses is the transport.
30656
+ *
30657
+ * A camera that cannot answer still gets a row, with `status: null` —
30658
+ * see {@link SceneMonitorStatusForDeviceSchema}. Never fewer rows than
30659
+ * ids: a caller that asked for twenty-nine and got twenty-seven cannot
30660
+ * tell which two are missing, or that any are.
30661
+ */
30662
+ listScenesBatch: method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).min(1).max(200) }), zod.z.array(SceneMonitorStatusForDeviceSchema).readonly()),
30395
30663
  createScene: method(zod.z.object({
30396
30664
  deviceId: zod.z.number(),
30397
30665
  label: zod.z.string(),
@@ -32662,6 +32930,27 @@ var CameraOccupancySnapshotSchema = zod.z.object({
32662
32930
  stationaryObjects: zod.z.array(StationaryObjectSchema).readonly().optional()
32663
32931
  });
32664
32932
  /**
32933
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
32934
+ *
32935
+ * THREE outcomes, and the single-camera method could only express two of them
32936
+ * because `snapshot: null` was already spoken for:
32937
+ *
32938
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
32939
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
32940
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
32941
+ * - `read: 'unreadable'` — the owner could not answer for this
32942
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
32943
+ *
32944
+ * Collapsing the last two is the failure this field exists to prevent: a
32945
+ * hydration that threw would otherwise render as an empty Stationary section,
32946
+ * which is a definite claim about a camera nobody could read.
32947
+ */
32948
+ var CameraOccupancySnapshotForDeviceSchema = zod.z.object({
32949
+ deviceId: zod.z.number(),
32950
+ read: zod.z.enum(["read", "unreadable"]),
32951
+ snapshot: CameraOccupancySnapshotSchema.nullable()
32952
+ });
32953
+ /**
32665
32954
  * Time-series resolution. The history methods return one bucket per
32666
32955
  * step over the requested range. Smaller resolutions cost more
32667
32956
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -32718,6 +33007,20 @@ var zoneAnalyticsCapability = {
32718
33007
  * (no inference result emitted since boot or since binding was
32719
33008
  * activated). */
32720
33009
  getCurrentSnapshot: method(zod.z.object({ deviceId: zod.z.number() }), CameraOccupancySnapshotSchema.nullable()),
33010
+ /**
33011
+ * The same snapshot, for a SET of cameras, in one round trip.
33012
+ *
33013
+ * The Events page's Stationary section polls this every 15s for every
33014
+ * selected camera. Fanned out client-side that is one query per camera to
33015
+ * read a `Map.get` at the owner — the answer costs nothing, the round trip
33016
+ * costs everything. Batched, N transports become one and the per-device
33017
+ * work is unchanged.
33018
+ *
33019
+ * Every requested deviceId gets a row, tagged `read` — see
33020
+ * {@link CameraOccupancySnapshotForDeviceSchema}. A camera the owner could
33021
+ * not answer for is `'unreadable'`, never an empty reading.
33022
+ */
33023
+ getCurrentSnapshotBatch: method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).min(1).max(200) }), zod.z.array(CameraOccupancySnapshotForDeviceSchema).readonly()),
32721
33024
  /** Time-series object count inside one zone. `className` optional —
32722
33025
  * omit to count every class in the zone. */
32723
33026
  getZoneHistory: method(zod.z.object({
@@ -36827,6 +37130,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
36827
37130
  addonId: null,
36828
37131
  access: "view"
36829
37132
  },
37133
+ "pipelineAnalytics.getEventMediaFootprintByKind": {
37134
+ capName: "pipeline-analytics",
37135
+ capScope: "device",
37136
+ addonId: null,
37137
+ access: "view"
37138
+ },
36830
37139
  "pipelineAnalytics.getEventStoreFootprint": {
36831
37140
  capName: "pipeline-analytics",
36832
37141
  capScope: "device",
@@ -36845,6 +37154,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
36845
37154
  addonId: null,
36846
37155
  access: "view"
36847
37156
  },
37157
+ "pipelineAnalytics.getKeyEventsBatch": {
37158
+ capName: "pipeline-analytics",
37159
+ capScope: "device",
37160
+ addonId: null,
37161
+ access: "view"
37162
+ },
36848
37163
  "pipelineAnalytics.getMotionEvents": {
36849
37164
  capName: "pipeline-analytics",
36850
37165
  capScope: "device",
@@ -38021,6 +38336,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
38021
38336
  addonId: null,
38022
38337
  access: "view"
38023
38338
  },
38339
+ "recording.reconcileLedgerAgainstDisk": {
38340
+ capName: "recording",
38341
+ capScope: "system",
38342
+ addonId: null,
38343
+ access: "create"
38344
+ },
38024
38345
  "recording.refreshStorageLocationsForMigration": {
38025
38346
  capName: "recording",
38026
38347
  capScope: "system",
@@ -38147,6 +38468,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
38147
38468
  addonId: null,
38148
38469
  access: "view"
38149
38470
  },
38471
+ "sceneMonitor.listScenesBatch": {
38472
+ capName: "scene-monitor",
38473
+ capScope: "device",
38474
+ addonId: null,
38475
+ access: "view"
38476
+ },
38150
38477
  "sceneMonitor.recheckNow": {
38151
38478
  capName: "scene-monitor",
38152
38479
  capScope: "device",
@@ -39503,6 +39830,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
39503
39830
  addonId: null,
39504
39831
  access: "view"
39505
39832
  },
39833
+ "zoneAnalytics.getCurrentSnapshotBatch": {
39834
+ capName: "zone-analytics",
39835
+ capScope: "device",
39836
+ addonId: null,
39837
+ access: "view"
39838
+ },
39506
39839
  "zoneAnalytics.getUnzonedHistory": {
39507
39840
  capName: "zone-analytics",
39508
39841
  capScope: "device",
@@ -40492,6 +40825,11 @@ Object.freeze({
40492
40825
  form: "single",
40493
40826
  optional: false
40494
40827
  }],
40828
+ "pipelineAnalytics.getEventMediaFootprintByKind": [{
40829
+ name: "deviceId",
40830
+ form: "single",
40831
+ optional: true
40832
+ }],
40495
40833
  "pipelineAnalytics.getGroup": [{
40496
40834
  name: "deviceId",
40497
40835
  form: "single",
@@ -40502,6 +40840,11 @@ Object.freeze({
40502
40840
  form: "single",
40503
40841
  optional: false
40504
40842
  }],
40843
+ "pipelineAnalytics.getKeyEventsBatch": [{
40844
+ name: "deviceIds",
40845
+ form: "array",
40846
+ optional: false
40847
+ }],
40505
40848
  "pipelineAnalytics.getMotionEvents": [{
40506
40849
  name: "deviceId",
40507
40850
  form: "single",
@@ -40942,6 +41285,11 @@ Object.freeze({
40942
41285
  form: "single",
40943
41286
  optional: false
40944
41287
  }],
41288
+ "recording.reconcileLedgerAgainstDisk": [{
41289
+ name: "deviceId",
41290
+ form: "single",
41291
+ optional: true
41292
+ }],
40945
41293
  "recording.relocateFootage": [{
40946
41294
  name: "deviceId",
40947
41295
  form: "single",
@@ -41007,6 +41355,11 @@ Object.freeze({
41007
41355
  form: "single",
41008
41356
  optional: false
41009
41357
  }],
41358
+ "sceneMonitor.listScenesBatch": [{
41359
+ name: "deviceIds",
41360
+ form: "array",
41361
+ optional: false
41362
+ }],
41010
41363
  "sceneMonitor.recheckNow": [{
41011
41364
  name: "deviceId",
41012
41365
  form: "single",
@@ -41268,6 +41621,11 @@ Object.freeze({
41268
41621
  form: "single",
41269
41622
  optional: false
41270
41623
  }],
41624
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
41625
+ name: "deviceIds",
41626
+ form: "array",
41627
+ optional: false
41628
+ }],
41271
41629
  "zoneAnalytics.getUnzonedHistory": [{
41272
41630
  name: "deviceId",
41273
41631
  form: "single",