@camstack/system 1.1.51 → 1.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  9. package/dist/builtins/alerts/alerts.addon.js +1 -1
  10. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/console-logging/index.js +1 -1
  14. package/dist/builtins/console-logging/index.mjs +1 -1
  15. package/dist/builtins/device-manager/device-linked-devices.d.ts +47 -0
  16. package/dist/builtins/device-manager/device-manager.addon.js +2821 -2683
  17. package/dist/builtins/device-manager/device-manager.addon.mjs +2821 -2683
  18. package/dist/builtins/doorbell/index.d.ts +2 -0
  19. package/dist/builtins/doorbell/index.js +8 -0
  20. package/dist/builtins/doorbell/index.mjs +2 -0
  21. package/dist/builtins/doorbell/trigger-engine.d.ts +69 -0
  22. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +87 -0
  23. package/dist/builtins/doorbell/virtual-doorbell.addon.js +489 -0
  24. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +483 -0
  25. package/dist/builtins/hub-forwarder/index.js +1 -1
  26. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  27. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  28. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  29. package/dist/builtins/local-network/local-network.addon.js +1 -1
  30. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  33. package/dist/builtins/platform-probe/index.js +1 -1
  34. package/dist/builtins/platform-probe/index.mjs +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  37. package/dist/builtins/snapshot/index.js +1 -1
  38. package/dist/builtins/snapshot/index.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +11 -3
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +11 -3
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  45. package/dist/builtins/system-config/system-config.addon.js +4 -4
  46. package/dist/builtins/system-config/system-config.addon.mjs +4 -4
  47. package/dist/builtins/winston-logging/index.js +1 -1
  48. package/dist/builtins/winston-logging/index.mjs +1 -1
  49. package/dist/{dist-C3uHEBtP.js → dist-BNro5CRu.js} +325 -27
  50. package/dist/{dist-BWQX9yUj.mjs → dist-D7_69kDt.mjs} +320 -28
  51. package/dist/http/file-data-plane.d.ts +8 -0
  52. package/dist/index.js +23 -6
  53. package/dist/index.mjs +23 -7
  54. package/dist/kernel/index.d.ts +1 -1
  55. package/dist/kernel/moleculer/readiness-context.d.ts +12 -8
  56. package/dist/kernel/moleculer/readiness-service.d.ts +16 -1
  57. package/dist/{manifest-python-deps-CVcJ9hDX.js → manifest-python-deps-DszE7bYn.js} +35 -9
  58. package/dist/{manifest-python-deps-BA6KA9If.mjs → manifest-python-deps-EM2H-jjS.mjs} +35 -9
  59. package/dist/{model-download-service-C-IHWnXx.mjs → model-download-service-Cp9f4dk6.mjs} +1 -1
  60. package/dist/{model-download-service-D-Umz4-L.js → model-download-service-hf0ookyy.js} +1 -1
  61. package/package.json +13 -1
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- //#region ../types/dist/event-category-CFZs3jI4.mjs
2
+ //#region ../types/dist/event-category-H4AVePnn.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -149,6 +149,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
149
149
  EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
150
150
  EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
151
151
  EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
152
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
153
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
154
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
152
155
  EventCategory["DetectionEvent"] = "detection.event";
153
156
  EventCategory["SessionTrackNew"] = "session.track.new";
154
157
  EventCategory["SessionTrackExpired"] = "session.track.expired";
@@ -1799,6 +1802,19 @@ function scopeKey(scope) {
1799
1802
  case "device": return `device:${scope.deviceId}`;
1800
1803
  }
1801
1804
  }
