@camstack/addon-remote-storage 1.1.24 → 1.1.26

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-BMMO0bPW.js");
5
+ const require_shared = require("./shared-BU3WLevm.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-CZllXqB6.mjs";
1
+ import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-Djb5j9fy.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-BMMO0bPW.js");
5
+ const require_shared = require("./shared-BU3WLevm.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-CZllXqB6.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-Djb5j9fy.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
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  let node_path = require("node:path");
24
24
  node_path = __toESM(node_path);
25
25
  let node_crypto = require("node:crypto");
26
- //#region ../types/dist/event-category-CFZs3jI4.mjs
26
+ //#region ../types/dist/event-category-H4AVePnn.mjs
27
27
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
28
28
  EventCategory["SystemBoot"] = "system.boot";
29
29
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -173,6 +173,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
173
173
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
174
174
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
175
175
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
176
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
177
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
178
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
176
179
  EventCategory["DetectionEvent"] = "detection.event";
177
180
  EventCategory["SessionTrackNew"] = "session.track.new";
178
181
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7242,6 +7245,24 @@ var RecordingRetentionSchema = object({
7242
7245
  maxSizeGb: number().min(0).optional()
7243
7246
  });
7244
7247
  /**
7248
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7249
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7250
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7251
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7252
+ *
7253
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7254
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7255
+ * preset changed over time renders every historical window at the dims it was
7256
+ * written with.
7257
+ */
7258
+ var ScrubThumbnailPresetSchema = _enum([
7259
+ "minimal",
7260
+ "low",
7261
+ "standard",
7262
+ "high",
7263
+ "max"
7264
+ ]);
7265
+ /**
7245
7266
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7246
7267
  *
7247
7268
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7278,7 +7299,14 @@ var RecordingConfigSchema = object({
7278
7299
  * derived into bands once via `migrateConfigToBands`.
7279
7300
  */
7280
7301
  bands: array(RecordingBandSchema).optional(),
7281
- retention: RecordingRetentionSchema.optional()
7302
+ retention: RecordingRetentionSchema.optional(),
7303
+ /**
7304
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7305
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7306
+ * windows only — existing sheets are immutable, and each window's index
7307
+ * carries its own tile dims so mixed-preset history renders correctly.
7308
+ */
7309
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7282
7310
  });
