@camstack/addon-pipeline 1.1.58 → 1.1.59
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/audio-analyzer/index.js +4 -4
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/detection-pipeline/index.js +9 -9
- package/dist/detection-pipeline/index.mjs +3 -3
- package/dist/{dist-Bn6Uq4vq.js → dist-B1u-QN05.js} +176 -12
- package/dist/{dist-CnwwDzX3.mjs → dist-DKeNH_5z.mjs} +176 -12
- package/dist/{model-download-service-C-IHWnXx-3Mmeob3l.mjs → model-download-service-Cp9f4dk6-0wh7OK3s.mjs} +2 -2
- package/dist/{model-download-service-C-IHWnXx-D326YNnt.js → model-download-service-Cp9f4dk6-Cp_GTKTw.js} +2 -2
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +123 -4
- package/dist/pipeline-runner/index.mjs +122 -3
- package/dist/recorder/index.js +993 -7
- package/dist/recorder/index.mjs +990 -5
- package/dist/session-decode/decode-worker-child.js +14 -1
- package/dist/session-decode/decode-worker-child.mjs +14 -1
- package/dist/{hub-hostname-cCknRYKj.mjs → sheet-geometry-BN80zpCa.js} +56 -1
- package/dist/{hub-hostname-DAJXlOgV.js → sheet-geometry-Bk3fR4s7.mjs} +39 -6
- package/dist/{step-definitions-D7NOrnzz.js → step-definitions-CPch37vh.js} +1 -1
- package/dist/{step-definitions-DhEe57Wn.mjs → step-definitions-DUB27V8Y.mjs} +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B8FQ1e0a.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DdkfKDgw.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D3h_W9uu.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DSXctoaw.mjs +26 -0
- package/dist/stream-broker/{hostInit-D1Vdyu-4.mjs → hostInit-BLn2Pv8Q.mjs} +3 -3
- package/dist/stream-broker/index.js +622 -149
- package/dist/stream-broker/index.mjs +622 -149
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CG5EE0W4.js → MaskShapeCanvas-DI4BY7W2-B0_nx15B.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-BFeLUNOl.js → MotionZonesSettings-NcxxQN8r-B8uM-VeO.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-pQQunq1F.js → PrivacyMaskSettings-APgPLF7p-DMOs4Hj9.js} +1 -1
- package/embed-dist/assets/index-W9-H5LTj.js +81 -0
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Ba7Eyd47.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-TWCUWhCm.mjs +0 -26
- package/embed-dist/assets/index-BxqQ931j.js +0 -81
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../types/dist/event-category-
|
|
1
|
+
//#region ../types/dist/event-category-H4AVePnn.mjs
|
|
2
2
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
3
3
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4
4
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -148,6 +148,9 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
148
148
|
EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
|
|
149
149
|
EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
|
|
150
150
|
EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
|
|
151
|
+
/** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
|
|
152
|
+
* thumb is a scrub gap the recorder's keyframe backfill covers. */
|
|
153
|
+
EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
|
|
151
154
|
EventCategory["DetectionEvent"] = "detection.event";
|
|
152
155
|
EventCategory["SessionTrackNew"] = "session.track.new";
|
|
153
156
|
EventCategory["SessionTrackExpired"] = "session.track.expired";
|
|
@@ -10738,7 +10741,7 @@ object({
|
|
|
10738
10741
|
deviceId: number(),
|
|
10739
10742
|
timestamp: number()
|
|
10740
10743
|
});
|
|
10741
|
-
DeviceType.Button;
|
|
10744
|
+
DeviceType.Button, DeviceType.Camera;
|
|
10742
10745
|
/**
|
|
10743
10746
|
* Enum-state sensor — a string value picked from a finite option set.
|
|
10744
10747
|
* Drives HA `sensor` entries with `state_class: enum` (HVAC action
|
|
@@ -15569,6 +15572,14 @@ var ConfigEntrySchema = object({
|
|
|
15569
15572
|
value: unknown(),
|
|
15570
15573
|
description: string().optional()
|
|
15571
15574
|
});
|
|
15575
|
+
var DeviceLinkModeSchema = _enum(["auto", "manual"]);
|
|
15576
|
+
/** One resolved linked device — the compact projection consumers need. */
|
|
15577
|
+
var LinkedDeviceSchema = object({
|
|
15578
|
+
deviceId: number(),
|
|
15579
|
+
name: string(),
|
|
15580
|
+
location: string().nullable(),
|
|
15581
|
+
features: array(string())
|
|
15582
|
+
});
|
|
15572
15583
|
var SavedDeviceRowSchema = object({
|
|
15573
15584
|
/** Numeric id reserved at allocateDeviceId time. */
|
|
15574
15585
|
id: number(),
|
|
@@ -15790,7 +15801,10 @@ method(object({
|
|
|
15790
15801
|
}), _void(), {
|
|
15791
15802
|
kind: "mutation",
|
|
15792
15803
|
auth: "admin"
|
|
15793
|
-
}), 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() }),
|
|
15804
|
+
}), 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({
|
|
15805
|
+
mode: DeviceLinkModeSchema,
|
|
15806
|
+
devices: array(LinkedDeviceSchema)
|
|
15807
|
+
})), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
|
|
15794
15808
|
deviceId: number(),
|
|
15795
15809
|
values: record(string(), unknown())
|
|
15796
15810
|
}), object({ success: literal(true) }), {
|
|
@@ -17059,6 +17073,61 @@ var EventKindSchema = _enum([
|
|
|
17059
17073
|
"object",
|
|
17060
17074
|
"audio"
|
|
17061
17075
|
]);
|
|
17076
|
+
/**
|
|
17077
|
+
* Spatial filter for `listTracks` — the rect + polygon variants of the shared
|
|
17078
|
+
* MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
|
|
17079
|
+
* of the camera frame (top-left origin), matching the drawing-plane editor.
|
|
17080
|
+
*/
|
|
17081
|
+
var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
|
|
17082
|
+
/** Closed icon vocabulary so clients render a known glyph per kind. */
|
|
17083
|
+
var EventKindIconSchema = _enum([
|
|
17084
|
+
"motion",
|
|
17085
|
+
"audio",
|
|
17086
|
+
"person",
|
|
17087
|
+
"vehicle",
|
|
17088
|
+
"animal",
|
|
17089
|
+
"door",
|
|
17090
|
+
"pir",
|
|
17091
|
+
"smoke",
|
|
17092
|
+
"water",
|
|
17093
|
+
"button",
|
|
17094
|
+
"generic"
|
|
17095
|
+
]);
|
|
17096
|
+
var EventKindCategorySchema = _enum([
|
|
17097
|
+
"motion",
|
|
17098
|
+
"audio",
|
|
17099
|
+
"detection",
|
|
17100
|
+
"sensor",
|
|
17101
|
+
"custom"
|
|
17102
|
+
]);
|
|
17103
|
+
var EventKindDescriptorSchema = object({
|
|
17104
|
+
/** Stable kind id (e.g. 'motion', 'person', 'contact'). */
|
|
17105
|
+
kind: string(),
|
|
17106
|
+
label: string(),
|
|
17107
|
+
/** Hex color for timeline/legend rendering. */
|
|
17108
|
+
color: string(),
|
|
17109
|
+
icon: EventKindIconSchema,
|
|
17110
|
+
category: EventKindCategorySchema,
|
|
17111
|
+
/** Which cap + device contributes this kind. For built-ins the camera
|
|
17112
|
+
* itself; for sensor kinds the LINKED source device. */
|
|
17113
|
+
source: object({
|
|
17114
|
+
capName: string(),
|
|
17115
|
+
deviceId: number()
|
|
17116
|
+
})
|
|
17117
|
+
});
|
|
17118
|
+
var SensorEventSchema = object({
|
|
17119
|
+
id: string(),
|
|
17120
|
+
/** The CAMERA the event is attributed to (a sensor linked to N cameras
|
|
17121
|
+
* yields N rows, one per camera). */
|
|
17122
|
+
deviceId: number(),
|
|
17123
|
+
/** The linked sensor device whose state changed. */
|
|
17124
|
+
sourceDeviceId: number(),
|
|
17125
|
+
/** Event kind id — matches an `EventKindDescriptor.kind`. */
|
|
17126
|
+
kind: string(),
|
|
17127
|
+
/** Snapshot of the sensor cap's runtime-state slice at the change. */
|
|
17128
|
+
value: record(string(), unknown()).nullable(),
|
|
17129
|
+
timestamp: number()
|
|
17130
|
+
});
|
|
17062
17131
|
var TrackPositionSchema = object({
|
|
17063
17132
|
x: number(),
|
|
17064
17133
|
y: number(),
|
|
@@ -17072,6 +17141,30 @@ var TrackSnapshotSchema = object({
|
|
|
17072
17141
|
mediaKey: string()
|
|
17073
17142
|
});
|
|
17074
17143
|
/**
|
|
17144
|
+
* Normalized 0..1 trajectory envelope (min/max over every position bbox,
|
|
17145
|
+
* divided by the track's detection-frame dims), computed at persist time.
|
|
17146
|
+
* Absent when the frame dims were unknown when the track was persisted
|
|
17147
|
+
* (legacy rows / dims-less sources) and on active (in-RAM) tracks.
|
|
17148
|
+
*/
|
|
17149
|
+
var TrackEnvelopeSchema = object({
|
|
17150
|
+
minX: number(),
|
|
17151
|
+
minY: number(),
|
|
17152
|
+
maxX: number(),
|
|
17153
|
+
maxY: number()
|
|
17154
|
+
});
|
|
17155
|
+
/**
|
|
17156
|
+
* Row projection for track list queries. `full` (default) returns the
|
|
17157
|
+
* complete Track including the frame-rate `positions[]` history and the
|
|
17158
|
+
* `snapshots[]` references — megabytes across a page of tracks. `slim`
|
|
17159
|
+
* keeps every scalar the list surfaces actually render (ids, class(es),
|
|
17160
|
+
* label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
|
|
17161
|
+
* zonesVisited, bestEventId, envelope) and returns `positions` /
|
|
17162
|
+
* `snapshots` as EMPTY arrays — detail views re-fetch the full row via
|
|
17163
|
+
* `getTrack`. Mirrors the event-store `projection` convention
|
|
17164
|
+
* (`getObjectEvents` et al.).
|
|
17165
|
+
*/
|
|
17166
|
+
var TrackProjectionSchema = _enum(["full", "slim"]);
|
|
17167
|
+
/**
|
|
17075
17168
|
* One audio-classification label heard on the track's camera while the
|
|
17076
17169
|
* track was alive, aggregated per label. An "episode" is one persisted
|
|
17077
17170
|
* audio event (the confident-classification path: score ≥ the device's
|
|
@@ -17122,7 +17215,11 @@ var TrackSchema = object({
|
|
|
17122
17215
|
/** Audio-classification labels heard on the camera during the track's
|
|
17123
17216
|
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
17124
17217
|
* Absent on legacy rows / tracks with no confident audio. */
|
|
17125
|
-
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
17218
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
|
|
17219
|
+
/** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
|
|
17220
|
+
* Populated from the persisted envelope columns on historical reads;
|
|
17221
|
+
* absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
|
|
17222
|
+
envelope: TrackEnvelopeSchema.optional()
|
|
17126
17223
|
});
|
|
17127
17224
|
var BaseEventFields = {
|
|
17128
17225
|
id: string(),
|
|
@@ -17232,11 +17329,13 @@ var MediaFileSchema = object({
|
|
|
17232
17329
|
sizeBytes: number(),
|
|
17233
17330
|
timestamp: number()
|
|
17234
17331
|
});
|
|
17332
|
+
var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
|
|
17333
|
+
var MAX_EVENT_QUERY_LIMIT = 5e3;
|
|
17235
17334
|
var DeviceEventQueryInput = object({
|
|
17236
17335
|
deviceId: number(),
|
|
17237
17336
|
since: number().optional(),
|
|
17238
17337
|
until: number().optional(),
|
|
17239
|
-
limit: number().int().min(1).max(
|
|
17338
|
+
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
|
|
17240
17339
|
/** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
|
|
17241
17340
|
* optional `mediaUrl` (populated by B5). `full` (default) keeps today's
|
|
17242
17341
|
* exact behaviour. Callers may omit this field — the store defaults to
|
|
@@ -17244,6 +17343,27 @@ var DeviceEventQueryInput = object({
|
|
|
17244
17343
|
projection: _enum(["full", "slim"]).optional()
|
|
17245
17344
|
});
|
|
17246
17345
|
var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
|
|
17346
|
+
var RecentTracksQueryInput = object({
|
|
17347
|
+
/** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
|
|
17348
|
+
deviceIds: array(number()),
|
|
17349
|
+
/** Window lower bound on `lastSeen` (inclusive). */
|
|
17350
|
+
since: number().optional(),
|
|
17351
|
+
/** Window upper bound on `lastSeen` (inclusive). */
|
|
17352
|
+
until: number().optional(),
|
|
17353
|
+
/** Page size. Default 200, max 1000. */
|
|
17354
|
+
limit: number().int().min(1).max(1e3).default(200),
|
|
17355
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`.
|
|
17356
|
+
* Encodes the (lastSeen, trackId) sort position — treat as opaque. */
|
|
17357
|
+
cursor: string().optional(),
|
|
17358
|
+
/** See {@link TrackProjectionSchema}. Default `full`. */
|
|
17359
|
+
projection: TrackProjectionSchema.optional()
|
|
17360
|
+
});
|
|
17361
|
+
var RecentTracksPageSchema = object({
|
|
17362
|
+
/** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
|
|
17363
|
+
tracks: array(TrackSchema).readonly(),
|
|
17364
|
+
/** Cursor for the next page, or null when this page is the last. */
|
|
17365
|
+
nextCursor: string().nullable()
|
|
17366
|
+
});
|
|
17247
17367
|
var KeyEventQueryInput = object({
|
|
17248
17368
|
deviceId: number(),
|
|
17249
17369
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -17326,11 +17446,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
17326
17446
|
deviceId: number(),
|
|
17327
17447
|
since: number().optional(),
|
|
17328
17448
|
until: number().optional(),
|
|
17329
|
-
limit: number().optional()
|
|
17330
|
-
|
|
17331
|
-
|
|
17332
|
-
|
|
17333
|
-
|
|
17449
|
+
limit: number().optional(),
|
|
17450
|
+
/** Spatial filter — only tracks whose trajectory intersects the zone
|
|
17451
|
+
* (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
|
|
17452
|
+
* envelope columns, then precisely tested per position. Tracks with
|
|
17453
|
+
* an unknown envelope (no frame dims at persist time) always match. */
|
|
17454
|
+
zone: TrackZoneFilterSchema.optional(),
|
|
17455
|
+
/** See {@link TrackProjectionSchema}. Default `full` (backward
|
|
17456
|
+
* compatible — omitting the field keeps today's exact behaviour). */
|
|
17457
|
+
projection: TrackProjectionSchema.optional()
|
|
17458
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
|
|
17459
|
+
kind: "mutation",
|
|
17460
|
+
auth: "admin"
|
|
17461
|
+
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
|
|
17462
|
+
deviceId: number(),
|
|
17463
|
+
since: number().optional(),
|
|
17464
|
+
until: number().optional(),
|
|
17465
|
+
kinds: array(string()).optional(),
|
|
17466
|
+
limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
|
|
17467
|
+
}), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
|
|
17334
17468
|
deviceId: number(),
|
|
17335
17469
|
since: number(),
|
|
17336
17470
|
until: number(),
|
|
@@ -20626,7 +20760,12 @@ var RecordingStorageUsageSchema = object({
|
|
|
20626
20760
|
/**
|
|
20627
20761
|
* Result of locating footage at a wall-clock instant for one device/profile.
|
|
20628
20762
|
* `segment` carries the covering segment's window; `gap` reports the forward
|
|
20629
|
-
* nearest covered edge (`null` past the end of footage / when none exists)
|
|
20763
|
+
* nearest covered edge (`null` past the end of footage / when none exists)
|
|
20764
|
+
* and the backward covered edge `prevEndMs` (exclusive end of the nearest
|
|
20765
|
+
* footage behind the epoch; `null` when none — optional so older providers
|
|
20766
|
+
* that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
|
|
20767
|
+
* small inter-segment cracks (durMs under-covers the span to the next
|
|
20768
|
+
* startMs by ~11-17 ms) instead of no-opping at a segment head.
|
|
20630
20769
|
*/
|
|
20631
20770
|
var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
|
|
20632
20771
|
kind: literal("segment"),
|
|
@@ -20635,7 +20774,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
|
|
|
20635
20774
|
bytes: number()
|
|
20636
20775
|
}), object({
|
|
20637
20776
|
kind: literal("gap"),
|
|
20638
|
-
nearestEdgeMs: number().nullable()
|
|
20777
|
+
nearestEdgeMs: number().nullable(),
|
|
20778
|
+
prevEndMs: number().nullable().optional()
|
|
20639
20779
|
})]);
|
|
20640
20780
|
/** Raw bytes of one finalized footage segment (read off disk on the recording node). */
|
|
20641
20781
|
var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
|
|
@@ -22237,6 +22377,12 @@ Object.freeze({
|
|
|
22237
22377
|
addonId: null,
|
|
22238
22378
|
access: "view"
|
|
22239
22379
|
},
|
|
22380
|
+
"deviceManager.getLinkedDevices": {
|
|
22381
|
+
capName: "device-manager",
|
|
22382
|
+
capScope: "system",
|
|
22383
|
+
addonId: null,
|
|
22384
|
+
access: "view"
|
|
22385
|
+
},
|
|
22240
22386
|
"deviceManager.getRoleDisplayDefaults": {
|
|
22241
22387
|
capName: "device-manager",
|
|
22242
22388
|
capScope: "system",
|
|
@@ -23791,6 +23937,12 @@ Object.freeze({
|
|
|
23791
23937
|
addonId: null,
|
|
23792
23938
|
access: "view"
|
|
23793
23939
|
},
|
|
23940
|
+
"pipelineAnalytics.getSensorEvents": {
|
|
23941
|
+
capName: "pipeline-analytics",
|
|
23942
|
+
capScope: "device",
|
|
23943
|
+
addonId: null,
|
|
23944
|
+
access: "view"
|
|
23945
|
+
},
|
|
23794
23946
|
"pipelineAnalytics.getTrack": {
|
|
23795
23947
|
capName: "pipeline-analytics",
|
|
23796
23948
|
capScope: "device",
|
|
@@ -23803,6 +23955,18 @@ Object.freeze({
|
|
|
23803
23955
|
addonId: null,
|
|
23804
23956
|
access: "view"
|
|
23805
23957
|
},
|
|
23958
|
+
"pipelineAnalytics.listEventKinds": {
|
|
23959
|
+
capName: "pipeline-analytics",
|
|
23960
|
+
capScope: "device",
|
|
23961
|
+
addonId: null,
|
|
23962
|
+
access: "view"
|
|
23963
|
+
},
|
|
23964
|
+
"pipelineAnalytics.listRecentTracks": {
|
|
23965
|
+
capName: "pipeline-analytics",
|
|
23966
|
+
capScope: "device",
|
|
23967
|
+
addonId: null,
|
|
23968
|
+
access: "view"
|
|
23969
|
+
},
|
|
23806
23970
|
"pipelineAnalytics.listTracks": {
|
|
23807
23971
|
capName: "pipeline-analytics",
|
|
23808
23972
|
capScope: "device",
|
|
@@ -3,7 +3,7 @@ import { createReadStream, promises } from "node:fs";
|
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import "node:crypto";
|
|
6
|
-
//#region ../system/dist/model-download-service-
|
|
6
|
+
//#region ../system/dist/model-download-service-Cp9f4dk6.mjs
|
|
7
7
|
/**
|
|
8
8
|
* Map a rel path to one candidate absolute path PER root, keeping only roots the
|
|
9
9
|
* path stays within (traversal guard). The handler serves the first candidate
|
|
@@ -121,7 +121,7 @@ function createFileDataPlaneHandler(opts) {
|
|
|
121
121
|
const baseHeaders = {
|
|
122
122
|
"content-type": contentTypeFor(absPath, opts.contentTypes),
|
|
123
123
|
"accept-ranges": "bytes",
|
|
124
|
-
"cache-control": "no-cache"
|
|
124
|
+
"cache-control": opts.cacheControl?.(rel) ?? "no-cache"
|
|
125
125
|
};
|
|
126
126
|
const range = parseRangeHeader(req.headers.range, size);
|
|
127
127
|
if (range) {
|
|
@@ -4,7 +4,7 @@ node_fs = require_chunk.__toESM(node_fs, 1);
|
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
6
6
|
require("node:crypto");
|
|
7
|
-
//#region ../system/dist/model-download-service-
|
|
7
|
+
//#region ../system/dist/model-download-service-Cp9f4dk6.mjs
|
|
8
8
|
/**
|
|
9
9
|
* Map a rel path to one candidate absolute path PER root, keeping only roots the
|
|
10
10
|
* path stays within (traversal guard). The handler serves the first candidate
|
|
@@ -122,7 +122,7 @@ function createFileDataPlaneHandler(opts) {
|
|
|
122
122
|
const baseHeaders = {
|
|
123
123
|
"content-type": contentTypeFor(absPath, opts.contentTypes),
|
|
124
124
|
"accept-ranges": "bytes",
|
|
125
|
-
"cache-control": "no-cache"
|
|
125
|
+
"cache-control": opts.cacheControl?.(rel) ?? "no-cache"
|
|
126
126
|
};
|
|
127
127
|
const range = parseRangeHeader(req.headers.range, size);
|
|
128
128
|
if (range) {
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-B1u-QN05.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as DeviceType, E as motionDetectionCapability, H as hydrateSchema, L as BaseAddon, x as evaluateZoneRules } from "../dist-
|
|
1
|
+
import { B as DeviceType, E as motionDetectionCapability, H as hydrateSchema, L as BaseAddon, x as evaluateZoneRules } from "../dist-DKeNH_5z.mjs";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-B1u-QN05.js");
|
|
7
7
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
8
|
-
const
|
|
8
|
+
const require_sheet_geometry = require("../sheet-geometry-BN80zpCa.js");
|
|
9
9
|
const require_worker_protocol = require("../worker-protocol-PP4jKHHJ.js");
|
|
10
|
-
const require_step_definitions = require("../step-definitions-
|
|
10
|
+
const require_step_definitions = require("../step-definitions-CPch37vh.js");
|
|
11
11
|
let node_child_process = require("node:child_process");
|
|
12
12
|
let node_url = require("node:url");
|
|
13
13
|
let sharp = require("sharp");
|
|
@@ -1279,6 +1279,40 @@ function cropRgb(frame, bbox, maxWidth) {
|
|
|
1279
1279
|
height: outH
|
|
1280
1280
|
};
|
|
1281
1281
|
}
|
|
1282
|
+
var ThumbSamplerPolicy = class {
|
|
1283
|
+
intervalMs;
|
|
1284
|
+
minGapMs;
|
|
1285
|
+
now;
|
|
1286
|
+
/** Last accepted-sample wall clock per device. */
|
|
1287
|
+
lastAcceptedAt = /* @__PURE__ */ new Map();
|
|
1288
|
+
constructor(options) {
|
|
1289
|
+
this.intervalMs = Math.max(0, options.intervalMs);
|
|
1290
|
+
this.minGapMs = Math.max(0, options.minGapMs ?? 1e3);
|
|
1291
|
+
this.now = options.now ?? (() => Date.now());
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Decide whether to sample `deviceId` now. Returns true (and records the
|
|
1295
|
+
* acceptance) when the larger of `intervalMs` / `minGapMs` has elapsed since
|
|
1296
|
+
* the last accepted sample for this device; false otherwise. The FIRST call
|
|
1297
|
+
* for a device always samples (cold start).
|
|
1298
|
+
*/
|
|
1299
|
+
shouldSample(deviceId) {
|
|
1300
|
+
const now = this.now();
|
|
1301
|
+
const gate = Math.max(this.intervalMs, this.minGapMs);
|
|
1302
|
+
const last = this.lastAcceptedAt.get(deviceId);
|
|
1303
|
+
if (last !== void 0 && now - last < gate) return false;
|
|
1304
|
+
this.lastAcceptedAt.set(deviceId, now);
|
|
1305
|
+
return true;
|
|
1306
|
+
}
|
|
1307
|
+
/** Forget a device (detach / phase exit) so its next frame cold-starts. */
|
|
1308
|
+
forget(deviceId) {
|
|
1309
|
+
this.lastAcceptedAt.delete(deviceId);
|
|
1310
|
+
}
|
|
1311
|
+
/** Drop all per-device state (shutdown). */
|
|
1312
|
+
clear() {
|
|
1313
|
+
this.lastAcceptedAt.clear();
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1282
1316
|
//#endregion
|
|
1283
1317
|
//#region src/session-decode/session-decode-coordinator.ts
|
|
1284
1318
|
/** How long to wait after `kill()` before escalating to `kill('SIGKILL')`. */
|
|
@@ -2029,6 +2063,8 @@ async function runDetailSubtree(deps, input) {
|
|
|
2029
2063
|
//#region src/pipeline-runner/index.ts
|
|
2030
2064
|
var DEFAULT_CONFIG = {
|
|
2031
2065
|
maxQueueDepth: 30,
|
|
2066
|
+
thumbnailsEnabled: true,
|
|
2067
|
+
thumbnailSampleMs: 5e3,
|
|
2032
2068
|
maxConcurrentInferences: 16,
|
|
2033
2069
|
targetLoadPercent: 80,
|
|
2034
2070
|
minThrottledFps: 1,
|
|
@@ -2244,7 +2280,7 @@ function resolveSessionDecodeOwnerNodeId(config) {
|
|
|
2244
2280
|
function resolveOwnerRestreamHost(frameSource, hubUrl) {
|
|
2245
2281
|
const reachable = frameSource.ownerReachableHost?.trim();
|
|
2246
2282
|
if (reachable !== void 0 && reachable.length > 0) return reachable;
|
|
2247
|
-
return
|
|
2283
|
+
return require_sheet_geometry.resolveHubHostname(frameSource.hubHostnameOverride, hubUrl);
|
|
2248
2284
|
}
|
|
2249
2285
|
/**
|
|
2250
2286
|
* Acquire the broker's COMPRESSED passthrough restream for one camStream and
|
|
@@ -2350,6 +2386,12 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2350
2386
|
* Initialised in {@link onInitialize} once `nodeId` is known.
|
|
2351
2387
|
*/
|
|
2352
2388
|
retainedFrames = null;
|
|
2389
|
+
/**
|
|
2390
|
+
* Scrub-thumbnail sampler policy (2026-07-16). Gates the per-camera JPEG
|
|
2391
|
+
* encode to ~1 / `thumbnailSampleMs` (with a hard 1 Hz ceiling) so the cost
|
|
2392
|
+
* stays a couple ms of sharp every few seconds. Null until `onInitialize`.
|
|
2393
|
+
*/
|
|
2394
|
+
thumbSampler = null;
|
|
2353
2395
|
attached = /* @__PURE__ */ new Map();
|
|
2354
2396
|
nodeId = "unknown";
|
|
2355
2397
|
metricsSnapshotTimer = null;
|
|
@@ -2470,6 +2512,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2470
2512
|
nodeId: this.nodeId,
|
|
2471
2513
|
logger: this.ctx.logger.child("retained-frames")
|
|
2472
2514
|
});
|
|
2515
|
+
this.thumbSampler = new ThumbSamplerPolicy({ intervalMs: this.config.thumbnailSampleMs });
|
|
2473
2516
|
this.runner = new PipelineRunner({
|
|
2474
2517
|
maxQueueDepth: this.config.maxQueueDepth,
|
|
2475
2518
|
maxConcurrentInferences: this.config.maxConcurrentInferences,
|
|
@@ -2559,6 +2602,8 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2559
2602
|
this.nativeCropRegistry.clear();
|
|
2560
2603
|
this.retainedFrames?.clear();
|
|
2561
2604
|
this.retainedFrames = null;
|
|
2605
|
+
this.thumbSampler?.clear();
|
|
2606
|
+
this.thumbSampler = null;
|
|
2562
2607
|
}
|
|
2563
2608
|
async cacheBenchFrame(input) {
|
|
2564
2609
|
const sharp$2 = (await import("sharp")).default;
|
|
@@ -3021,6 +3066,59 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3021
3066
|
frameSpace: denormalizeRect(paddedBbox, handle.width, handle.height)
|
|
3022
3067
|
};
|
|
3023
3068
|
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Scrub-thumbnail sampler (2026-07-16). Rides the already-decoded rgb
|
|
3071
|
+
* detection frame: at most once per `thumbnailSampleMs` per camera (policy
|
|
3072
|
+
* enforces a 1 Hz hard ceiling), downscale to a ~320px JPEG and emit it as
|
|
3073
|
+
* `recording.thumb-sampled` telemetry. The recorder (single designated node)
|
|
3074
|
+
* packs 5-minute sprite sheets from these. Fire-and-forget — a failed encode
|
|
3075
|
+
* is logged at debug and never disturbs the decode/detect path. Zero extra
|
|
3076
|
+
* decode (frame exists); one sharp encode per cadence per camera (D9: a
|
|
3077
|
+
* compressed payload at 1/5 Hz, never raw pixels at frame-rate).
|
|
3078
|
+
*/
|
|
3079
|
+
maybeSampleThumbnail(deviceId, frame) {
|
|
3080
|
+
if (!this.config.thumbnailsEnabled) return;
|
|
3081
|
+
const sampler = this.thumbSampler;
|
|
3082
|
+
if (!sampler || frame.format !== "rgb") return;
|
|
3083
|
+
if (!sampler.shouldSample(deviceId)) return;
|
|
3084
|
+
const capturedAt = typeof frame.capturedAt === "number" && frame.capturedAt > 0 ? frame.capturedAt : Date.now();
|
|
3085
|
+
const width = frame.width;
|
|
3086
|
+
const height = frame.height;
|
|
3087
|
+
const data = frame.data;
|
|
3088
|
+
(async () => {
|
|
3089
|
+
try {
|
|
3090
|
+
const targetW = Math.min(320, width);
|
|
3091
|
+
const image = (0, sharp.default)(data, { raw: {
|
|
3092
|
+
width,
|
|
3093
|
+
height,
|
|
3094
|
+
channels: 3
|
|
3095
|
+
} });
|
|
3096
|
+
if (targetW < width) image.resize(targetW);
|
|
3097
|
+
const jpeg = await image.jpeg({ quality: 60 }).toBuffer();
|
|
3098
|
+
const meta = {
|
|
3099
|
+
width: targetW,
|
|
3100
|
+
height: Math.round(targetW * height / width)
|
|
3101
|
+
};
|
|
3102
|
+
this.ctxIfReady?.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.RecordingThumbSampled, {
|
|
3103
|
+
type: "pipeline",
|
|
3104
|
+
id: deviceId,
|
|
3105
|
+
nodeId: this.nodeId,
|
|
3106
|
+
deviceId
|
|
3107
|
+
}, {
|
|
3108
|
+
deviceId,
|
|
3109
|
+
capturedAt,
|
|
3110
|
+
width: meta.width,
|
|
3111
|
+
height: meta.height,
|
|
3112
|
+
jpeg: new Uint8Array(jpeg)
|
|
3113
|
+
}));
|
|
3114
|
+
} catch (err) {
|
|
3115
|
+
this.ctx?.logger.debug("thumbnail sample encode failed", {
|
|
3116
|
+
tags: { deviceId },
|
|
3117
|
+
meta: { error: err instanceof Error ? err.message : String(err) }
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
})();
|
|
3121
|
+
}
|
|
3024
3122
|
/** Real `runPipeline` dep: node-local `pipelineExecutor.runPipeline` on the `'full'` plane. */
|
|
3025
3123
|
async runDetailPipeline(steps, imageJpeg, deviceId) {
|
|
3026
3124
|
const api = this.ctxIfReady?.api;
|
|
@@ -3098,6 +3196,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3098
3196
|
attachment.detectionUnsubscribe?.();
|
|
3099
3197
|
this.lastMotionFrameAt.delete(deviceId);
|
|
3100
3198
|
this.lastDecodeUnexpectedEndAt.delete(deviceId);
|
|
3199
|
+
this.thumbSampler?.forget(deviceId);
|
|
3101
3200
|
this.attached.delete(deviceId);
|
|
3102
3201
|
this.lastMotionAt.delete(deviceId);
|
|
3103
3202
|
this.lastEmittedCameraMetrics.delete(deviceId);
|
|
@@ -3339,6 +3438,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3339
3438
|
const handle = retainedFrames?.write(config.deviceId, frame) ?? void 0;
|
|
3340
3439
|
if (handle && nativeCrop) this.nativeCropRegistry.register(handle, nativeCrop);
|
|
3341
3440
|
this.enqueueSharedDetectionFrame(config.deviceId, frame, handle);
|
|
3441
|
+
this.maybeSampleThumbnail(config.deviceId, frame);
|
|
3342
3442
|
}
|
|
3343
3443
|
});
|
|
3344
3444
|
}
|
|
@@ -3993,12 +4093,31 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3993
4093
|
step: 1,
|
|
3994
4094
|
default: DEFAULT_CONFIG.maxConcurrentDecodeSpawns,
|
|
3995
4095
|
showValue: true
|
|
4096
|
+
},
|
|
4097
|
+
{
|
|
4098
|
+
type: "boolean",
|
|
4099
|
+
key: "thumbnailsEnabled",
|
|
4100
|
+
label: "Scrub thumbnails",
|
|
4101
|
+
description: "Sample the already-decoded detection frame into small JPEG scrub thumbnails (zero extra decode). The recorder packs them into 5-minute sprite sheets the viewer timeline scrubs. Disable to stop emitting thumbnail telemetry from this node.",
|
|
4102
|
+
default: DEFAULT_CONFIG.thumbnailsEnabled
|
|
4103
|
+
},
|
|
4104
|
+
{
|
|
4105
|
+
type: "slider",
|
|
4106
|
+
key: "thumbnailSampleMs",
|
|
4107
|
+
label: "Thumbnail cadence (ms)",
|
|
4108
|
+
description: "How often (ms) each camera contributes a scrub thumbnail. Lower = denser scrub preview + more storage; a hard 1 Hz ceiling always applies. Default 5000.",
|
|
4109
|
+
min: 1e3,
|
|
4110
|
+
max: 3e4,
|
|
4111
|
+
step: 1e3,
|
|
4112
|
+
default: DEFAULT_CONFIG.thumbnailSampleMs,
|
|
4113
|
+
showValue: true
|
|
3996
4114
|
}
|
|
3997
4115
|
]
|
|
3998
4116
|
}] });
|
|
3999
4117
|
}
|
|
4000
4118
|
async onConfigChanged() {
|
|
4001
4119
|
this.runner?.updateLimits(this.config);
|
|
4120
|
+
this.thumbSampler = new ThumbSamplerPolicy({ intervalMs: this.config.thumbnailSampleMs });
|
|
4002
4121
|
this.spawnGate?.resize(Math.max(1, this.config.maxConcurrentDecodeSpawns));
|
|
4003
4122
|
this.ctx.logger.info("pipeline-runner tuning updated", { meta: {
|
|
4004
4123
|
maxQueueDepth: this.config.maxQueueDepth,
|