1805
+ /**
1806
+ * Producer transition order within ONE generation: `starting → ready →
1807
+ * down`. Used by `hydrate` to accept only forward moves for
1808
+ * same-generation snapshot records (a snapshot can be ahead of a lost
1809
+ * delta, never behind it, within one producer generation).
1810
+ */
1811
+ function stateRank(state) {
1812
+ switch (state) {
1813
+ case "starting": return 0;
1814
+ case "ready": return 1;
1815
+ case "down": return 2;
1816
+ }
1817
+ }
1802
1818
  function scopesEqual(a, b) {
1803
1819
  if (a.type !== b.type) return false;
1804
1820
  if (a.type === "global" || b.type === "global") return true;
@@ -1849,43 +1865,70 @@ var ReadinessRegistry = class {
1849
1865
  return Array.from(this.snapshot.values());
1850
1866
  }
1851
1867
  /**
1852
- * Hydrate the snapshot from an authoritative source. Entries already
1853
- * present locally are skipped live deltas (received via the event
1854
- * bus subscription) always take precedence over the snapshot. For
1855
- * each newly hydrated entry, a one-shot transition is dispatched to
1856
- * matching subscriptions so pending `awaitReady` callers unblock
1857
- * without having to wait for a fresh event.
1868
+ * Hydrate the snapshot from an authoritative source a RECONCILE, not
1869
+ * an add-only merge (D8: readiness events are telemetry and may be
1870
+ * lost; the on-reconnect/periodic snapshot re-pull is the repair path,
1871
+ * so it MUST be able to advance a stale record the 2026-07-17
1872
+ * "still awaiting platform-probe forever" wedge was this method
1873
+ * skipping every already-seen key).
1874
+ *
1875
+ * - **Unseen keys** are added (epoch 1 — mirrors the value the
1876
+ * consumer would have derived from the first observed `ready`).
1877
+ * - **Existing keys** are UPDATED when the authoritative record
1878
+ * carries a different generation (producer restart / synthetic-down
1879
+ * superseded — epoch bumps on a new-generation `ready`, mirroring
1880
+ * `ingest`), or a FORWARD state move within the same generation
1881
+ * (`starting → ready → down`; within one producer generation a
1882
+ * snapshot can only be ahead of a lost delta, never behind it).
1883
+ * - **Never a same-generation regress** — a snapshot pulled just
1884
+ * before a live delta landed must not un-ready a record (the next
1885
+ * reconcile round converges it anyway).
1886
+ * - **Never a record this process is authoritative for** — node-scoped
1887
+ * records for our own nodeId, and device/global records we emitted,
1888
+ * keep local truth (an authority's stale copy of OUR record must
1889
+ * not regress us).
1858
1890
  *
1859
- * Local `epoch` is reset to 1 per entry — consumer-side epoch is
1860
- * derived from observed generation transitions, so this mirrors the
1861
- * value that would have been assigned had the consumer observed the
1862
- * first `ready` event directly.
1891
+ * Every applied entry dispatches a one-shot transition to matching
1892
+ * subscriptions so pending `awaitReady` callers unblock without having
1893
+ * to wait for a fresh event.
1863
1894
  */
1864
1895
  hydrate(records) {
1865
1896
  const now = this.now();
1866
1897
  for (const record of records) {
1867
1898
  const key = readinessKey(record.capName, record.scope);
1868
- if (this.snapshot.has(key)) continue;
1899
+ const prev = this.snapshot.get(key) ?? null;
1900
+ let epoch;
1901
+ let durationInPrevState;
1902
+ if (prev !== null) {
1903
+ if (this.isLocallyAuthoritative(prev)) continue;
1904
+ const sameGeneration = prev.generation === record.generation;
1905
+ if (sameGeneration && stateRank(record.state) <= stateRank(prev.state)) continue;
1906
+ epoch = !sameGeneration && record.state === "ready" ? prev.epoch + 1 : prev.epoch;
1907
+ durationInPrevState = Math.max(0, now - prev.lastChange);
1908
+ } else {
1909
+ epoch = 1;
1910
+ durationInPrevState = 0;
1911
+ }
1869
1912
  const hydrated = {
1870
1913
  capName: record.capName,
1871
1914
  scope: record.scope,
1872
1915
  state: record.state,
1873
1916
  generation: record.generation,
1874
- epoch: 1,
1917
+ epoch,
1875
1918
  lastChange: now,
1876
1919
  sourceNodeId: record.sourceNodeId
1877
1920
  };
1878
1921
  this.snapshot.set(key, hydrated);
1879
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated, gen=${record.generation.slice(0, 6)})`);
1922
+ if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
1880
1923
  const transition = {
1881
1924
  capName: record.capName,
1882
1925
  scope: record.scope,
1883
1926
  state: record.state,
1884
- epoch: 1,
1927
+ epoch,
1885
1928
  generation: record.generation,
1886
1929
  sourceNodeId: "hydrated",
1887
1930
  ts: now,
1888
- durationInPrevState: 0
1931
+ durationInPrevState
1889
1932
  };
1890
1933
  for (const sub of this.subscriptions) {
1891
1934
  if (sub.capName !== record.capName) continue;
@@ -1898,6 +1941,17 @@ var ReadinessRegistry = class {
1898
1941
  }
1899
1942
  }
1900
1943
  }
1944
+ /**
1945
+ * True when THIS process is the origin authority for `record` — a
1946
+ * snapshot pulled from elsewhere must never overwrite it:
1947
+ * - node-scoped records whose `scope.nodeId` is our own node id
1948
+ * (this process — or a child bridged onto our bus — emits them);
1949
+ * - device/global-scoped records whose latest transition WE emitted.
1950
+ */
1951
+ isLocallyAuthoritative(record) {
1952
+ if (record.scope.type === "node") return record.scope.nodeId === this.sourceNodeId;
1953
+ return record.sourceNodeId === this.sourceNodeId;
1954
+ }
1901
1955
  /** Shallow copy of the full snapshot — mainly for diagnostics/tests. */
1902
1956
  getAll() {
1903
1957
  return new Map(this.snapshot);
@@ -3201,6 +3255,24 @@ var RecordingRetentionSchema = z.object({
3201
3255
  maxSizeGb: z.number().min(0).optional()
3202
3256
  });
3203
3257
  /**
3258
+ * Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
3259
+ * sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
3260
+ * previews at. Five graduated steps; absent on a config = `standard` (the
3261
+ * shipped default, matching `sheet-geometry`/`sheet-composer`).
3262
+ *
3263
+ * Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
3264
+ * Each window's index sidecar carries its own tile dims, so a camera whose
3265
+ * preset changed over time renders every historical window at the dims it was
3266
+ * written with.
3267
+ */
3268
+ var ScrubThumbnailPresetSchema = z.enum([
3269
+ "minimal",
3270
+ "low",
3271
+ "standard",
3272
+ "high",
3273
+ "max"
3274
+ ]);
3275
+ /**
3204
3276
  * The full per-camera recording intent — the wire shape of a RecordingTarget.
3205
3277
  *
3206
3278
  * `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
@@ -3237,7 +3309,14 @@ var RecordingConfigSchema = z.object({
3237
3309
  * derived into bands once via `migrateConfigToBands`.
3238
3310
  */
3239
3311
  bands: z.array(RecordingBandSchema).optional(),
3240
- retention: RecordingRetentionSchema.optional()
3312
+ retention: RecordingRetentionSchema.optional(),
3313
+ /**
3314
+ * Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
3315
+ * timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
3316
+ * windows only — existing sheets are immutable, and each window's index
3317
+ * carries its own tile dims so mixed-preset history renders correctly.
3318
+ */
3319
+ scrubThumbnails: ScrubThumbnailPresetSchema.optional()
3241
3320
  });
3242
3321
  /**
3243
3322
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -6903,10 +6982,23 @@ var deviceDiscoveryCapability = {
6903
6982
  }
6904
6983
  };
6905
6984
  /**
6906
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
6907
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
6908
- * time the firmware pushes a ring; status tracks the last press and
6909
- * a pressCount since start (diagnostic).
6985
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
6986
+ * name (same pattern as `snapshot`):
6987
+ *
6988
+ * - **Native** providers: registered per-device by device-driver
6989
+ * addons via `ctx.registerNativeCap` — either on a
6990
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
6991
+ * or directly on the camera (Reolink registers at camera level).
6992
+ * Emits an `onPressed` event every time the firmware pushes a
6993
+ * ring; status tracks the last press and a pressCount since start
6994
+ * (diagnostic).
6995
+ *
6996
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
6997
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
6998
+ * device (contact / switch / event-emitter …) into a doorbell for
6999
+ * a camera. `defaultActive: false` — the operator explicitly binds
7000
+ * it per camera in the device-bindings UI, then picks the source
7001
+ * device + trigger in the per-device settings.
6910
7002
  *
6911
7003
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
6912
7004
  * — subscribers listening at the camera level receive ring events
@@ -6927,7 +7019,10 @@ var doorbellCapability = {
6927
7019
  scope: "device",
6928
7020
  deviceNative: true,
6929
7021
  mode: "singleton",
6930
- deviceTypes: [DeviceType.Button],
7022
+ kind: "wrapper",
7023
+ defaultActive: false,
7024
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
7025
+ exposesDeviceSettings: true,
6931
7026
  methods: {},
6932
7027
  events: {
6933
7028
  /**
@@ -13565,6 +13660,14 @@ var ConfigEntrySchema = z.object({
13565
13660
  value: z.unknown(),
13566
13661
  description: z.string().optional()
13567
13662
  });
13663
+ var DeviceLinkModeSchema = z.enum(["auto", "manual"]);
13664
+ /** One resolved linked device — the compact projection consumers need. */
13665
+ var LinkedDeviceSchema = z.object({
13666
+ deviceId: z.number(),
13667
+ name: z.string(),
13668
+ location: z.string().nullable(),
13669
+ features: z.array(z.string())
13670
+ });
13568
13671
  var SavedDeviceRowSchema = z.object({
13569
13672
  /** Numeric id reserved at allocateDeviceId time. */
13570
13673
  id: z.number(),
@@ -13672,6 +13775,7 @@ var deviceManagerCapability = {
13672
13775
  name: "device-manager",
13673
13776
  scope: "system",
13674
13777
  mode: "singleton",
13778
+ exposesDeviceSettings: true,
13675
13779
  methods: {
13676
13780
  /** Reserve (or re-resolve) a progressive numeric id for `(addonId, stableId)`.
13677
13781
  * Idempotent: returns the existing id if one is already persisted for the
@@ -13922,6 +14026,19 @@ var deviceManagerCapability = {
13922
14026
  getDevice: method(z.object({ deviceId: z.number() }), DeviceInfoSchema.nullable()),
13923
14027
  /** List children of a parent device (by parent numeric id). */
13924
14028
  getChildren: method(z.object({ parentDeviceId: z.number() }), z.array(DeviceInfoSchema)),
14029
+ /**
14030
+ * Resolve the devices LINKED to a camera — the single policy authority
14031
+ * both consumers call (viewer devices panel + pipeline-analytics event
14032
+ * kinds/ingest). Device-tree children are ALWAYS included; mode 'auto'
14033
+ * (default) adds every device sharing the camera's non-null `location`,
14034
+ * mode 'manual' adds the persisted manual list instead. Excludes the
14035
+ * camera itself; deduped. Configuration is standard per-device settings
14036
+ * (see the module docblock) — there are no bespoke link mutations.
14037
+ */
14038
+ getLinkedDevices: method(z.object({ deviceId: z.number() }), z.object({
14039
+ mode: DeviceLinkModeSchema,
14040
+ devices: z.array(LinkedDeviceSchema)
14041
+ })),
13925
14042
  /** Get stream sources for a camera device. */
13926
14043
  getStreamSources: method(z.object({ deviceId: z.number() }), z.array(StreamSourceEntrySchema$1)),
13927
14044
  /** Get config entries (key + value + description) for a device. */
@@ -15640,6 +15757,61 @@ var EventKindSchema = z.enum([
15640
15757
  "object",
15641
15758
  "audio"
15642
15759
  ]);
15760
+ /**
15761
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
15762
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
15763
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
15764
+ */
15765
+ var TrackZoneFilterSchema = z.discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
15766
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
15767
+ var EventKindIconSchema = z.enum([
15768
+ "motion",
15769
+ "audio",
15770
+ "person",
15771
+ "vehicle",
15772
+ "animal",
15773
+ "door",
15774
+ "pir",
15775
+ "smoke",
15776
+ "water",
15777
+ "button",
15778
+ "generic"
15779
+ ]);
15780
+ var EventKindCategorySchema = z.enum([
15781
+ "motion",
15782
+ "audio",
15783
+ "detection",
15784
+ "sensor",
15785
+ "custom"
15786
+ ]);
15787
+ var EventKindDescriptorSchema = z.object({
15788
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
15789
+ kind: z.string(),
15790
+ label: z.string(),
15791
+ /** Hex color for timeline/legend rendering. */
15792
+ color: z.string(),
15793
+ icon: EventKindIconSchema,
15794
+ category: EventKindCategorySchema,
15795
+ /** Which cap + device contributes this kind. For built-ins the camera
15796
+ * itself; for sensor kinds the LINKED source device. */
15797
+ source: z.object({
15798
+ capName: z.string(),
15799
+ deviceId: z.number()
15800
+ })
15801
+ });
15802
+ var SensorEventSchema = z.object({
15803
+ id: z.string(),
15804
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
15805
+ * yields N rows, one per camera). */
15806
+ deviceId: z.number(),
15807
+ /** The linked sensor device whose state changed. */
15808
+ sourceDeviceId: z.number(),
15809
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
15810
+ kind: z.string(),
15811
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
15812
+ value: z.record(z.string(), z.unknown()).nullable(),
15813
+ timestamp: z.number()
15814
+ });
15643
15815
  var TrackPositionSchema = z.object({
15644
15816
  x: z.number(),
15645
15817
  y: z.number(),
@@ -15653,6 +15825,30 @@ var TrackSnapshotSchema = z.object({
15653
15825
  mediaKey: z.string()
15654
15826
  });
15655
15827
  /**
15828
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
15829
+ * divided by the track's detection-frame dims), computed at persist time.
15830
+ * Absent when the frame dims were unknown when the track was persisted
15831
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
15832
+ */
15833
+ var TrackEnvelopeSchema = z.object({
15834
+ minX: z.number(),
15835
+ minY: z.number(),
15836
+ maxX: z.number(),
15837
+ maxY: z.number()
15838
+ });
15839
+ /**
15840
+ * Row projection for track list queries. `full` (default) returns the
15841
+ * complete Track including the frame-rate `positions[]` history and the
15842
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
15843
+ * keeps every scalar the list surfaces actually render (ids, class(es),
15844
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15845
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
15846
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15847
+ * `getTrack`. Mirrors the event-store `projection` convention
15848
+ * (`getObjectEvents` et al.).
15849
+ */
15850
+ var TrackProjectionSchema = z.enum(["full", "slim"]);
15851
+ /**
15656
15852
  * One audio-classification label heard on the track's camera while the
15657
15853
  * track was alive, aggregated per label. An "episode" is one persisted
15658
15854
  * audio event (the confident-classification path: score ≥ the device's
@@ -15703,7 +15899,11 @@ var TrackSchema = z.object({
15703
15899
  /** Audio-classification labels heard on the camera during the track's
15704
15900
  * life (score ≥ device `classificationMinScore`), aggregated per label.
15705
15901
  * Absent on legacy rows / tracks with no confident audio. */
15706
- audioLabels: z.array(TrackAudioLabelSchema).readonly().optional()
15902
+ audioLabels: z.array(TrackAudioLabelSchema).readonly().optional(),
15903
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
15904
+ * Populated from the persisted envelope columns on historical reads;
15905
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
15906
+ envelope: TrackEnvelopeSchema.optional()
15707
15907
  });
15708
15908
  var BaseEventFields = {
15709
15909
  id: z.string(),
@@ -15813,11 +16013,13 @@ var MediaFileSchema = z.object({
15813
16013
  sizeBytes: z.number(),
15814
16014
  timestamp: z.number()
15815
16015
  });
16016
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
16017
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
15816
16018
  var DeviceEventQueryInput = z.object({
15817
16019
  deviceId: z.number(),
15818
16020
  since: z.number().optional(),
15819
16021
  until: z.number().optional(),
15820
- limit: z.number().int().min(1).max(5e3).default(1e3),
16022
+ limit: z.number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
15821
16023
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
15822
16024
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
15823
16025
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -15825,6 +16027,27 @@ var DeviceEventQueryInput = z.object({
15825
16027
  projection: z.enum(["full", "slim"]).optional()
15826
16028
  });
15827
16029
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: z.string().optional() });
16030
+ var RecentTracksQueryInput = z.object({
16031
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
16032
+ deviceIds: z.array(z.number()),
16033
+ /** Window lower bound on `lastSeen` (inclusive). */
16034
+ since: z.number().optional(),
16035
+ /** Window upper bound on `lastSeen` (inclusive). */
16036
+ until: z.number().optional(),
16037
+ /** Page size. Default 200, max 1000. */
16038
+ limit: z.number().int().min(1).max(1e3).default(200),
16039
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
16040
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16041
+ cursor: z.string().optional(),
16042
+ /** See {@link TrackProjectionSchema}. Default `full`. */
16043
+ projection: TrackProjectionSchema.optional()
16044
+ });
16045
+ var RecentTracksPageSchema = z.object({
16046
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
16047
+ tracks: z.array(TrackSchema).readonly(),
16048
+ /** Cursor for the next page, or null when this page is the last. */
16049
+ nextCursor: z.string().nullable()
16050
+ });
15828
16051
  var KeyEventQueryInput = z.object({
15829
16052
  deviceId: z.number(),
15830
16053
  /** Window lower bound (track firstSeen ≥ since). */
@@ -15921,8 +16144,27 @@ var pipelineAnalyticsCapability = {
15921
16144
  deviceId: z.number(),
15922
16145
  since: z.number().optional(),
15923
16146
  until: z.number().optional(),
15924
- limit: z.number().optional()
16147
+ limit: z.number().optional(),
16148
+ /** Spatial filter — only tracks whose trajectory intersects the zone
16149
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
16150
+ * envelope columns, then precisely tested per position. Tracks with
16151
+ * an unknown envelope (no frame dims at persist time) always match. */
16152
+ zone: TrackZoneFilterSchema.optional(),
16153
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
16154
+ * compatible — omitting the field keeps today's exact behaviour). */
16155
+ projection: TrackProjectionSchema.optional()
15925
16156
  }), z.array(TrackSchema).readonly()),
16157
+ /**
16158
+ * Batched cluster-wide track listing — ONE call for the events page /
16159
+ * reel first paint instead of a per-camera `listTracks` fan-out. Merges
16160
+ * the persisted completed tracks of every requested device, sorted by
16161
+ * `lastSeen` DESC with a stable (lastSeen, trackId) cursor. Per-device
16162
+ * indexed pages (`idx_tracks_device_lastSeen`) are k-way merged
16163
+ * provider-side; `projection: 'slim'` drops the heavy `positions[]` /
16164
+ * `snapshots[]` JSON (returned as empty arrays). Active in-RAM tracks
16165
+ * are not included (same contract as `listTracks`).
16166
+ */
16167
+ listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
15926
16168
  clearTracks: method(z.object({ deviceId: z.number() }), z.void(), {
15927
16169
  kind: "mutation",
15928
16170
  auth: "admin"
@@ -15931,6 +16173,26 @@ var pipelineAnalyticsCapability = {
15931
16173
  getObjectEvents: method(ObjectEventQueryInput, z.array(ObjectEventSchema).readonly()),
15932
16174
  getAudioEvents: method(DeviceEventQueryInput, z.array(AudioEventSchema).readonly()),
15933
16175
  /**
16176
+ * Every event kind the device can produce: built-ins (motion + audio),
16177
+ * detection classes actually observed for the device (from the track
16178
+ * history), and sensor kinds contributed by LINKED devices (resolved via
16179
+ * `device-manager.getLinkedDevices`, mapped through `EVENT_KIND_BY_CAP`).
16180
+ */
16181
+ listEventKinds: method(z.object({ deviceId: z.number() }), z.array(EventKindDescriptorSchema).readonly()),
16182
+ /**
16183
+ * Sensor-event history for a camera: state changes of LINKED sensor
16184
+ * devices, attributed to the camera at ingest time (one row per linked
16185
+ * camera). Mirrors `getAudioEvents` query semantics; `kinds` narrows to
16186
+ * a kind subset.
16187
+ */
16188
+ getSensorEvents: method(z.object({
16189
+ deviceId: z.number(),
16190
+ since: z.number().optional(),
16191
+ until: z.number().optional(),
16192
+ kinds: z.array(z.string()).optional(),
16193
+ limit: z.number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
16194
+ }), z.array(SensorEventSchema).readonly()),
16195
+ /**
15934
16196
  * Importance-ranked highlights for a device+window. Queries completed
15935
16197
  * tracks by (deviceId, firstSeen ∈ [since,until]), scores each (or reuses
15936
16198
  * the persisted score), filters by minImportance/classFilter, orders by
@@ -20545,7 +20807,12 @@ var RecordingStorageUsageSchema = z.object({
20545
20807
  /**
20546
20808
  * Result of locating footage at a wall-clock instant for one device/profile.
20547
20809
  * `segment` carries the covering segment's window; `gap` reports the forward
20548
- * nearest covered edge (`null` past the end of footage / when none exists).
20810
+ * nearest covered edge (`null` past the end of footage / when none exists)
20811
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
20812
+ * footage behind the epoch; `null` when none — optional so older providers
20813
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
20814
+ * small inter-segment cracks (durMs under-covers the span to the next
20815
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
20549
20816
  */
20550
20817
  var LocateSegmentResultSchema = z.discriminatedUnion("kind", [z.object({
20551
20818
  kind: z.literal("segment"),
@@ -20554,7 +20821,8 @@ var LocateSegmentResultSchema = z.discriminatedUnion("kind", [z.object({
20554
20821
  bytes: z.number()
20555
20822
  }), z.object({
20556
20823
  kind: z.literal("gap"),
20557
- nearestEdgeMs: z.number().nullable()
20824
+ nearestEdgeMs: z.number().nullable(),
20825
+ prevEndMs: z.number().nullable().optional()
20558
20826
  })]);
20559
20827
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
20560
20828
  var ReadSegmentBytesResultSchema = z.object({ data: z.instanceof(Uint8Array) });
@@ -22717,6 +22985,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
22717
22985
  addonId: null,
22718
22986
  access: "view"
22719
22987
  },
22988
+ "deviceManager.getLinkedDevices": {
22989
+ capName: "device-manager",
22990
+ capScope: "system",
22991
+ addonId: null,
22992
+ access: "view"
22993
+ },
22720
22994
  "deviceManager.getRoleDisplayDefaults": {
22721
22995
  capName: "device-manager",
22722
22996
  capScope: "system",
@@ -24271,6 +24545,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
24271
24545
  addonId: null,
24272
24546
  access: "view"
24273
24547
  },
24548
+ "pipelineAnalytics.getSensorEvents": {
24549
+ capName: "pipeline-analytics",
24550
+ capScope: "device",
24551
+ addonId: null,
24552
+ access: "view"
24553
+ },
24274
24554
  "pipelineAnalytics.getTrack": {
24275
24555
  capName: "pipeline-analytics",
24276
24556
  capScope: "device",
@@ -24283,6 +24563,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
24283
24563
  addonId: null,
24284
24564
  access: "view"
24285
24565
  },
24566
+ "pipelineAnalytics.listEventKinds": {
24567
+ capName: "pipeline-analytics",
24568
+ capScope: "device",
24569
+ addonId: null,
24570
+ access: "view"
24571
+ },
24572
+ "pipelineAnalytics.listRecentTracks": {
24573
+ capName: "pipeline-analytics",
24574
+ capScope: "device",
24575
+ addonId: null,
24576
+ access: "view"
24577
+ },
24286
24578
  "pipelineAnalytics.listTracks": {
24287
24579
  capName: "pipeline-analytics",
24288
24580
  capScope: "device",
@@ -26326,4 +26618,4 @@ function scoreRuntimes(hw) {
26326
26618
  };
26327
26619
  }
26328
26620
  //#endregion
26329
- export { toExpressionValue as $, isCollectionArrayMethod as A, normalizeUnit as B, enumerateItemArrayFields as C, parseJsonUnknown as Ct, filesystemBrowseCapability as D, sleep as Dt, extractNestedAddonId as E, scopeKey as Et, localNetworkCapability as F, readNodePin as G, parseStreamParamsFormPatch as H, logDestinationCapability as I, settingsStoreCapability as J, scoreRuntimes as K, looseSchema as L, isVoidInput as M, kebabToCamel as N, getByPath as O, EventCategory as Ot, lifecycleJobSchema as P, streamQualityLabel as Q, metricsProviderCapability as R, deviceStatusCapability as S, parseJsonObject as St, evaluateLinkExpression as T, resolveCapMount as Tt, platformProbeCapability as U, objectInputDeclaresAddonId as V, procedureAuthKey as W, storageCapability as X, snapshotCapability as Y, storageProviderCapability as Z, authProviderCapability as _, emitDownForOwnedCaps as _t, DeviceStatusSchema as a, DEVICE_SETTINGS_CONTRIBUTION_METHODS as at, deviceManagerCapability as b, hydrateSchema as bt, STREAM_PROFILE_META as c, DeviceRole as ct, UserRecordSchema as d, ReadinessTimeoutError as dt, userManagementCapability as et, addonPagesCapability as f, WELL_KNOWN_TAB_MAP as ft, applyTransform as g, createEvent as gt, alertsCapability as h, asString as ht, CAP_NAMES_WITH_STATUS as i, DATAPLANE_SECRET_HEADER as it, isObjectInput as j, isArrayOutputSchema as k, ScopedTokenSchema as l, DeviceType as lt, addonWidgetsCapability as m, asNumber as mt, AlertSchema as n, errMsg as nt, METHOD_ACCESS_MAP as o, DEVICE_STATUS_METHOD as ot, addonSettingsCapability as p, asJsonObject as pt, setByPath as q, ApiKeyRecordSchema as r, BaseAddon as rt, RUNTIME_DEFAULTS as s, DeviceFeature as st, ALL_CAPABILITY_DEFINITIONS as t, validateExpressionSource as tt, StorageLocationTypeSchema as u, ReadinessRegistry as ut, backupCapability as v, emitReadiness as vt, enumerateSchemaFields as w, readinessKey as wt, deviceStateCapability as x, isDeviceConfigCap as xt, buildStreamParamsConfigSchema as y, expandCapMethods as yt, nodePin as z };
26621
+ export { streamQualityLabel as $, isArrayOutputSchema as A, nodePin as B, doorbellCapability as C, parseJsonObject as Ct, extractNestedAddonId as D, scopeKey as Dt, evaluateLinkExpression as E, resolveCapMount as Et, lifecycleJobSchema as F, procedureAuthKey as G, objectInputDeclaresAddonId as H, localNetworkCapability as I, setByPath as J, readNodePin as K, logDestinationCapability as L, isObjectInput as M, isVoidInput as N, filesystemBrowseCapability as O, sleep as Ot, kebabToCamel as P, storageProviderCapability as Q, looseSchema as R, deviceStatusCapability as S, isDeviceConfigCap as St, enumerateSchemaFields as T, readinessKey as Tt, parseStreamParamsFormPatch as U, normalizeUnit as V, platformProbeCapability as W, snapshotCapability as X, settingsStoreCapability as Y, storageCapability as Z, authProviderCapability as _, createEvent as _t, DeviceStatusSchema as a, DATAPLANE_SECRET_HEADER as at, deviceManagerCapability as b, expandCapMethods as bt, STREAM_PROFILE_META as c, DeviceFeature as ct, UserRecordSchema as d, ReadinessRegistry as dt, toExpressionValue as et, addonPagesCapability as f, ReadinessTimeoutError as ft, applyTransform as g, asString as gt, alertsCapability as h, asNumber as ht, CAP_NAMES_WITH_STATUS as i, BaseAddon as it, isCollectionArrayMethod as j, getByPath as k, EventCategory as kt, ScopedTokenSchema as l, DeviceRole as lt, addonWidgetsCapability as m, asJsonObject as mt, AlertSchema as n, validateExpressionSource as nt, METHOD_ACCESS_MAP as o, DEVICE_SETTINGS_CONTRIBUTION_METHODS as ot, addonSettingsCapability as p, WELL_KNOWN_TAB_MAP as pt, scoreRuntimes as q, ApiKeyRecordSchema as r, errMsg as rt, RUNTIME_DEFAULTS as s, DEVICE_STATUS_METHOD as st, ALL_CAPABILITY_DEFINITIONS as t, userManagementCapability as tt, StorageLocationTypeSchema as u, DeviceType as ut, backupCapability as v, emitDownForOwnedCaps as vt, enumerateItemArrayFields as w, parseJsonUnknown as wt, deviceStateCapability as x, hydrateSchema as xt, buildStreamParamsConfigSchema as y, emitReadiness as yt, metricsProviderCapability as z };
@@ -5,6 +5,14 @@ export interface FileDataPlaneOptions {
5
5
  readonly getRoots: () => readonly string[];
6
6
  /** Optional extension→content-type overrides merged over the media defaults. */
7
7
  readonly contentTypes?: Readonly<Record<string, string>>;
8
+ /**
9
+ * Optional per-path Cache-Control resolver. Return a header value to use for
10
+ * this rel path, or undefined for the default (`no-cache`). Lets a caller
11
+ * serve content-hashed bundle assets as `public, max-age=…, immutable`
12
+ * (re-downloading a 1MB SPA bundle on every open is a real cold-start cost)
13
+ * while entry HTML stays revalidated.
14
+ */
15
+ readonly cacheControl?: (rel: string) => string | undefined;
8
16
  }
9
17
  /** Build a `(req, res)` handler that serves `getRoots()` files with Range. */
10
18
  export declare function createFileDataPlaneHandler(opts: FileDataPlaneOptions): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-C3uHEBtP.js");
4
- const require_model_download_service = require("./model-download-service-D-Umz4-L.js");
5
- const require_manifest_python_deps = require("./manifest-python-deps-CVcJ9hDX.js");
3
+ const require_dist = require("./dist-BNro5CRu.js");
4
+ const require_model_download_service = require("./model-download-service-hf0ookyy.js");
5
+ const require_manifest_python_deps = require("./manifest-python-deps-DszE7bYn.js");
6
6
  const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
7
7
  const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
8
8
  const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
@@ -92047,16 +92047,32 @@ async function callRegisterNodeWithRetry(broker, params, opts) {
92047
92047
  }
92048
92048
  //#endregion
92049
92049
  //#region src/kernel/moleculer/readiness-service.ts
92050
+ /**
92051
+ * Service name AGENTS register their own readiness snapshot under.
92052
+ *
92053
+ * Deliberately NOT `$readiness`: agents call `$readiness.getSnapshot`
92054
+ * UNQUALIFIED to reach the hub's authoritative registry, and Moleculer
92055
+ * prefers a local service for an unqualified call — an agent registering
92056
+ * `$readiness` would shadow the hub and hydrate from itself. The hub
92057
+ * pulls `$agent-readiness.getSnapshot` with an explicit `{ nodeID }` on
92058
+ * every agent (re)connect + periodically, closing the reverse leg of the
92059
+ * D8 snapshot-reconcile (the hub otherwise learns agent readiness only
92060
+ * from lossy cross-node event broadcasts).
92061
+ */
92062
+ var AGENT_READINESS_SERVICE_NAME = "$agent-readiness";
92050
92063
  function createReadinessService(deps) {
92051
92064
  return {
92052
- name: "$readiness",
92065
+ name: deps.name ?? "$readiness",
92053
92066
  actions: { getSnapshot: { handler() {
92054
92067
  return deps.getSnapshot();
92055
92068
  } } }
92056
92069
  };
92057
92070
  }
92058
- function createReadinessServiceForRegistry(registry) {
92059
- return createReadinessService({ getSnapshot: () => registry.getSnapshotForTransport() });
92071
+ function createReadinessServiceForRegistry(registry, name) {
92072
+ return createReadinessService({
92073
+ getSnapshot: () => registry.getSnapshotForTransport(),
92074
+ ...name !== void 0 ? { name } : {}
92075
+ });
92060
92076
  }
92061
92077
  //#endregion
92062
92078
  //#region src/kernel/moleculer/stream-probe-service.ts
@@ -93391,6 +93407,7 @@ async function stageFrameworkLockstep(input) {
93391
93407
  //#endregion
93392
93408
  exports.AGENT_CAP_FWD_ACTION = require_manifest_python_deps.AGENT_CAP_FWD_ACTION;
93393
93409
  exports.AGENT_CAP_FWD_SERVICE = require_manifest_python_deps.AGENT_CAP_FWD_SERVICE;
93410
+ exports.AGENT_READINESS_SERVICE_NAME = AGENT_READINESS_SERVICE_NAME;
93394
93411
  exports.ALL_CAPABILITY_DEFINITIONS = require_dist.ALL_CAPABILITY_DEFINITIONS;
93395
93412
  exports.AddonApiFactory = AddonApiFactory;
93396
93413
  exports.AddonDepsManager = require_manifest_python_deps.AddonDepsManager;