@camstack/addon-remote-storage 1.2.46 → 1.2.48

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.
package/dist/s3.addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-DAxSJcnX.js");
5
+ const require_shared = require("./shared-Bwt_Ye8T.js");
6
6
  let node_stream = require("node:stream");
7
7
  let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
8
8
  let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
package/dist/s3.addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-hUiD7Ohg.mjs";
1
+ import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-CQEWmUcA.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { DeleteObjectCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
4
  import { Upload } from "@aws-sdk/lib-storage";
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-DAxSJcnX.js");
5
+ const require_shared = require("./shared-Bwt_Ye8T.js");
6
6
  let ssh2 = require("ssh2");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_shared.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-hUiD7Ohg.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-CQEWmUcA.mjs";
2
2
  import { Client } from "ssh2";
3
3
  import * as path from "node:path";
4
4
  //#region src/providers/sftp/sftp-config-schema.ts
@@ -8026,6 +8026,20 @@ var RelocateJobSchema = object({
8026
8026
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8027
8027
  */
8028
8028
  rowsReconciled: number().int().nonnegative().optional(),
8029
+ /**
8030
+ * Rows this run FORGOT because the file they name is not on disk.
8031
+ *
8032
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8033
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8034
+ * and the durable row is dropped through the same channel eviction uses. It
8035
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8036
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8037
+ * the same failure as one that quietly skips them (D295).
8038
+ *
8039
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8040
+ * ledger claimed 5.65 GB of footage that no longer existed.
8041
+ */
8042
+ rowsForgotten: number().int().nonnegative().optional(),
8029
8043
  startedAt: number(),
8030
8044
  finishedAt: number().nullable(),
8031
8045
  error: string().nullable()
@@ -8389,6 +8403,91 @@ var RelocateResidueSchema = object({
8389
8403
  segments: number().int().nonnegative(),
8390
8404
  bytes: number().int().nonnegative()
8391
8405
  }).nullable();
8406
+ /**
8407
+ * Ask one location whether its durable hour rows describe the disk — the walk
8408
+ * (D319).
8409
+ *
8410
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8411
+ * missing tool is the question, and the dry run is how they sanity-check the
8412
+ * destructive run before authorising it.
8413
+ */
8414
+ var LedgerWalkInputSchema = object({
8415
+ locationId: string().min(1),
8416
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8417
+ apply: boolean().optional(),
8418
+ /** Narrow to one camera. */
8419
+ deviceId: number().int().positive().optional(),
8420
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8421
+ profiles: array(string().min(1)).optional()
8422
+ });
8423
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8424
+ var LedgerWalkRefusalSchema = _enum([
8425
+ "location-unknown",
8426
+ "source-writable",
8427
+ "no-ledger",
8428
+ "archive-unreadable",
8429
+ "anchor-absent",
8430
+ "anchor-unreadable",
8431
+ "anchor-moved"
8432
+ ]);
8433
+ _enum([
8434
+ "live-tail",
8435
+ "listing-error",
8436
+ "path-mismatch",
8437
+ "durable-refused"
8438
+ ]);
8439
+ /** Every skip reason, always present, always a number — so a reason that never
8440
+ * fired reports as zero rather than absent and the report shape is constant
8441
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8442
+ var LedgerWalkSkipCountsSchema = object({
8443
+ "live-tail": number().int().nonnegative(),
8444
+ "listing-error": number().int().nonnegative(),
8445
+ "path-mismatch": number().int().nonnegative(),
8446
+ "durable-refused": number().int().nonnegative()
8447
+ });
8448
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8449
+ var LedgerWalkDeviceReportSchema = object({
8450
+ deviceId: number().int(),
8451
+ hoursWalked: number().int().nonnegative(),
8452
+ hoursMissing: number().int().nonnegative(),
8453
+ ghostSegments: number().int().nonnegative(),
8454
+ ghostBytes: number().int().nonnegative(),
8455
+ forgottenSegments: number().int().nonnegative(),
8456
+ orphanFiles: number().int().nonnegative()
8457
+ });
8458
+ /**
8459
+ * What one walk claimed, listed, found and (only when armed) forgot.
8460
+ *
8461
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8462
+ * walk that saw a fraction of the location is visible in its own report rather
8463
+ * than in the absence of one.
8464
+ */
8465
+ var LedgerWalkReportSchema = object({
8466
+ locationId: string(),
8467
+ applied: boolean(),
8468
+ refused: LedgerWalkRefusalSchema.nullable(),
8469
+ archiveSegments: number().int().nonnegative().nullable(),
8470
+ archiveBytes: number().int().nonnegative().nullable(),
8471
+ hoursClaimed: number().int().nonnegative(),
8472
+ hoursWalked: number().int().nonnegative(),
8473
+ hoursMissing: number().int().nonnegative(),
8474
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8475
+ listings: number().int().nonnegative(),
8476
+ segmentsClaimed: number().int().nonnegative(),
8477
+ ghostSegments: number().int().nonnegative(),
8478
+ ghostBytes: number().int().nonnegative(),
8479
+ ghostHoursWhole: number().int().nonnegative(),
8480
+ forgottenSegments: number().int().nonnegative(),
8481
+ forgottenBytes: number().int().nonnegative(),
8482
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8483
+ orphanFiles: number().int().nonnegative(),
8484
+ orphanSample: array(string()).readonly(),
8485
+ hoursSkipped: number().int().nonnegative(),
8486
+ skippedByReason: LedgerWalkSkipCountsSchema,
8487
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8488
+ bounded: boolean(),
8489
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8490
+ });
8392
8491
  /** How many rows a media pass would still act on against a given target — the
8393
8492
  * media lane's denominator AND its residue, from ONE derivation so the two can
8394
8493
  * never disagree. `null` = the count could not be taken. */
@@ -18662,13 +18761,15 @@ var ListGroupsPageSchema = object({
18662
18761
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18663
18762
  nextCursor: string().nullable()
18664
18763
  });
18764
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
18765
+ var KEY_EVENTS_MAX_LIMIT = 200;
18665
18766
  var KeyEventQueryInput = object({
18666
18767
  deviceId: number(),
18667
18768
  /** Window lower bound (track firstSeen ≥ since). */
18668
18769
  since: number(),
18669
18770
  /** Window upper bound (track firstSeen ≤ until). */
18670
18771
  until: number(),
18671
- limit: number().int().min(1).max(200).default(50),
18772
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18672
18773
  /** Drop tracks scoring below this importance. */
18673
18774
  minImportance: number().min(0).max(1).optional(),
18674
18775
  /** Restrict to a single class (e.g. 'person'). */
@@ -18690,6 +18791,32 @@ var KeyEventSchema = object({
18690
18791
  ...TrackFlagFields,
18691
18792
  ...TrackRetrainFields
18692
18793
  });
18794
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
18795
+ var KeyEventBatchQueryInput = object({
18796
+ deviceIds: array(number()).min(1).max(200),
18797
+ since: number(),
18798
+ until: number(),
18799
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
18800
+ * across the set, which would let a busy camera starve a quiet one of its
18801
+ * rows and change what the merged feed contains. */
18802
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18803
+ minImportance: number().min(0).max(1).optional(),
18804
+ classFilter: string().optional()
18805
+ });
18806
+ /**
18807
+ * One camera's key events in a batch answer.
18808
+ *
18809
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
18810
+ * error rather than throwing, so a camera whose store read failed and one with
18811
+ * no events in the window were ALREADY indistinguishable per camera — the
18812
+ * batch does not make that worse, and the row keeps the deviceId the single
18813
+ * method's output never carried (the caller used to stamp it from the fan-out
18814
+ * key, which only worked because there was one query per camera).
18815
+ */
18816
+ var KeyEventsForDeviceSchema = object({
18817
+ deviceId: number(),
18818
+ events: array(KeyEventSchema).readonly()
18819
+ });
18693
18820
  object({
18694
18821
  trackId: string(),
18695
18822
  className: string(),
@@ -18761,6 +18888,50 @@ var EventStoreFootprintSchema = object({
18761
18888
  totalBytes: number().int(),
18762
18889
  devices: array(EventStoreDeviceFootprintSchema).readonly()
18763
18890
  });
18891
+ /** Event-media footprint for one {@link MediaFileKind}. */
18892
+ var EventMediaKindFootprintSchema = object({
18893
+ kind: MediaFileKindEnum,
18894
+ /** Media rows of this kind. */
18895
+ rows: number().int(),
18896
+ /** Bytes on disk held by those rows. */
18897
+ bytes: number().int()
18898
+ });
18899
+ /**
18900
+ * The media footprint broken down by KIND — the axis a deletion decision
18901
+ * actually turns on.
18902
+ *
18903
+ * A byte total says how much there is; it cannot say what is safe to remove.
18904
+ * The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
18905
+ * motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
18906
+ * `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
18907
+ * buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
18908
+ * nothing else, so sizing a deletion means summing per kind.
18909
+ *
18910
+ * ## Why `unaccounted*` exists
18911
+ *
18912
+ * `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
18913
+ * writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
18914
+ * from a SEPARATE unfiltered aggregate over the same rows, never from adding
18915
+ * `kinds` up. A row whose stored `kind` is not in the enum (written by a
18916
+ * retired code path, or by a version that knew a kind this one does not) would
18917
+ * otherwise vanish from the total silently, and an operator would delete
18918
+ * against a denominator smaller than the disk.
18919
+ *
18920
+ * `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
18921
+ * zero; a non-zero value is a real finding and must be shown, not rounded away.
18922
+ */
18923
+ var EventMediaKindBreakdownSchema = object({
18924
+ /** Every media row in scope, from one unfiltered aggregate. */
18925
+ totalRows: number().int(),
18926
+ /** Every media byte in scope, from that same aggregate. */
18927
+ totalBytes: number().int(),
18928
+ /** Per-kind footprint, ordered by bytes descending. */
18929
+ kinds: array(EventMediaKindFootprintSchema).readonly(),
18930
+ /** `totalRows` minus the summed `kinds` rows — see the schema note. */
18931
+ unaccountedRows: number().int(),
18932
+ /** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
18933
+ unaccountedBytes: number().int()
18934
+ });
18764
18935
  /** Per-kind counts returned by the event-prune / device-delete mutations. */
18765
18936
  var EventPruneCountsSchema = object({
18766
18937
  motion: number().int(),
@@ -18915,7 +19086,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18915
19086
  until: number().optional(),
18916
19087
  kinds: array(string()).optional(),
18917
19088
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18918
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19089
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
18919
19090
  deviceId: number(),
18920
19091
  since: number(),
18921
19092
  until: number(),
@@ -18964,6 +19135,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18964
19135
  }), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
18965
19136
  kind: "query",
18966
19137
  auth: "admin"
19138
+ }), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
19139
+ kind: "query",
19140
+ auth: "admin"
18967
19141
  }), method(object({
18968
19142
  olderThanMs: number(),
18969
19143
  reason: OpsLogReasonSchema.optional()
@@ -20915,6 +21089,20 @@ method(object({
20915
21089
  error: string().optional()
20916
21090
  }), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
20917
21091
  providerId: string(),
21092
+ /**
21093
+ * The location this config is an UNSAVED edit of, when there is one.
21094
+ *
21095
+ * `listLocations` replaces every declared secret with the redaction
21096
+ * sentinel, so the edit modal's form state holds the sentinel for any
21097
+ * credential the operator did not retype — and posting that here
21098
+ * without a way to resolve it makes the provider try to authenticate
21099
+ * as `__camstack_redacted__` and report the operator's own working
21100
+ * password as wrong. Given this id, the orchestrator restores each
21101
+ * sentinel from the stored config (same rule as `upsertLocation`)
21102
+ * before dispatching. Omitted by the "Add location" wizard, where
21103
+ * every value was typed just now and nothing is stored yet.
21104
+ */
21105
+ locationId: string().optional(),
20918
21106
  config: record(string(), unknown())
20919
21107
  }), object({
20920
21108
  ok: boolean(),
@@ -26610,6 +26798,9 @@ method(object({
26610
26798
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26611
26799
  kind: "query",
26612
26800
  auth: "admin"
26801
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26802
+ kind: "mutation",
26803
+ auth: "admin"
26613
26804
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26614
26805
  kind: "mutation",
26615
26806
  auth: "admin"
@@ -27001,7 +27192,26 @@ var SceneMonitorStatusSchema = object({
27001
27192
  monitors: array(SceneMonitorSchema),
27002
27193
  lastFetchedAt: number()
27003
27194
  });
27004
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27195
+ /**
27196
+ * One camera's row in a `listScenesBatch` answer.
27197
+ *
27198
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27199
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27200
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27201
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27202
+ * configured. Fanned out per camera the difference was visible — one query
27203
+ * errored while the others resolved — and a batch that returned only the rows
27204
+ * it managed would have destroyed it, silently, by making an unreachable camera
27205
+ * indistinguishable from one that answered `monitors: []`.
27206
+ *
27207
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27208
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27209
+ */
27210
+ var SceneMonitorStatusForDeviceSchema = object({
27211
+ deviceId: number(),
27212
+ status: SceneMonitorStatusSchema.nullable()
27213
+ });
27214
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27005
27215
  deviceId: number(),
27006
27216
  label: string(),
27007
27217
  roi: MaskRectShapeSchema,
@@ -28325,6 +28535,27 @@ var CameraOccupancySnapshotSchema = object({
28325
28535
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28326
28536
  });
28327
28537
  /**
28538
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28539
+ *
28540
+ * THREE outcomes, and the single-camera method could only express two of them
28541
+ * because `snapshot: null` was already spoken for:
28542
+ *
28543
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28544
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28545
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28546
+ * - `read: 'unreadable'` — the owner could not answer for this
28547
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28548
+ *
28549
+ * Collapsing the last two is the failure this field exists to prevent: a
28550
+ * hydration that threw would otherwise render as an empty Stationary section,
28551
+ * which is a definite claim about a camera nobody could read.
28552
+ */
28553
+ var CameraOccupancySnapshotForDeviceSchema = object({
28554
+ deviceId: number(),
28555
+ read: _enum(["read", "unreadable"]),
28556
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28557
+ });
28558
+ /**
28328
28559
  * Time-series resolution. The history methods return one bucket per
28329
28560
  * step over the requested range. Smaller resolutions cost more
28330
28561
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28348,7 +28579,7 @@ var HistoryPointSchema = object({
28348
28579
  /** Object count averaged over the bucket (rounded to nearest integer). */
28349
28580
  count: number().int().nonnegative()
28350
28581
  });
28351
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28582
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28352
28583
  deviceId: number(),
28353
28584
  zoneId: string(),
28354
28585
  className: string().optional()
@@ -31629,6 +31860,12 @@ Object.freeze({
31629
31860
  addonId: null,
31630
31861
  access: "view"
31631
31862
  },
31863
+ "pipelineAnalytics.getEventMediaFootprintByKind": {
31864
+ capName: "pipeline-analytics",
31865
+ capScope: "device",
31866
+ addonId: null,
31867
+ access: "view"
31868
+ },
31632
31869
  "pipelineAnalytics.getEventStoreFootprint": {
31633
31870
  capName: "pipeline-analytics",
31634
31871
  capScope: "device",
@@ -31647,6 +31884,12 @@ Object.freeze({
31647
31884
  addonId: null,
31648
31885
  access: "view"
31649
31886
  },
31887
+ "pipelineAnalytics.getKeyEventsBatch": {
31888
+ capName: "pipeline-analytics",
31889
+ capScope: "device",
31890
+ addonId: null,
31891
+ access: "view"
31892
+ },
31650
31893
  "pipelineAnalytics.getMotionEvents": {
31651
31894
  capName: "pipeline-analytics",
31652
31895
  capScope: "device",
@@ -32823,6 +33066,12 @@ Object.freeze({
32823
33066
  addonId: null,
32824
33067
  access: "view"
32825
33068
  },
33069
+ "recording.reconcileLedgerAgainstDisk": {
33070
+ capName: "recording",
33071
+ capScope: "system",
33072
+ addonId: null,
33073
+ access: "create"
33074
+ },
32826
33075
  "recording.refreshStorageLocationsForMigration": {
32827
33076
  capName: "recording",
32828
33077
  capScope: "system",
@@ -32949,6 +33198,12 @@ Object.freeze({
32949
33198
  addonId: null,
32950
33199
  access: "view"
32951
33200
  },
33201
+ "sceneMonitor.listScenesBatch": {
33202
+ capName: "scene-monitor",
33203
+ capScope: "device",
33204
+ addonId: null,
33205
+ access: "view"
33206
+ },
32952
33207
  "sceneMonitor.recheckNow": {
32953
33208
  capName: "scene-monitor",
32954
33209
  capScope: "device",
@@ -34305,6 +34560,12 @@ Object.freeze({
34305
34560
  addonId: null,
34306
34561
  access: "view"
34307
34562
  },
34563
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34564
+ capName: "zone-analytics",
34565
+ capScope: "device",
34566
+ addonId: null,
34567
+ access: "view"
34568
+ },
34308
34569
  "zoneAnalytics.getUnzonedHistory": {
34309
34570
  capName: "zone-analytics",
34310
34571
  capScope: "device",
@@ -35294,6 +35555,11 @@ Object.freeze({
35294
35555
  form: "single",
35295
35556
  optional: false
35296
35557
  }],
35558
+ "pipelineAnalytics.getEventMediaFootprintByKind": [{
35559
+ name: "deviceId",
35560
+ form: "single",
35561
+ optional: true
35562
+ }],
35297
35563
  "pipelineAnalytics.getGroup": [{
35298
35564
  name: "deviceId",
35299
35565
  form: "single",
@@ -35304,6 +35570,11 @@ Object.freeze({
35304
35570
  form: "single",
35305
35571
  optional: false
35306
35572
  }],
35573
+ "pipelineAnalytics.getKeyEventsBatch": [{
35574
+ name: "deviceIds",
35575
+ form: "array",
35576
+ optional: false
35577
+ }],
35307
35578
  "pipelineAnalytics.getMotionEvents": [{
35308
35579
  name: "deviceId",
35309
35580
  form: "single",
@@ -35744,6 +36015,11 @@ Object.freeze({
35744
36015
  form: "single",
35745
36016
  optional: false
35746
36017
  }],
36018
+ "recording.reconcileLedgerAgainstDisk": [{
36019
+ name: "deviceId",
36020
+ form: "single",
36021
+ optional: true
36022
+ }],
35747
36023
  "recording.relocateFootage": [{
35748
36024
  name: "deviceId",
35749
36025
  form: "single",
@@ -35809,6 +36085,11 @@ Object.freeze({
35809
36085
  form: "single",
35810
36086
  optional: false
35811
36087
  }],
36088
+ "sceneMonitor.listScenesBatch": [{
36089
+ name: "deviceIds",
36090
+ form: "array",
36091
+ optional: false
36092
+ }],
35812
36093
  "sceneMonitor.recheckNow": [{
35813
36094
  name: "deviceId",
35814
36095
  form: "single",
@@ -36070,6 +36351,11 @@ Object.freeze({
36070
36351
  form: "single",
36071
36352
  optional: false
36072
36353
  }],
36354
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36355
+ name: "deviceIds",
36356
+ form: "array",
36357
+ optional: false
36358
+ }],
36073
36359
  "zoneAnalytics.getUnzonedHistory": [{
36074
36360
  name: "deviceId",
36075
36361
  form: "single",
@@ -8003,6 +8003,20 @@ var RelocateJobSchema = object({
8003
8003
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8004
8004
  */
8005
8005
  rowsReconciled: number().int().nonnegative().optional(),
8006
+ /**
8007
+ * Rows this run FORGOT because the file they name is not on disk.
8008
+ *
8009
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8010
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8011
+ * and the durable row is dropped through the same channel eviction uses. It
8012
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8013
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8014
+ * the same failure as one that quietly skips them (D295).
8015
+ *
8016
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8017
+ * ledger claimed 5.65 GB of footage that no longer existed.
8018
+ */
8019
+ rowsForgotten: number().int().nonnegative().optional(),
8006
8020
  startedAt: number(),
8007
8021
  finishedAt: number().nullable(),
8008
8022
  error: string().nullable()
@@ -8366,6 +8380,91 @@ var RelocateResidueSchema = object({
8366
8380
  segments: number().int().nonnegative(),
8367
8381
  bytes: number().int().nonnegative()
8368
8382
  }).nullable();
8383
+ /**
8384
+ * Ask one location whether its durable hour rows describe the disk — the walk
8385
+ * (D319).
8386
+ *
8387
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8388
+ * missing tool is the question, and the dry run is how they sanity-check the
8389
+ * destructive run before authorising it.
8390
+ */
8391
+ var LedgerWalkInputSchema = object({
8392
+ locationId: string().min(1),
8393
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8394
+ apply: boolean().optional(),
8395
+ /** Narrow to one camera. */
8396
+ deviceId: number().int().positive().optional(),
8397
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8398
+ profiles: array(string().min(1)).optional()
8399
+ });
8400
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8401
+ var LedgerWalkRefusalSchema = _enum([
8402
+ "location-unknown",
8403
+ "source-writable",
8404
+ "no-ledger",
8405
+ "archive-unreadable",
8406
+ "anchor-absent",
8407
+ "anchor-unreadable",
8408
+ "anchor-moved"
8409
+ ]);
8410
+ _enum([
8411
+ "live-tail",
8412
+ "listing-error",
8413
+ "path-mismatch",
8414
+ "durable-refused"
8415
+ ]);
8416
+ /** Every skip reason, always present, always a number — so a reason that never
8417
+ * fired reports as zero rather than absent and the report shape is constant
8418
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8419
+ var LedgerWalkSkipCountsSchema = object({
8420
+ "live-tail": number().int().nonnegative(),
8421
+ "listing-error": number().int().nonnegative(),
8422
+ "path-mismatch": number().int().nonnegative(),
8423
+ "durable-refused": number().int().nonnegative()
8424
+ });
8425
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8426
+ var LedgerWalkDeviceReportSchema = object({
8427
+ deviceId: number().int(),
8428
+ hoursWalked: number().int().nonnegative(),
8429
+ hoursMissing: number().int().nonnegative(),
8430
+ ghostSegments: number().int().nonnegative(),
8431
+ ghostBytes: number().int().nonnegative(),
8432
+ forgottenSegments: number().int().nonnegative(),
8433
+ orphanFiles: number().int().nonnegative()
8434
+ });
8435
+ /**
8436
+ * What one walk claimed, listed, found and (only when armed) forgot.
8437
+ *
8438
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8439
+ * walk that saw a fraction of the location is visible in its own report rather
8440
+ * than in the absence of one.
8441
+ */
8442
+ var LedgerWalkReportSchema = object({
8443
+ locationId: string(),
8444
+ applied: boolean(),
8445
+ refused: LedgerWalkRefusalSchema.nullable(),
8446
+ archiveSegments: number().int().nonnegative().nullable(),
8447
+ archiveBytes: number().int().nonnegative().nullable(),
8448
+ hoursClaimed: number().int().nonnegative(),
8449
+ hoursWalked: number().int().nonnegative(),
8450
+ hoursMissing: number().int().nonnegative(),
8451
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8452
+ listings: number().int().nonnegative(),
8453
+ segmentsClaimed: number().int().nonnegative(),
8454
+ ghostSegments: number().int().nonnegative(),
8455
+ ghostBytes: number().int().nonnegative(),
8456
+ ghostHoursWhole: number().int().nonnegative(),
8457
+ forgottenSegments: number().int().nonnegative(),
8458
+ forgottenBytes: number().int().nonnegative(),
8459
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8460
+ orphanFiles: number().int().nonnegative(),
8461
+ orphanSample: array(string()).readonly(),
8462
+ hoursSkipped: number().int().nonnegative(),
8463
+ skippedByReason: LedgerWalkSkipCountsSchema,
8464
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8465
+ bounded: boolean(),
8466
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8467
+ });
8369
8468
  /** How many rows a media pass would still act on against a given target — the
8370
8469
  * media lane's denominator AND its residue, from ONE derivation so the two can
8371
8470
  * never disagree. `null` = the count could not be taken. */
@@ -18639,13 +18738,15 @@ var ListGroupsPageSchema = object({
18639
18738
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18640
18739
  nextCursor: string().nullable()
18641
18740
  });
18741
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
18742
+ var KEY_EVENTS_MAX_LIMIT = 200;
18642
18743
  var KeyEventQueryInput = object({
18643
18744
  deviceId: number(),
18644
18745
  /** Window lower bound (track firstSeen ≥ since). */
18645
18746
  since: number(),
18646
18747
  /** Window upper bound (track firstSeen ≤ until). */
18647
18748
  until: number(),
18648
- limit: number().int().min(1).max(200).default(50),
18749
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18649
18750
  /** Drop tracks scoring below this importance. */
18650
18751
  minImportance: number().min(0).max(1).optional(),
18651
18752
  /** Restrict to a single class (e.g. 'person'). */
@@ -18667,6 +18768,32 @@ var KeyEventSchema = object({
18667
18768
  ...TrackFlagFields,
18668
18769
  ...TrackRetrainFields
18669
18770
  });
18771
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
18772
+ var KeyEventBatchQueryInput = object({
18773
+ deviceIds: array(number()).min(1).max(200),
18774
+ since: number(),
18775
+ until: number(),
18776
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
18777
+ * across the set, which would let a busy camera starve a quiet one of its
18778
+ * rows and change what the merged feed contains. */
18779
+ limit: number().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18780
+ minImportance: number().min(0).max(1).optional(),
18781
+ classFilter: string().optional()
18782
+ });
18783
+ /**
18784
+ * One camera's key events in a batch answer.
18785
+ *
18786
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
18787
+ * error rather than throwing, so a camera whose store read failed and one with
18788
+ * no events in the window were ALREADY indistinguishable per camera — the
18789
+ * batch does not make that worse, and the row keeps the deviceId the single
18790
+ * method's output never carried (the caller used to stamp it from the fan-out
18791
+ * key, which only worked because there was one query per camera).
18792
+ */
18793
+ var KeyEventsForDeviceSchema = object({
18794
+ deviceId: number(),
18795
+ events: array(KeyEventSchema).readonly()
18796
+ });
18670
18797
  object({
18671
18798
  trackId: string(),
18672
18799
  className: string(),
@@ -18738,6 +18865,50 @@ var EventStoreFootprintSchema = object({
18738
18865
  totalBytes: number().int(),
18739
18866
  devices: array(EventStoreDeviceFootprintSchema).readonly()
18740
18867
  });
18868
+ /** Event-media footprint for one {@link MediaFileKind}. */
18869
+ var EventMediaKindFootprintSchema = object({
18870
+ kind: MediaFileKindEnum,
18871
+ /** Media rows of this kind. */
18872
+ rows: number().int(),
18873
+ /** Bytes on disk held by those rows. */
18874
+ bytes: number().int()
18875
+ });
18876
+ /**
18877
+ * The media footprint broken down by KIND — the axis a deletion decision
18878
+ * actually turns on.
18879
+ *
18880
+ * A byte total says how much there is; it cannot say what is safe to remove.
18881
+ * The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
18882
+ * motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
18883
+ * `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
18884
+ * buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
18885
+ * nothing else, so sizing a deletion means summing per kind.
18886
+ *
18887
+ * ## Why `unaccounted*` exists
18888
+ *
18889
+ * `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
18890
+ * writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
18891
+ * from a SEPARATE unfiltered aggregate over the same rows, never from adding
18892
+ * `kinds` up. A row whose stored `kind` is not in the enum (written by a
18893
+ * retired code path, or by a version that knew a kind this one does not) would
18894
+ * otherwise vanish from the total silently, and an operator would delete
18895
+ * against a denominator smaller than the disk.
18896
+ *
18897
+ * `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
18898
+ * zero; a non-zero value is a real finding and must be shown, not rounded away.
18899
+ */
18900
+ var EventMediaKindBreakdownSchema = object({
18901
+ /** Every media row in scope, from one unfiltered aggregate. */
18902
+ totalRows: number().int(),
18903
+ /** Every media byte in scope, from that same aggregate. */
18904
+ totalBytes: number().int(),
18905
+ /** Per-kind footprint, ordered by bytes descending. */
18906
+ kinds: array(EventMediaKindFootprintSchema).readonly(),
18907
+ /** `totalRows` minus the summed `kinds` rows — see the schema note. */
18908
+ unaccountedRows: number().int(),
18909
+ /** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
18910
+ unaccountedBytes: number().int()
18911
+ });
18741
18912
  /** Per-kind counts returned by the event-prune / device-delete mutations. */
18742
18913
  var EventPruneCountsSchema = object({
18743
18914
  motion: number().int(),
@@ -18892,7 +19063,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18892
19063
  until: number().optional(),
18893
19064
  kinds: array(string()).optional(),
18894
19065
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18895
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19066
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
18896
19067
  deviceId: number(),
18897
19068
  since: number(),
18898
19069
  until: number(),
@@ -18941,6 +19112,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18941
19112
  }), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
18942
19113
  kind: "query",
18943
19114
  auth: "admin"
19115
+ }), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
19116
+ kind: "query",
19117
+ auth: "admin"
18944
19118
  }), method(object({
18945
19119
  olderThanMs: number(),
18946
19120
  reason: OpsLogReasonSchema.optional()
@@ -20892,6 +21066,20 @@ method(object({
20892
21066
  error: string().optional()
20893
21067
  }), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
20894
21068
  providerId: string(),
21069
+ /**
21070
+ * The location this config is an UNSAVED edit of, when there is one.
21071
+ *
21072
+ * `listLocations` replaces every declared secret with the redaction
21073
+ * sentinel, so the edit modal's form state holds the sentinel for any
21074
+ * credential the operator did not retype — and posting that here
21075
+ * without a way to resolve it makes the provider try to authenticate
21076
+ * as `__camstack_redacted__` and report the operator's own working
21077
+ * password as wrong. Given this id, the orchestrator restores each
21078
+ * sentinel from the stored config (same rule as `upsertLocation`)
21079
+ * before dispatching. Omitted by the "Add location" wizard, where
21080
+ * every value was typed just now and nothing is stored yet.
21081
+ */
21082
+ locationId: string().optional(),
20895
21083
  config: record(string(), unknown())
20896
21084
  }), object({
20897
21085
  ok: boolean(),
@@ -26587,6 +26775,9 @@ method(object({
26587
26775
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26588
26776
  kind: "query",
26589
26777
  auth: "admin"
26778
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26779
+ kind: "mutation",
26780
+ auth: "admin"
26590
26781
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26591
26782
  kind: "mutation",
26592
26783
  auth: "admin"
@@ -26978,7 +27169,26 @@ var SceneMonitorStatusSchema = object({
26978
27169
  monitors: array(SceneMonitorSchema),
26979
27170
  lastFetchedAt: number()
26980
27171
  });
26981
- DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
27172
+ /**
27173
+ * One camera's row in a `listScenesBatch` answer.
27174
+ *
27175
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27176
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27177
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27178
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27179
+ * configured. Fanned out per camera the difference was visible — one query
27180
+ * errored while the others resolved — and a batch that returned only the rows
27181
+ * it managed would have destroyed it, silently, by making an unreachable camera
27182
+ * indistinguishable from one that answered `monitors: []`.
27183
+ *
27184
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27185
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27186
+ */
27187
+ var SceneMonitorStatusForDeviceSchema = object({
27188
+ deviceId: number(),
27189
+ status: SceneMonitorStatusSchema.nullable()
27190
+ });
27191
+ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
26982
27192
  deviceId: number(),
26983
27193
  label: string(),
26984
27194
  roi: MaskRectShapeSchema,
@@ -28302,6 +28512,27 @@ var CameraOccupancySnapshotSchema = object({
28302
28512
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28303
28513
  });
28304
28514
  /**
28515
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28516
+ *
28517
+ * THREE outcomes, and the single-camera method could only express two of them
28518
+ * because `snapshot: null` was already spoken for:
28519
+ *
28520
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28521
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28522
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28523
+ * - `read: 'unreadable'` — the owner could not answer for this
28524
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28525
+ *
28526
+ * Collapsing the last two is the failure this field exists to prevent: a
28527
+ * hydration that threw would otherwise render as an empty Stationary section,
28528
+ * which is a definite claim about a camera nobody could read.
28529
+ */
28530
+ var CameraOccupancySnapshotForDeviceSchema = object({
28531
+ deviceId: number(),
28532
+ read: _enum(["read", "unreadable"]),
28533
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28534
+ });
28535
+ /**
28305
28536
  * Time-series resolution. The history methods return one bucket per
28306
28537
  * step over the requested range. Smaller resolutions cost more
28307
28538
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28325,7 +28556,7 @@ var HistoryPointSchema = object({
28325
28556
  /** Object count averaged over the bucket (rounded to nearest integer). */
28326
28557
  count: number().int().nonnegative()
28327
28558
  });
28328
- DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28559
+ DeviceType.Camera, method(object({ deviceId: number() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28329
28560
  deviceId: number(),
28330
28561
  zoneId: string(),
28331
28562
  className: string().optional()
@@ -31606,6 +31837,12 @@ Object.freeze({
31606
31837
  addonId: null,
31607
31838
  access: "view"
31608
31839
  },
31840
+ "pipelineAnalytics.getEventMediaFootprintByKind": {
31841
+ capName: "pipeline-analytics",
31842
+ capScope: "device",
31843
+ addonId: null,
31844
+ access: "view"
31845
+ },
31609
31846
  "pipelineAnalytics.getEventStoreFootprint": {
31610
31847
  capName: "pipeline-analytics",
31611
31848
  capScope: "device",
@@ -31624,6 +31861,12 @@ Object.freeze({
31624
31861
  addonId: null,
31625
31862
  access: "view"
31626
31863
  },
31864
+ "pipelineAnalytics.getKeyEventsBatch": {
31865
+ capName: "pipeline-analytics",
31866
+ capScope: "device",
31867
+ addonId: null,
31868
+ access: "view"
31869
+ },
31627
31870
  "pipelineAnalytics.getMotionEvents": {
31628
31871
  capName: "pipeline-analytics",
31629
31872
  capScope: "device",
@@ -32800,6 +33043,12 @@ Object.freeze({
32800
33043
  addonId: null,
32801
33044
  access: "view"
32802
33045
  },
33046
+ "recording.reconcileLedgerAgainstDisk": {
33047
+ capName: "recording",
33048
+ capScope: "system",
33049
+ addonId: null,
33050
+ access: "create"
33051
+ },
32803
33052
  "recording.refreshStorageLocationsForMigration": {
32804
33053
  capName: "recording",
32805
33054
  capScope: "system",
@@ -32926,6 +33175,12 @@ Object.freeze({
32926
33175
  addonId: null,
32927
33176
  access: "view"
32928
33177
  },
33178
+ "sceneMonitor.listScenesBatch": {
33179
+ capName: "scene-monitor",
33180
+ capScope: "device",
33181
+ addonId: null,
33182
+ access: "view"
33183
+ },
32929
33184
  "sceneMonitor.recheckNow": {
32930
33185
  capName: "scene-monitor",
32931
33186
  capScope: "device",
@@ -34282,6 +34537,12 @@ Object.freeze({
34282
34537
  addonId: null,
34283
34538
  access: "view"
34284
34539
  },
34540
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34541
+ capName: "zone-analytics",
34542
+ capScope: "device",
34543
+ addonId: null,
34544
+ access: "view"
34545
+ },
34285
34546
  "zoneAnalytics.getUnzonedHistory": {
34286
34547
  capName: "zone-analytics",
34287
34548
  capScope: "device",
@@ -35271,6 +35532,11 @@ Object.freeze({
35271
35532
  form: "single",
35272
35533
  optional: false
35273
35534
  }],
35535
+ "pipelineAnalytics.getEventMediaFootprintByKind": [{
35536
+ name: "deviceId",
35537
+ form: "single",
35538
+ optional: true
35539
+ }],
35274
35540
  "pipelineAnalytics.getGroup": [{
35275
35541
  name: "deviceId",
35276
35542
  form: "single",
@@ -35281,6 +35547,11 @@ Object.freeze({
35281
35547
  form: "single",
35282
35548
  optional: false
35283
35549
  }],
35550
+ "pipelineAnalytics.getKeyEventsBatch": [{
35551
+ name: "deviceIds",
35552
+ form: "array",
35553
+ optional: false
35554
+ }],
35284
35555
  "pipelineAnalytics.getMotionEvents": [{
35285
35556
  name: "deviceId",
35286
35557
  form: "single",
@@ -35721,6 +35992,11 @@ Object.freeze({
35721
35992
  form: "single",
35722
35993
  optional: false
35723
35994
  }],
35995
+ "recording.reconcileLedgerAgainstDisk": [{
35996
+ name: "deviceId",
35997
+ form: "single",
35998
+ optional: true
35999
+ }],
35724
36000
  "recording.relocateFootage": [{
35725
36001
  name: "deviceId",
35726
36002
  form: "single",
@@ -35786,6 +36062,11 @@ Object.freeze({
35786
36062
  form: "single",
35787
36063
  optional: false
35788
36064
  }],
36065
+ "sceneMonitor.listScenesBatch": [{
36066
+ name: "deviceIds",
36067
+ form: "array",
36068
+ optional: false
36069
+ }],
35789
36070
  "sceneMonitor.recheckNow": [{
35790
36071
  name: "deviceId",
35791
36072
  form: "single",
@@ -36047,6 +36328,11 @@ Object.freeze({
36047
36328
  form: "single",
36048
36329
  optional: false
36049
36330
  }],
36331
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36332
+ name: "deviceIds",
36333
+ form: "array",
36334
+ optional: false
36335
+ }],
36050
36336
  "zoneAnalytics.getUnzonedHistory": [{
36051
36337
  name: "deviceId",
36052
36338
  form: "single",
package/dist/smb.addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-DAxSJcnX.js");
5
+ const require_shared = require("./shared-Bwt_Ye8T.js");
6
6
  let node_crypto = require("node:crypto");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_shared.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, s as storageProviderCapability } from "./shared-hUiD7Ohg.mjs";
1
+ import { c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, s as storageProviderCapability } from "./shared-CQEWmUcA.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as path from "node:path";
4
4
  import * as fsp from "node:fs/promises";
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-DAxSJcnX.js");
5
+ const require_shared = require("./shared-Bwt_Ye8T.js");
6
6
  let node_stream = require("node:stream");
7
7
  let webdav = require("webdav");
8
8
  //#region src/providers/webdav/webdav-config-schema.ts
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-hUiD7Ohg.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-CQEWmUcA.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { AuthType, createClient } from "webdav";
4
4
  //#region src/providers/webdav/webdav-config-schema.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-remote-storage",
3
- "version": "1.2.46",
3
+ "version": "1.2.48",
4
4
  "description": "Remote storage providers (SFTP, S3, WebDAV, SMB) — unifies remote backends behind the storage-provider cap",
5
5
  "keywords": [
6
6
  "camstack",