@camstack/addon-remote-storage 1.2.47 → 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-BvqywmnV.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-Cysa81KY.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-BvqywmnV.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-Cysa81KY.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(),
@@ -18959,7 +19086,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18959
19086
  until: number().optional(),
18960
19087
  kinds: array(string()).optional(),
18961
19088
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18962
- }), 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({
18963
19090
  deviceId: number(),
18964
19091
  since: number(),
18965
19092
  until: number(),
@@ -26671,6 +26798,9 @@ method(object({
26671
26798
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26672
26799
  kind: "query",
26673
26800
  auth: "admin"
26801
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26802
+ kind: "mutation",
26803
+ auth: "admin"
26674
26804
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26675
26805
  kind: "mutation",
26676
26806
  auth: "admin"
@@ -27062,7 +27192,26 @@ var SceneMonitorStatusSchema = object({
27062
27192
  monitors: array(SceneMonitorSchema),
27063
27193
  lastFetchedAt: number()
27064
27194
  });
27065
- 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({
27066
27215
  deviceId: number(),
27067
27216
  label: string(),
27068
27217
  roi: MaskRectShapeSchema,
@@ -28386,6 +28535,27 @@ var CameraOccupancySnapshotSchema = object({
28386
28535
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28387
28536
  });
28388
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
+ /**
28389
28559
  * Time-series resolution. The history methods return one bucket per
28390
28560
  * step over the requested range. Smaller resolutions cost more
28391
28561
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28409,7 +28579,7 @@ var HistoryPointSchema = object({
28409
28579
  /** Object count averaged over the bucket (rounded to nearest integer). */
28410
28580
  count: number().int().nonnegative()
28411
28581
  });
28412
- 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({
28413
28583
  deviceId: number(),
28414
28584
  zoneId: string(),
28415
28585
  className: string().optional()
@@ -31714,6 +31884,12 @@ Object.freeze({
31714
31884
  addonId: null,
31715
31885
  access: "view"
31716
31886
  },
31887
+ "pipelineAnalytics.getKeyEventsBatch": {
31888
+ capName: "pipeline-analytics",
31889
+ capScope: "device",
31890
+ addonId: null,
31891
+ access: "view"
31892
+ },
31717
31893
  "pipelineAnalytics.getMotionEvents": {
31718
31894
  capName: "pipeline-analytics",
31719
31895
  capScope: "device",
@@ -32890,6 +33066,12 @@ Object.freeze({
32890
33066
  addonId: null,
32891
33067
  access: "view"
32892
33068
  },
33069
+ "recording.reconcileLedgerAgainstDisk": {
33070
+ capName: "recording",
33071
+ capScope: "system",
33072
+ addonId: null,
33073
+ access: "create"
33074
+ },
32893
33075
  "recording.refreshStorageLocationsForMigration": {
32894
33076
  capName: "recording",
32895
33077
  capScope: "system",
@@ -33016,6 +33198,12 @@ Object.freeze({
33016
33198
  addonId: null,
33017
33199
  access: "view"
33018
33200
  },
33201
+ "sceneMonitor.listScenesBatch": {
33202
+ capName: "scene-monitor",
33203
+ capScope: "device",
33204
+ addonId: null,
33205
+ access: "view"
33206
+ },
33019
33207
  "sceneMonitor.recheckNow": {
33020
33208
  capName: "scene-monitor",
33021
33209
  capScope: "device",
@@ -34372,6 +34560,12 @@ Object.freeze({
34372
34560
  addonId: null,
34373
34561
  access: "view"
34374
34562
  },
34563
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34564
+ capName: "zone-analytics",
34565
+ capScope: "device",
34566
+ addonId: null,
34567
+ access: "view"
34568
+ },
34375
34569
  "zoneAnalytics.getUnzonedHistory": {
34376
34570
  capName: "zone-analytics",
34377
34571
  capScope: "device",
@@ -35376,6 +35570,11 @@ Object.freeze({
35376
35570
  form: "single",
35377
35571
  optional: false
35378
35572
  }],
35573
+ "pipelineAnalytics.getKeyEventsBatch": [{
35574
+ name: "deviceIds",
35575
+ form: "array",
35576
+ optional: false
35577
+ }],
35379
35578
  "pipelineAnalytics.getMotionEvents": [{
35380
35579
  name: "deviceId",
35381
35580
  form: "single",
@@ -35816,6 +36015,11 @@ Object.freeze({
35816
36015
  form: "single",
35817
36016
  optional: false
35818
36017
  }],
36018
+ "recording.reconcileLedgerAgainstDisk": [{
36019
+ name: "deviceId",
36020
+ form: "single",
36021
+ optional: true
36022
+ }],
35819
36023
  "recording.relocateFootage": [{
35820
36024
  name: "deviceId",
35821
36025
  form: "single",
@@ -35881,6 +36085,11 @@ Object.freeze({
35881
36085
  form: "single",
35882
36086
  optional: false
35883
36087
  }],
36088
+ "sceneMonitor.listScenesBatch": [{
36089
+ name: "deviceIds",
36090
+ form: "array",
36091
+ optional: false
36092
+ }],
35884
36093
  "sceneMonitor.recheckNow": [{
35885
36094
  name: "deviceId",
35886
36095
  form: "single",
@@ -36142,6 +36351,11 @@ Object.freeze({
36142
36351
  form: "single",
36143
36352
  optional: false
36144
36353
  }],
36354
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36355
+ name: "deviceIds",
36356
+ form: "array",
36357
+ optional: false
36358
+ }],
36145
36359
  "zoneAnalytics.getUnzonedHistory": [{
36146
36360
  name: "deviceId",
36147
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(),
@@ -18936,7 +19063,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18936
19063
  until: number().optional(),
18937
19064
  kinds: array(string()).optional(),
18938
19065
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
18939
- }), 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({
18940
19067
  deviceId: number(),
18941
19068
  since: number(),
18942
19069
  until: number(),
@@ -26648,6 +26775,9 @@ method(object({
26648
26775
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26649
26776
  kind: "query",
26650
26777
  auth: "admin"
26778
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
26779
+ kind: "mutation",
26780
+ auth: "admin"
26651
26781
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26652
26782
  kind: "mutation",
26653
26783
  auth: "admin"
@@ -27039,7 +27169,26 @@ var SceneMonitorStatusSchema = object({
27039
27169
  monitors: array(SceneMonitorSchema),
27040
27170
  lastFetchedAt: number()
27041
27171
  });
27042
- 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({
27043
27192
  deviceId: number(),
27044
27193
  label: string(),
27045
27194
  roi: MaskRectShapeSchema,
@@ -28363,6 +28512,27 @@ var CameraOccupancySnapshotSchema = object({
28363
28512
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28364
28513
  });
28365
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
+ /**
28366
28536
  * Time-series resolution. The history methods return one bucket per
28367
28537
  * step over the requested range. Smaller resolutions cost more
28368
28538
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28386,7 +28556,7 @@ var HistoryPointSchema = object({
28386
28556
  /** Object count averaged over the bucket (rounded to nearest integer). */
28387
28557
  count: number().int().nonnegative()
28388
28558
  });
28389
- 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({
28390
28560
  deviceId: number(),
28391
28561
  zoneId: string(),
28392
28562
  className: string().optional()
@@ -31691,6 +31861,12 @@ Object.freeze({
31691
31861
  addonId: null,
31692
31862
  access: "view"
31693
31863
  },
31864
+ "pipelineAnalytics.getKeyEventsBatch": {
31865
+ capName: "pipeline-analytics",
31866
+ capScope: "device",
31867
+ addonId: null,
31868
+ access: "view"
31869
+ },
31694
31870
  "pipelineAnalytics.getMotionEvents": {
31695
31871
  capName: "pipeline-analytics",
31696
31872
  capScope: "device",
@@ -32867,6 +33043,12 @@ Object.freeze({
32867
33043
  addonId: null,
32868
33044
  access: "view"
32869
33045
  },
33046
+ "recording.reconcileLedgerAgainstDisk": {
33047
+ capName: "recording",
33048
+ capScope: "system",
33049
+ addonId: null,
33050
+ access: "create"
33051
+ },
32870
33052
  "recording.refreshStorageLocationsForMigration": {
32871
33053
  capName: "recording",
32872
33054
  capScope: "system",
@@ -32993,6 +33175,12 @@ Object.freeze({
32993
33175
  addonId: null,
32994
33176
  access: "view"
32995
33177
  },
33178
+ "sceneMonitor.listScenesBatch": {
33179
+ capName: "scene-monitor",
33180
+ capScope: "device",
33181
+ addonId: null,
33182
+ access: "view"
33183
+ },
32996
33184
  "sceneMonitor.recheckNow": {
32997
33185
  capName: "scene-monitor",
32998
33186
  capScope: "device",
@@ -34349,6 +34537,12 @@ Object.freeze({
34349
34537
  addonId: null,
34350
34538
  access: "view"
34351
34539
  },
34540
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34541
+ capName: "zone-analytics",
34542
+ capScope: "device",
34543
+ addonId: null,
34544
+ access: "view"
34545
+ },
34352
34546
  "zoneAnalytics.getUnzonedHistory": {
34353
34547
  capName: "zone-analytics",
34354
34548
  capScope: "device",
@@ -35353,6 +35547,11 @@ Object.freeze({
35353
35547
  form: "single",
35354
35548
  optional: false
35355
35549
  }],
35550
+ "pipelineAnalytics.getKeyEventsBatch": [{
35551
+ name: "deviceIds",
35552
+ form: "array",
35553
+ optional: false
35554
+ }],
35356
35555
  "pipelineAnalytics.getMotionEvents": [{
35357
35556
  name: "deviceId",
35358
35557
  form: "single",
@@ -35793,6 +35992,11 @@ Object.freeze({
35793
35992
  form: "single",
35794
35993
  optional: false
35795
35994
  }],
35995
+ "recording.reconcileLedgerAgainstDisk": [{
35996
+ name: "deviceId",
35997
+ form: "single",
35998
+ optional: true
35999
+ }],
35796
36000
  "recording.relocateFootage": [{
35797
36001
  name: "deviceId",
35798
36002
  form: "single",
@@ -35858,6 +36062,11 @@ Object.freeze({
35858
36062
  form: "single",
35859
36063
  optional: false
35860
36064
  }],
36065
+ "sceneMonitor.listScenesBatch": [{
36066
+ name: "deviceIds",
36067
+ form: "array",
36068
+ optional: false
36069
+ }],
35861
36070
  "sceneMonitor.recheckNow": [{
35862
36071
  name: "deviceId",
35863
36072
  form: "single",
@@ -36119,6 +36328,11 @@ Object.freeze({
36119
36328
  form: "single",
36120
36329
  optional: false
36121
36330
  }],
36331
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36332
+ name: "deviceIds",
36333
+ form: "array",
36334
+ optional: false
36335
+ }],
36122
36336
  "zoneAnalytics.getUnzonedHistory": [{
36123
36337
  name: "deviceId",
36124
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-BvqywmnV.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-Cysa81KY.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-BvqywmnV.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-Cysa81KY.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.47",
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",