7283
7311
  /**
7284
7312
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9837,7 +9865,7 @@ object({
9837
9865
  deviceId: number(),
9838
9866
  timestamp: number()
9839
9867
  });
9840
- DeviceType.Button;
9868
+ DeviceType.Button, DeviceType.Camera;
9841
9869
  /**
9842
9870
  * Enum-state sensor — a string value picked from a finite option set.
9843
9871
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14207,6 +14235,14 @@ var ConfigEntrySchema = object({
14207
14235
  value: unknown(),
14208
14236
  description: string().optional()
14209
14237
  });
14238
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14239
+ /** One resolved linked device — the compact projection consumers need. */
14240
+ var LinkedDeviceSchema = object({
14241
+ deviceId: number(),
14242
+ name: string(),
14243
+ location: string().nullable(),
14244
+ features: array(string())
14245
+ });
14210
14246
  var SavedDeviceRowSchema = object({
14211
14247
  /** Numeric id reserved at allocateDeviceId time. */
14212
14248
  id: number(),
@@ -14428,7 +14464,10 @@ method(object({
14428
14464
  }), _void(), {
14429
14465
  kind: "mutation",
14430
14466
  auth: "admin"
14431
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14467
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
14468
+ mode: DeviceLinkModeSchema,
14469
+ devices: array(LinkedDeviceSchema)
14470
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14432
14471
  deviceId: number(),
14433
14472
  values: record(string(), unknown())
14434
14473
  }), object({ success: literal(true) }), {
@@ -15697,6 +15736,61 @@ var EventKindSchema = _enum([
15697
15736
  "object",
15698
15737
  "audio"
15699
15738
  ]);
15739
+ /**
15740
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15741
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15742
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15743
+ */
15744
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15745
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15746
+ var EventKindIconSchema = _enum([
15747
+ "motion",
15748
+ "audio",
15749
+ "person",
15750
+ "vehicle",
15751
+ "animal",
15752
+ "door",
15753
+ "pir",
15754
+ "smoke",
15755
+ "water",
15756
+ "button",
15757
+ "generic"
15758
+ ]);
15759
+ var EventKindCategorySchema = _enum([
15760
+ "motion",
15761
+ "audio",
15762
+ "detection",
15763
+ "sensor",
15764
+ "custom"
15765
+ ]);
15766
+ var EventKindDescriptorSchema = object({
15767
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15768
+ kind: string(),
15769
+ label: string(),
15770
+ /** Hex color for timeline/legend rendering. */
15771
+ color: string(),
15772
+ icon: EventKindIconSchema,
15773
+ category: EventKindCategorySchema,
15774
+ /** Which cap + device contributes this kind. For built-ins the camera
15775
+ * itself; for sensor kinds the LINKED source device. */
15776
+ source: object({
15777
+ capName: string(),
15778
+ deviceId: number()
15779
+ })
15780
+ });
15781
+ var SensorEventSchema = object({
15782
+ id: string(),
15783
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15784
+ * yields N rows, one per camera). */
15785
+ deviceId: number(),
15786
+ /** The linked sensor device whose state changed. */
15787
+ sourceDeviceId: number(),
15788
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15789
+ kind: string(),
15790
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15791
+ value: record(string(), unknown()).nullable(),
15792
+ timestamp: number()
15793
+ });
15700
15794
  var TrackPositionSchema = object({
15701
15795
  x: number(),
15702
15796
  y: number(),
@@ -15710,6 +15804,30 @@ var TrackSnapshotSchema = object({
15710
15804
  mediaKey: string()
15711
15805
  });
15712
15806
  /**
15807
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15808
+ * divided by the track's detection-frame dims), computed at persist time.
15809
+ * Absent when the frame dims were unknown when the track was persisted
15810
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15811
+ */
15812
+ var TrackEnvelopeSchema = object({
15813
+ minX: number(),
15814
+ minY: number(),
15815
+ maxX: number(),
15816
+ maxY: number()
15817
+ });
15818
+ /**
15819
+ * Row projection for track list queries. `full` (default) returns the
15820
+ * complete Track including the frame-rate `positions[]` history and the
15821
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15822
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15823
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15824
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15825
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15826
+ * `getTrack`. Mirrors the event-store `projection` convention
15827
+ * (`getObjectEvents` et al.).
15828
+ */
15829
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15830
+ /**
15713
15831
  * One audio-classification label heard on the track's camera while the
15714
15832
  * track was alive, aggregated per label. An "episode" is one persisted
15715
15833
  * audio event (the confident-classification path: score ≥ the device's
@@ -15760,7 +15878,11 @@ var TrackSchema = object({
15760
15878
  /** Audio-classification labels heard on the camera during the track's
15761
15879
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15762
15880
  * Absent on legacy rows / tracks with no confident audio. */
15763
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
15881
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
15882
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
15883
+ * Populated from the persisted envelope columns on historical reads;
15884
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
15885
+ envelope: TrackEnvelopeSchema.optional()
15764
15886
  });
15765
15887
  var BaseEventFields = {
15766
15888
  id: string(),
@@ -15870,11 +15992,13 @@ var MediaFileSchema = object({
15870
15992
  sizeBytes: number(),
15871
15993
  timestamp: number()
15872
15994
  });
15995
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
15996
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15873
15997
  var DeviceEventQueryInput = object({
15874
15998
  deviceId: number(),
15875
15999
  since: number().optional(),
15876
16000
  until: number().optional(),
15877
- limit: number().int().min(1).max(5e3).default(1e3),
16001
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15878
16002
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15879
16003
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15880
16004
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -15882,6 +16006,27 @@ var DeviceEventQueryInput = object({
15882
16006
  projection: _enum(["full", "slim"]).optional()
15883
16007
  });
15884
16008
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16009
+ var RecentTracksQueryInput = object({
16010
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16011
+ deviceIds: array(number()),
16012
+ /** Window lower bound on `lastSeen` (inclusive). */
16013
+ since: number().optional(),
16014
+ /** Window upper bound on `lastSeen` (inclusive). */
16015
+ until: number().optional(),
16016
+ /** Page size. Default 200, max 1000. */
16017
+ limit: number().int().min(1).max(1e3).default(200),
16018
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16019
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16020
+ cursor: string().optional(),
16021
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16022
+ projection: TrackProjectionSchema.optional()
16023
+ });
16024
+ var RecentTracksPageSchema = object({
16025
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16026
+ tracks: array(TrackSchema).readonly(),
16027
+ /** Cursor for the next page, or null when this page is the last. */
16028
+ nextCursor: string().nullable()
16029
+ });
15885
16030
  var KeyEventQueryInput = object({
15886
16031
  deviceId: number(),
15887
16032
  /** Window lower bound (track firstSeen ≥ since). */
@@ -15964,11 +16109,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15964
16109
  deviceId: number(),
15965
16110
  since: number().optional(),
15966
16111
  until: number().optional(),
15967
- limit: number().optional()
15968
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
15969
- kind: "mutation",
15970
- auth: "admin"
15971
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16112
+ limit: number().optional(),
16113
+ /** Spatial filter only tracks whose trajectory intersects the zone
16114
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16115
+ * envelope columns, then precisely tested per position. Tracks with
16116
+ * an unknown envelope (no frame dims at persist time) always match. */
16117
+ zone: TrackZoneFilterSchema.optional(),
16118
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16119
+ * compatible — omitting the field keeps today's exact behaviour). */
16120
+ projection: TrackProjectionSchema.optional()
16121
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16122
+ kind: "mutation",
16123
+ auth: "admin"
16124
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16125
+ deviceId: number(),
16126
+ since: number().optional(),
16127
+ until: number().optional(),
16128
+ kinds: array(string()).optional(),
16129
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16130
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
15972
16131
  deviceId: number(),
15973
16132
  since: number(),
15974
16133
  until: number(),
@@ -19214,7 +19373,12 @@ var RecordingStorageUsageSchema = object({
19214
19373
  /**
19215
19374
  * Result of locating footage at a wall-clock instant for one device/profile.
19216
19375
  * `segment` carries the covering segment's window; `gap` reports the forward
19217
- * nearest covered edge (`null` past the end of footage / when none exists).
19376
+ * nearest covered edge (`null` past the end of footage / when none exists)
19377
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19378
+ * footage behind the epoch; `null` when none — optional so older providers
19379
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19380
+ * small inter-segment cracks (durMs under-covers the span to the next
19381
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19218
19382
  */
19219
19383
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19220
19384
  kind: literal("segment"),
@@ -19223,7 +19387,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19223
19387
  bytes: number()
19224
19388
  }), object({
19225
19389
  kind: literal("gap"),
19226
- nearestEdgeMs: number().nullable()
19390
+ nearestEdgeMs: number().nullable(),
19391
+ prevEndMs: number().nullable().optional()
19227
19392
  })]);
19228
19393
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19229
19394
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20792,6 +20957,12 @@ Object.freeze({
20792
20957
  addonId: null,
20793
20958
  access: "view"
20794
20959
  },
20960
+ "deviceManager.getLinkedDevices": {
20961
+ capName: "device-manager",
20962
+ capScope: "system",
20963
+ addonId: null,
20964
+ access: "view"
20965
+ },
20795
20966
  "deviceManager.getRoleDisplayDefaults": {
20796
20967
  capName: "device-manager",
20797
20968
  capScope: "system",
@@ -22346,6 +22517,12 @@ Object.freeze({
22346
22517
  addonId: null,
22347
22518
  access: "view"
22348
22519
  },
22520
+ "pipelineAnalytics.getSensorEvents": {
22521
+ capName: "pipeline-analytics",
22522
+ capScope: "device",
22523
+ addonId: null,
22524
+ access: "view"
22525
+ },
22349
22526
  "pipelineAnalytics.getTrack": {
22350
22527
  capName: "pipeline-analytics",
22351
22528
  capScope: "device",
@@ -22358,6 +22535,18 @@ Object.freeze({
22358
22535
  addonId: null,
22359
22536
  access: "view"
22360
22537
  },
22538
+ "pipelineAnalytics.listEventKinds": {
22539
+ capName: "pipeline-analytics",
22540
+ capScope: "device",
22541
+ addonId: null,
22542
+ access: "view"
22543
+ },
22544
+ "pipelineAnalytics.listRecentTracks": {
22545
+ capName: "pipeline-analytics",
22546
+ capScope: "device",
22547
+ addonId: null,
22548
+ access: "view"
22549
+ },
22361
22550
  "pipelineAnalytics.listTracks": {
22362
22551
  capName: "pipeline-analytics",
22363
22552
  capScope: "device",
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import { randomUUID } from "node:crypto";
3
- //#region ../types/dist/event-category-CFZs3jI4.mjs
3
+ //#region ../types/dist/event-category-H4AVePnn.mjs
4
4
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
5
5
  EventCategory["SystemBoot"] = "system.boot";
6
6
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -150,6 +150,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
150
150
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
151
151
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
152
152
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
153
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
154
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
155
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
153
156
  EventCategory["DetectionEvent"] = "detection.event";
154
157
  EventCategory["SessionTrackNew"] = "session.track.new";
155
158
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -7219,6 +7222,24 @@ var RecordingRetentionSchema = object({
7219
7222
  maxSizeGb: number().min(0).optional()
7220
7223
  });
7221
7224
  /**
7225
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
7226
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
7227
+ * previews at. Five graduated steps; absent on a config = `standard` (the
7228
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
7229
+ *
7230
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
7231
+ * Each window's index sidecar carries its own tile dims, so a camera whose
7232
+ * preset changed over time renders every historical window at the dims it was
7233
+ * written with.
7234
+ */
7235
+ var ScrubThumbnailPresetSchema = _enum([
7236
+ "minimal",
7237
+ "low",
7238
+ "standard",
7239
+ "high",
7240
+ "max"
7241
+ ]);
7242
+ /**
7222
7243
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
7223
7244
  *
7224
7245
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -7255,7 +7276,14 @@ var RecordingConfigSchema = object({
7255
7276
  * derived into bands once via `migrateConfigToBands`.
7256
7277
  */
7257
7278
  bands: array(RecordingBandSchema).optional(),
7258
- retention: RecordingRetentionSchema.optional()
7279
+ retention: RecordingRetentionSchema.optional(),
7280
+ /**
7281
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
7282
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
7283
+ * windows only — existing sheets are immutable, and each window's index
7284
+ * carries its own tile dims so mixed-preset history renders correctly.
7285
+ */
7286
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
7259
7287
  });
7260
7288
  /**
7261
7289
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -9814,7 +9842,7 @@ object({
9814
9842
  deviceId: number(),
9815
9843
  timestamp: number()
9816
9844
  });
9817
- DeviceType.Button;
9845
+ DeviceType.Button, DeviceType.Camera;
9818
9846
  /**
9819
9847
  * Enum-state sensor — a string value picked from a finite option set.
9820
9848
  * Drives HA `sensor` entries with `state_class: enum` (HVAC action
@@ -14184,6 +14212,14 @@ var ConfigEntrySchema = object({
14184
14212
  value: unknown(),
14185
14213
  description: string().optional()
14186
14214
  });
14215
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
14216
+ /** One resolved linked device — the compact projection consumers need. */
14217
+ var LinkedDeviceSchema = object({
14218
+ deviceId: number(),
14219
+ name: string(),
14220
+ location: string().nullable(),
14221
+ features: array(string())
14222
+ });
14187
14223
  var SavedDeviceRowSchema = object({
14188
14224
  /** Numeric id reserved at allocateDeviceId time. */
14189
14225
  id: number(),
@@ -14405,7 +14441,10 @@ method(object({
14405
14441
  }), _void(), {
14406
14442
  kind: "mutation",
14407
14443
  auth: "admin"
14408
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14444
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
14445
+ mode: DeviceLinkModeSchema,
14446
+ devices: array(LinkedDeviceSchema)
14447
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
14409
14448
  deviceId: number(),
14410
14449
  values: record(string(), unknown())
14411
14450
  }), object({ success: literal(true) }), {
@@ -15674,6 +15713,61 @@ var EventKindSchema = _enum([
15674
15713
  "object",
15675
15714
  "audio"
15676
15715
  ]);
15716
+ /**
15717
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15718
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15719
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15720
+ */
15721
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15722
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15723
+ var EventKindIconSchema = _enum([
15724
+ "motion",
15725
+ "audio",
15726
+ "person",
15727
+ "vehicle",
15728
+ "animal",
15729
+ "door",
15730
+ "pir",
15731
+ "smoke",
15732
+ "water",
15733
+ "button",
15734
+ "generic"
15735
+ ]);
15736
+ var EventKindCategorySchema = _enum([
15737
+ "motion",
15738
+ "audio",
15739
+ "detection",
15740
+ "sensor",
15741
+ "custom"
15742
+ ]);
15743
+ var EventKindDescriptorSchema = object({
15744
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15745
+ kind: string(),
15746
+ label: string(),
15747
+ /** Hex color for timeline/legend rendering. */
15748
+ color: string(),
15749
+ icon: EventKindIconSchema,
15750
+ category: EventKindCategorySchema,
15751
+ /** Which cap + device contributes this kind. For built-ins the camera
15752
+ * itself; for sensor kinds the LINKED source device. */
15753
+ source: object({
15754
+ capName: string(),
15755
+ deviceId: number()
15756
+ })
15757
+ });
15758
+ var SensorEventSchema = object({
15759
+ id: string(),
15760
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15761
+ * yields N rows, one per camera). */
15762
+ deviceId: number(),
15763
+ /** The linked sensor device whose state changed. */
15764
+ sourceDeviceId: number(),
15765
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15766
+ kind: string(),
15767
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15768
+ value: record(string(), unknown()).nullable(),
15769
+ timestamp: number()
15770
+ });
15677
15771
  var TrackPositionSchema = object({
15678
15772
  x: number(),
15679
15773
  y: number(),
@@ -15687,6 +15781,30 @@ var TrackSnapshotSchema = object({
15687
15781
  mediaKey: string()
15688
15782
  });
15689
15783
  /**
15784
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15785
+ * divided by the track's detection-frame dims), computed at persist time.
15786
+ * Absent when the frame dims were unknown when the track was persisted
15787
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15788
+ */
15789
+ var TrackEnvelopeSchema = object({
15790
+ minX: number(),
15791
+ minY: number(),
15792
+ maxX: number(),
15793
+ maxY: number()
15794
+ });
15795
+ /**
15796
+ * Row projection for track list queries. `full` (default) returns the
15797
+ * complete Track including the frame-rate `positions[]` history and the
15798
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15799
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15800
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15801
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15802
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15803
+ * `getTrack`. Mirrors the event-store `projection` convention
15804
+ * (`getObjectEvents` et al.).
15805
+ */
15806
+ var TrackProjectionSchema = _enum(["full", "slim"]);
15807
+ /**
15690
15808
  * One audio-classification label heard on the track's camera while the
15691
15809
  * track was alive, aggregated per label. An "episode" is one persisted
15692
15810
  * audio event (the confident-classification path: score ≥ the device's
@@ -15737,7 +15855,11 @@ var TrackSchema = object({
15737
15855
  /** Audio-classification labels heard on the camera during the track's
15738
15856
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15739
15857
  * Absent on legacy rows / tracks with no confident audio. */
15740
- audioLabels: array(TrackAudioLabelSchema).readonly().optional()
15858
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
15859
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
15860
+ * Populated from the persisted envelope columns on historical reads;
15861
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
15862
+ envelope: TrackEnvelopeSchema.optional()
15741
15863
  });
15742
15864
  var BaseEventFields = {
15743
15865
  id: string(),
@@ -15847,11 +15969,13 @@ var MediaFileSchema = object({
15847
15969
  sizeBytes: number(),
15848
15970
  timestamp: number()
15849
15971
  });
15972
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
15973
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15850
15974
  var DeviceEventQueryInput = object({
15851
15975
  deviceId: number(),
15852
15976
  since: number().optional(),
15853
15977
  until: number().optional(),
15854
- limit: number().int().min(1).max(5e3).default(1e3),
15978
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15855
15979
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15856
15980
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15857
15981
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -15859,6 +15983,27 @@ var DeviceEventQueryInput = object({
15859
15983
  projection: _enum(["full", "slim"]).optional()
15860
15984
  });
15861
15985
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
15986
+ var RecentTracksQueryInput = object({
15987
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
15988
+ deviceIds: array(number()),
15989
+ /** Window lower bound on `lastSeen` (inclusive). */
15990
+ since: number().optional(),
15991
+ /** Window upper bound on `lastSeen` (inclusive). */
15992
+ until: number().optional(),
15993
+ /** Page size. Default 200, max 1000. */
15994
+ limit: number().int().min(1).max(1e3).default(200),
15995
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
15996
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
15997
+ cursor: string().optional(),
15998
+ /** See {@link TrackProjectionSchema}. Default `full`. */
15999
+ projection: TrackProjectionSchema.optional()
16000
+ });
16001
+ var RecentTracksPageSchema = object({
16002
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16003
+ tracks: array(TrackSchema).readonly(),
16004
+ /** Cursor for the next page, or null when this page is the last. */
16005
+ nextCursor: string().nullable()
16006
+ });
15862
16007
  var KeyEventQueryInput = object({
15863
16008
  deviceId: number(),
15864
16009
  /** Window lower bound (track firstSeen ≥ since). */
@@ -15941,11 +16086,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15941
16086
  deviceId: number(),
15942
16087
  since: number().optional(),
15943
16088
  until: number().optional(),
15944
- limit: number().optional()
15945
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
15946
- kind: "mutation",
15947
- auth: "admin"
15948
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16089
+ limit: number().optional(),
16090
+ /** Spatial filter only tracks whose trajectory intersects the zone
16091
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16092
+ * envelope columns, then precisely tested per position. Tracks with
16093
+ * an unknown envelope (no frame dims at persist time) always match. */
16094
+ zone: TrackZoneFilterSchema.optional(),
16095
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16096
+ * compatible — omitting the field keeps today's exact behaviour). */
16097
+ projection: TrackProjectionSchema.optional()
16098
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16099
+ kind: "mutation",
16100
+ auth: "admin"
16101
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
16102
+ deviceId: number(),
16103
+ since: number().optional(),
16104
+ until: number().optional(),
16105
+ kinds: array(string()).optional(),
16106
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16107
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
15949
16108
  deviceId: number(),
15950
16109
  since: number(),
15951
16110
  until: number(),
@@ -19191,7 +19350,12 @@ var RecordingStorageUsageSchema = object({
19191
19350
  /**
19192
19351
  * Result of locating footage at a wall-clock instant for one device/profile.
19193
19352
  * `segment` carries the covering segment's window; `gap` reports the forward
19194
- * nearest covered edge (`null` past the end of footage / when none exists).
19353
+ * nearest covered edge (`null` past the end of footage / when none exists)
19354
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
19355
+ * footage behind the epoch; `null` when none — optional so older providers
19356
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
19357
+ * small inter-segment cracks (durMs under-covers the span to the next
19358
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
19195
19359
  */
19196
19360
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19197
19361
  kind: literal("segment"),
@@ -19200,7 +19364,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
19200
19364
  bytes: number()
19201
19365
  }), object({
19202
19366
  kind: literal("gap"),
19203
- nearestEdgeMs: number().nullable()
19367
+ nearestEdgeMs: number().nullable(),
19368
+ prevEndMs: number().nullable().optional()
19204
19369
  })]);
19205
19370
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
19206
19371
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -20769,6 +20934,12 @@ Object.freeze({
20769
20934
  addonId: null,
20770
20935
  access: "view"
20771
20936
  },
20937
+ "deviceManager.getLinkedDevices": {
20938
+ capName: "device-manager",
20939
+ capScope: "system",
20940
+ addonId: null,
20941
+ access: "view"
20942
+ },
20772
20943
  "deviceManager.getRoleDisplayDefaults": {
20773
20944
  capName: "device-manager",
20774
20945
  capScope: "system",
@@ -22323,6 +22494,12 @@ Object.freeze({
22323
22494
  addonId: null,
22324
22495
  access: "view"
22325
22496
  },
22497
+ "pipelineAnalytics.getSensorEvents": {
22498
+ capName: "pipeline-analytics",
22499
+ capScope: "device",
22500
+ addonId: null,
22501
+ access: "view"
22502
+ },
22326
22503
  "pipelineAnalytics.getTrack": {
22327
22504
  capName: "pipeline-analytics",
22328
22505
  capScope: "device",
@@ -22335,6 +22512,18 @@ Object.freeze({
22335
22512
  addonId: null,
22336
22513
  access: "view"
22337
22514
  },
22515
+ "pipelineAnalytics.listEventKinds": {
22516
+ capName: "pipeline-analytics",
22517
+ capScope: "device",
22518
+ addonId: null,
22519
+ access: "view"
22520
+ },
22521
+ "pipelineAnalytics.listRecentTracks": {
22522
+ capName: "pipeline-analytics",
22523
+ capScope: "device",
22524
+ addonId: null,
22525
+ access: "view"
22526
+ },
22338
22527
  "pipelineAnalytics.listTracks": {
22339
22528
  capName: "pipeline-analytics",
22340
22529
  capScope: "device",
@@ -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-BMMO0bPW.js");
5
+ const require_shared = require("./shared-BU3WLevm.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-CZllXqB6.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-Djb5j9fy.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.1.24",
3
+ "version": "1.1.26",
4
4
  "description": "Remote storage providers (SFTP, S3, WebDAV) — unifies remote backends behind the storage-provider cap",
5
5
  "keywords": [
6
6
  "camstack",