@camstack/addon-pipeline 1.1.27 → 1.1.28
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 +7 -6
- package/dist/audio-analyzer/index.mjs +7 -6
- package/dist/audio-codec-ffmpeg/index.js +1 -1
- package/dist/audio-codec-ffmpeg/index.mjs +1 -1
- package/dist/decoder-ffmpeg/index.js +43 -11
- package/dist/decoder-ffmpeg/index.mjs +43 -11
- package/dist/decoder-nodeav/index.js +13 -8
- package/dist/decoder-nodeav/index.mjs +13 -8
- package/dist/detection-pipeline/index.js +157 -219
- package/dist/detection-pipeline/index.mjs +157 -219
- package/dist/{dist-DAIlCdAx.js → dist-Biq62zt4.js} +372 -35
- package/dist/{dist-CgEP_0OL.mjs → dist-C6_wgXqF.mjs} +373 -24
- package/dist/{frame-handle-plane-Dq20KtKL.mjs → frame-handle-plane-BIoY6nRV.mjs} +13 -5
- package/dist/{frame-handle-plane-DtTRX_0n.js → frame-handle-plane-D6BzyEgy.js} +13 -5
- package/dist/{frame-ring-sink-BbzrqTWj.js → frame-ring-sink-9J0wCdLF.js} +128 -23
- package/dist/{frame-ring-sink-B_NvPTJZ.mjs → frame-ring-sink-Cs9vby6v.mjs} +117 -24
- package/dist/motion-wasm/index.js +55 -3
- package/dist/motion-wasm/index.mjs +55 -3
- package/dist/node-topology-platform-BkR_k6WT.mjs +15 -0
- package/dist/node-topology-platform-CFZ7F4xW.js +20 -0
- package/dist/pipeline-runner/index.js +50 -16
- package/dist/pipeline-runner/index.mjs +50 -16
- package/dist/recorder/index.js +36 -40
- package/dist/recorder/index.mjs +36 -40
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-5tQlh9h4.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DoReAb4y.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DhY3MZ2C.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-BJK0-svt.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DtoYGpTp.mjs} +1 -1
- package/dist/stream-broker/{hostInit-DyLqyJaS.mjs → hostInit-C0SuwQhL.mjs} +3 -3
- package/dist/stream-broker/index.js +61 -25
- package/dist/stream-broker/index.mjs +61 -25
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-BDLNwJ_F.js → MaskShapeCanvas-DI4BY7W2-Br2yi8ah.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CoLjNiUN.js → MotionZonesSettings-NcxxQN8r-BSCcPdPf.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DJE3OU-q.js → PrivacyMaskSettings-APgPLF7p-ztxvxSjS.js} +1 -1
- package/embed-dist/assets/index-C5UpuPr8.css +2 -0
- package/embed-dist/assets/{index-C-pL8ETk.js → index-CeZV1B-2.js} +10 -10
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/python/inference_pool.py +93 -102
- package/python/test_inference_pool_device_selection.py +43 -38
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CkOPfV8r.mjs +0 -26
- package/embed-dist/assets/index-DrJ0ee3f.css +0 -2
|
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_frame_handle_plane = require("../frame-handle-plane-
|
|
5
|
+
const require_dist = require("../dist-Biq62zt4.js");
|
|
6
|
+
const require_frame_handle_plane = require("../frame-handle-plane-D6BzyEgy.js");
|
|
7
7
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
8
8
|
let _camstack_shm_ring = require("@camstack/shm-ring");
|
|
9
9
|
//#region src/pipeline-runner/bench-actions.ts
|
|
@@ -950,6 +950,34 @@ function ownDetectionEntry(entry) {
|
|
|
950
950
|
handle: entry.handle
|
|
951
951
|
};
|
|
952
952
|
}
|
|
953
|
+
/**
|
|
954
|
+
* Clone a {@link MotionQueueEntry} for the queue's copy-on-retain boundary —
|
|
955
|
+
* same contract as {@link ownDetectionEntry}: detach the borrowed shm pixels,
|
|
956
|
+
* carry the plain-record `FrameHandle` through unchanged.
|
|
957
|
+
*/
|
|
958
|
+
function ownMotionEntry(entry) {
|
|
959
|
+
return {
|
|
960
|
+
frame: ownFrame(entry.frame),
|
|
961
|
+
handle: entry.handle
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* CB5 shm FrameHandle passthrough gate. The runner hands the executor / motion
|
|
966
|
+
* provider a zero-pixel shm `FrameHandle` (instead of re-serialising ~1.2MB of
|
|
967
|
+
* pixels over UDS) ONLY while the pipeline is keeping up. The FrameRing is a
|
|
968
|
+
* latest-wins seqlock with NO refcount, so deferring the pixel read from the
|
|
969
|
+
* poll tick to the executor (after queue + semaphore + UDS) widens the
|
|
970
|
+
* publish→read window — under backpressure the producer can recycle the slot
|
|
971
|
+
* before the executor reads it (a silent drop). When the pipeline is backed up
|
|
972
|
+
* we therefore fall back to copying pixels inline (today's behaviour).
|
|
973
|
+
*
|
|
974
|
+
* The per-camera detection `FrameQueue` is latest-only (capacity 1), so raw
|
|
975
|
+
* queue length is 0/1 and the operative backpressure signal is inference
|
|
976
|
+
* saturation on the shared semaphore — no spare inference permit ⇒ frames are
|
|
977
|
+
* waiting ⇒ ship pixels. The queue-depth clause is kept for defence in depth
|
|
978
|
+
* and future multi-slot queues.
|
|
979
|
+
*/
|
|
980
|
+
var HANDLE_PASSTHROUGH_MAX_QUEUE_DEPTH = 1;
|
|
953
981
|
var DEFAULT_MOTION_COOLDOWN_MS = 3e4;
|
|
954
982
|
function toFrameInput$1(frame) {
|
|
955
983
|
return {
|
|
@@ -1032,7 +1060,7 @@ var PipelineRunner = class {
|
|
|
1032
1060
|
this.detectionStreamHandler = handler;
|
|
1033
1061
|
}
|
|
1034
1062
|
registerCamera(deviceId, registration) {
|
|
1035
|
-
const motionQueue = new FrameQueue(this.config.maxQueueDepth,
|
|
1063
|
+
const motionQueue = new FrameQueue(this.config.maxQueueDepth, ownMotionEntry);
|
|
1036
1064
|
const detectionQueue = new FrameQueue(this.config.maxQueueDepth, ownDetectionEntry);
|
|
1037
1065
|
const initialPhase = registration.detectionMode === "disabled" ? "idle" : registration.detectionMode === "always-on" ? "active" : "watching";
|
|
1038
1066
|
const state = {
|
|
@@ -1087,10 +1115,13 @@ var PipelineRunner = class {
|
|
|
1087
1115
|
if (this.defaultRoundRobinIndex >= this.defaultRoundRobinKeys.length) this.defaultRoundRobinIndex = 0;
|
|
1088
1116
|
}
|
|
1089
1117
|
}
|
|
1090
|
-
enqueueMotionFrame(deviceId, frame) {
|
|
1118
|
+
enqueueMotionFrame(deviceId, frame, handle) {
|
|
1091
1119
|
const state = this.cameras.get(deviceId);
|
|
1092
1120
|
if (!state) return;
|
|
1093
|
-
state.motionQueue.enqueue(
|
|
1121
|
+
state.motionQueue.enqueue({
|
|
1122
|
+
frame,
|
|
1123
|
+
handle
|
|
1124
|
+
});
|
|
1094
1125
|
}
|
|
1095
1126
|
enqueueDetectionFrame(deviceId, frame, handle) {
|
|
1096
1127
|
const state = this.cameras.get(deviceId);
|
|
@@ -1274,8 +1305,8 @@ var PipelineRunner = class {
|
|
|
1274
1305
|
}
|
|
1275
1306
|
drainMotionQueues() {
|
|
1276
1307
|
for (const [deviceId, state] of this.cameras) while (state.motionQueue.size > 0) {
|
|
1277
|
-
const
|
|
1278
|
-
if (
|
|
1308
|
+
const entry = state.motionQueue.dequeue();
|
|
1309
|
+
if (entry) this.config.analyzeMotion(deviceId, entry.frame, entry.handle);
|
|
1279
1310
|
}
|
|
1280
1311
|
}
|
|
1281
1312
|
async processWithSemaphore(deviceId, entry, frameInput, state, streamType) {
|
|
@@ -1284,8 +1315,9 @@ var PipelineRunner = class {
|
|
|
1284
1315
|
const enqueuedAt = frame._enqueuedAt ?? pickedAt;
|
|
1285
1316
|
const release = await this.semaphore.acquire();
|
|
1286
1317
|
const semAcquiredAt = Date.now();
|
|
1318
|
+
const executorHandle = state.detectionQueue.size <= HANDLE_PASSTHROUGH_MAX_QUEUE_DEPTH && this.semaphore.available > 0 ? handle : void 0;
|
|
1287
1319
|
try {
|
|
1288
|
-
const result = await this.config.processFrame(deviceId, frameInput);
|
|
1320
|
+
const result = await this.config.processFrame(deviceId, frameInput, executorHandle);
|
|
1289
1321
|
const inferDoneAt = Date.now();
|
|
1290
1322
|
const inferenceMs = inferDoneAt - semAcquiredAt;
|
|
1291
1323
|
state.inferenceTimes.push(inferenceMs);
|
|
@@ -1463,8 +1495,8 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
1463
1495
|
maxConcurrentInferences: this.config.maxConcurrentInferences,
|
|
1464
1496
|
targetLoadPercent: this.config.targetLoadPercent,
|
|
1465
1497
|
minThrottledFps: this.config.minThrottledFps,
|
|
1466
|
-
processFrame: (deviceId, frame) => this.runInference(deviceId, frame),
|
|
1467
|
-
analyzeMotion: (deviceId, frame) => this.runMotionAnalysis(deviceId, frame),
|
|
1498
|
+
processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
|
|
1499
|
+
analyzeMotion: (deviceId, frame, handle) => this.runMotionAnalysis(deviceId, frame, handle),
|
|
1468
1500
|
onPhaseChanged: (deviceId, phase, meta) => this.handlePhaseChanged(deviceId, phase, meta),
|
|
1469
1501
|
logger: this.ctx.logger,
|
|
1470
1502
|
onOccupancyRecheck: (deviceId, frames) => {
|
|
@@ -2129,8 +2161,8 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2129
2161
|
ownNodeId: this.nodeId,
|
|
2130
2162
|
getRemoteFrame: (handle) => this.getRemoteFrame(handle),
|
|
2131
2163
|
...remote ? { source: remote.makeSource(config.motionStreamId) } : {},
|
|
2132
|
-
onFrame: (frame,
|
|
2133
|
-
runner.enqueueMotionFrame(config.deviceId, frame);
|
|
2164
|
+
onFrame: (frame, handle) => {
|
|
2165
|
+
runner.enqueueMotionFrame(config.deviceId, frame, handle);
|
|
2134
2166
|
}
|
|
2135
2167
|
});
|
|
2136
2168
|
}
|
|
@@ -2221,7 +2253,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2221
2253
|
}
|
|
2222
2254
|
});
|
|
2223
2255
|
}
|
|
2224
|
-
async runInference(deviceId, frame) {
|
|
2256
|
+
async runInference(deviceId, frame, handle) {
|
|
2225
2257
|
if (!this.ctx) return null;
|
|
2226
2258
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
2227
2259
|
const api = this.ctx.api;
|
|
@@ -2237,10 +2269,11 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2237
2269
|
return null;
|
|
2238
2270
|
}
|
|
2239
2271
|
if (steps.length === 0) return null;
|
|
2272
|
+
const useHandle = handle !== void 0 && handle.nodeId === this.nodeId;
|
|
2240
2273
|
try {
|
|
2241
2274
|
return await api.pipelineExecutor.runPipeline.mutate({
|
|
2242
2275
|
steps: [...steps],
|
|
2243
|
-
frame,
|
|
2276
|
+
...useHandle ? { frameHandle: handle } : { frame },
|
|
2244
2277
|
deviceId,
|
|
2245
2278
|
...engine ? { engine } : {}
|
|
2246
2279
|
});
|
|
@@ -2250,7 +2283,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2250
2283
|
return null;
|
|
2251
2284
|
}
|
|
2252
2285
|
}
|
|
2253
|
-
async runMotionAnalysis(deviceId, frame) {
|
|
2286
|
+
async runMotionAnalysis(deviceId, frame, handle) {
|
|
2254
2287
|
const ctx = this.ctx;
|
|
2255
2288
|
const runner = this.runner;
|
|
2256
2289
|
if (!ctx || !runner) return;
|
|
@@ -2262,9 +2295,10 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
2262
2295
|
log.warn("runMotionAnalysis: this.ctx.api not available");
|
|
2263
2296
|
return;
|
|
2264
2297
|
}
|
|
2298
|
+
const useHandle = handle !== void 0 && handle.nodeId === this.nodeId;
|
|
2265
2299
|
const result = await api.motionDetection.analyze.mutate({
|
|
2266
2300
|
deviceId,
|
|
2267
|
-
frame: toFrameInput(frame)
|
|
2301
|
+
...useHandle ? { frameHandle: handle } : { frame: toFrameInput(frame) }
|
|
2268
2302
|
});
|
|
2269
2303
|
if (!result) return;
|
|
2270
2304
|
const detected = result.regions.length > 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as errMsg, C as defineCustomActions, G as EventCategory,
|
|
2
|
-
import { t as FrameHandlePlane } from "../frame-handle-plane-
|
|
1
|
+
import { B as errMsg, C as defineCustomActions, G as EventCategory, K as createEvent, N as pipelineRunnerCapability, V as BaseAddon, Y as makeSourceBrokerId, b as customAction, et as _enum, it as lazy, j as nodePin, lt as string, nt as boolean, ot as number, st as object, tt as array } from "../dist-C6_wgXqF.mjs";
|
|
2
|
+
import { t as FrameHandlePlane } from "../frame-handle-plane-BIoY6nRV.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
4
|
import { FrameRingReaderCache } from "@camstack/shm-ring";
|
|
5
5
|
//#region src/pipeline-runner/bench-actions.ts
|
|
@@ -946,6 +946,34 @@ function ownDetectionEntry(entry) {
|
|
|
946
946
|
handle: entry.handle
|
|
947
947
|
};
|
|
948
948
|
}
|
|
949
|
+
/**
|
|
950
|
+
* Clone a {@link MotionQueueEntry} for the queue's copy-on-retain boundary —
|
|
951
|
+
* same contract as {@link ownDetectionEntry}: detach the borrowed shm pixels,
|
|
952
|
+
* carry the plain-record `FrameHandle` through unchanged.
|
|
953
|
+
*/
|
|
954
|
+
function ownMotionEntry(entry) {
|
|
955
|
+
return {
|
|
956
|
+
frame: ownFrame(entry.frame),
|
|
957
|
+
handle: entry.handle
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* CB5 shm FrameHandle passthrough gate. The runner hands the executor / motion
|
|
962
|
+
* provider a zero-pixel shm `FrameHandle` (instead of re-serialising ~1.2MB of
|
|
963
|
+
* pixels over UDS) ONLY while the pipeline is keeping up. The FrameRing is a
|
|
964
|
+
* latest-wins seqlock with NO refcount, so deferring the pixel read from the
|
|
965
|
+
* poll tick to the executor (after queue + semaphore + UDS) widens the
|
|
966
|
+
* publish→read window — under backpressure the producer can recycle the slot
|
|
967
|
+
* before the executor reads it (a silent drop). When the pipeline is backed up
|
|
968
|
+
* we therefore fall back to copying pixels inline (today's behaviour).
|
|
969
|
+
*
|
|
970
|
+
* The per-camera detection `FrameQueue` is latest-only (capacity 1), so raw
|
|
971
|
+
* queue length is 0/1 and the operative backpressure signal is inference
|
|
972
|
+
* saturation on the shared semaphore — no spare inference permit ⇒ frames are
|
|
973
|
+
* waiting ⇒ ship pixels. The queue-depth clause is kept for defence in depth
|
|
974
|
+
* and future multi-slot queues.
|
|
975
|
+
*/
|
|
976
|
+
var HANDLE_PASSTHROUGH_MAX_QUEUE_DEPTH = 1;
|
|
949
977
|
var DEFAULT_MOTION_COOLDOWN_MS = 3e4;
|
|
950
978
|
function toFrameInput$1(frame) {
|
|
951
979
|
return {
|
|
@@ -1028,7 +1056,7 @@ var PipelineRunner = class {
|
|
|
1028
1056
|
this.detectionStreamHandler = handler;
|
|
1029
1057
|
}
|
|
1030
1058
|
registerCamera(deviceId, registration) {
|
|
1031
|
-
const motionQueue = new FrameQueue(this.config.maxQueueDepth,
|
|
1059
|
+
const motionQueue = new FrameQueue(this.config.maxQueueDepth, ownMotionEntry);
|
|
1032
1060
|
const detectionQueue = new FrameQueue(this.config.maxQueueDepth, ownDetectionEntry);
|
|
1033
1061
|
const initialPhase = registration.detectionMode === "disabled" ? "idle" : registration.detectionMode === "always-on" ? "active" : "watching";
|
|
1034
1062
|
const state = {
|
|
@@ -1083,10 +1111,13 @@ var PipelineRunner = class {
|
|
|
1083
1111
|
if (this.defaultRoundRobinIndex >= this.defaultRoundRobinKeys.length) this.defaultRoundRobinIndex = 0;
|
|
1084
1112
|
}
|
|
1085
1113
|
}
|
|
1086
|
-
enqueueMotionFrame(deviceId, frame) {
|
|
1114
|
+
enqueueMotionFrame(deviceId, frame, handle) {
|
|
1087
1115
|
const state = this.cameras.get(deviceId);
|
|
1088
1116
|
if (!state) return;
|
|
1089
|
-
state.motionQueue.enqueue(
|
|
1117
|
+
state.motionQueue.enqueue({
|
|
1118
|
+
frame,
|
|
1119
|
+
handle
|
|
1120
|
+
});
|
|
1090
1121
|
}
|
|
1091
1122
|
enqueueDetectionFrame(deviceId, frame, handle) {
|
|
1092
1123
|
const state = this.cameras.get(deviceId);
|
|
@@ -1270,8 +1301,8 @@ var PipelineRunner = class {
|
|
|
1270
1301
|
}
|
|
1271
1302
|
drainMotionQueues() {
|
|
1272
1303
|
for (const [deviceId, state] of this.cameras) while (state.motionQueue.size > 0) {
|
|
1273
|
-
const
|
|
1274
|
-
if (
|
|
1304
|
+
const entry = state.motionQueue.dequeue();
|
|
1305
|
+
if (entry) this.config.analyzeMotion(deviceId, entry.frame, entry.handle);
|
|
1275
1306
|
}
|
|
1276
1307
|
}
|
|
1277
1308
|
async processWithSemaphore(deviceId, entry, frameInput, state, streamType) {
|
|
@@ -1280,8 +1311,9 @@ var PipelineRunner = class {
|
|
|
1280
1311
|
const enqueuedAt = frame._enqueuedAt ?? pickedAt;
|
|
1281
1312
|
const release = await this.semaphore.acquire();
|
|
1282
1313
|
const semAcquiredAt = Date.now();
|
|
1314
|
+
const executorHandle = state.detectionQueue.size <= HANDLE_PASSTHROUGH_MAX_QUEUE_DEPTH && this.semaphore.available > 0 ? handle : void 0;
|
|
1283
1315
|
try {
|
|
1284
|
-
const result = await this.config.processFrame(deviceId, frameInput);
|
|
1316
|
+
const result = await this.config.processFrame(deviceId, frameInput, executorHandle);
|
|
1285
1317
|
const inferDoneAt = Date.now();
|
|
1286
1318
|
const inferenceMs = inferDoneAt - semAcquiredAt;
|
|
1287
1319
|
state.inferenceTimes.push(inferenceMs);
|
|
@@ -1459,8 +1491,8 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
1459
1491
|
maxConcurrentInferences: this.config.maxConcurrentInferences,
|
|
1460
1492
|
targetLoadPercent: this.config.targetLoadPercent,
|
|
1461
1493
|
minThrottledFps: this.config.minThrottledFps,
|
|
1462
|
-
processFrame: (deviceId, frame) => this.runInference(deviceId, frame),
|
|
1463
|
-
analyzeMotion: (deviceId, frame) => this.runMotionAnalysis(deviceId, frame),
|
|
1494
|
+
processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
|
|
1495
|
+
analyzeMotion: (deviceId, frame, handle) => this.runMotionAnalysis(deviceId, frame, handle),
|
|
1464
1496
|
onPhaseChanged: (deviceId, phase, meta) => this.handlePhaseChanged(deviceId, phase, meta),
|
|
1465
1497
|
logger: this.ctx.logger,
|
|
1466
1498
|
onOccupancyRecheck: (deviceId, frames) => {
|
|
@@ -2125,8 +2157,8 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
2125
2157
|
ownNodeId: this.nodeId,
|
|
2126
2158
|
getRemoteFrame: (handle) => this.getRemoteFrame(handle),
|
|
2127
2159
|
...remote ? { source: remote.makeSource(config.motionStreamId) } : {},
|
|
2128
|
-
onFrame: (frame,
|
|
2129
|
-
runner.enqueueMotionFrame(config.deviceId, frame);
|
|
2160
|
+
onFrame: (frame, handle) => {
|
|
2161
|
+
runner.enqueueMotionFrame(config.deviceId, frame, handle);
|
|
2130
2162
|
}
|
|
2131
2163
|
});
|
|
2132
2164
|
}
|
|
@@ -2217,7 +2249,7 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
2217
2249
|
}
|
|
2218
2250
|
});
|
|
2219
2251
|
}
|
|
2220
|
-
async runInference(deviceId, frame) {
|
|
2252
|
+
async runInference(deviceId, frame, handle) {
|
|
2221
2253
|
if (!this.ctx) return null;
|
|
2222
2254
|
const log = this.ctx.logger.withTags({ deviceId });
|
|
2223
2255
|
const api = this.ctx.api;
|
|
@@ -2233,10 +2265,11 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
2233
2265
|
return null;
|
|
2234
2266
|
}
|
|
2235
2267
|
if (steps.length === 0) return null;
|
|
2268
|
+
const useHandle = handle !== void 0 && handle.nodeId === this.nodeId;
|
|
2236
2269
|
try {
|
|
2237
2270
|
return await api.pipelineExecutor.runPipeline.mutate({
|
|
2238
2271
|
steps: [...steps],
|
|
2239
|
-
frame,
|
|
2272
|
+
...useHandle ? { frameHandle: handle } : { frame },
|
|
2240
2273
|
deviceId,
|
|
2241
2274
|
...engine ? { engine } : {}
|
|
2242
2275
|
});
|
|
@@ -2246,7 +2279,7 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
2246
2279
|
return null;
|
|
2247
2280
|
}
|
|
2248
2281
|
}
|
|
2249
|
-
async runMotionAnalysis(deviceId, frame) {
|
|
2282
|
+
async runMotionAnalysis(deviceId, frame, handle) {
|
|
2250
2283
|
const ctx = this.ctx;
|
|
2251
2284
|
const runner = this.runner;
|
|
2252
2285
|
if (!ctx || !runner) return;
|
|
@@ -2258,9 +2291,10 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
2258
2291
|
log.warn("runMotionAnalysis: this.ctx.api not available");
|
|
2259
2292
|
return;
|
|
2260
2293
|
}
|
|
2294
|
+
const useHandle = handle !== void 0 && handle.nodeId === this.nodeId;
|
|
2261
2295
|
const result = await api.motionDetection.analyze.mutate({
|
|
2262
2296
|
deviceId,
|
|
2263
|
-
frame: toFrameInput(frame)
|
|
2297
|
+
...useHandle ? { frameHandle: handle } : { frame: toFrameInput(frame) }
|
|
2264
2298
|
});
|
|
2265
2299
|
if (!result) return;
|
|
2266
2300
|
const detected = result.regions.length > 0;
|
package/dist/recorder/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-DxM2DSns.js");
|
|
2
|
-
const require_dist = require("../dist-
|
|
2
|
+
const require_dist = require("../dist-Biq62zt4.js");
|
|
3
3
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
let node_path = require("node:path");
|
|
@@ -726,9 +726,15 @@ function buildRecordingDeviceSchema() {
|
|
|
726
726
|
* (stringified) numeric deviceId — enumerable for boot hydrate (so the addon
|
|
727
727
|
* knows which devices have persisted footage to index without scanning every
|
|
728
728
|
* device store). The whole Zod-validated blob round-trips on every read/write
|
|
729
|
-
* via a {@link
|
|
730
|
-
*
|
|
731
|
-
*
|
|
729
|
+
* via a {@link DurableState} handle, so no per-device field can be silently
|
|
730
|
+
* dropped on persist. A corrupt blob reads back as EMPTY rather than crashing
|
|
731
|
+
* boot.
|
|
732
|
+
*
|
|
733
|
+
* P3b settings-API migration: the functions here consume the durable handle
|
|
734
|
+
* directly — in production the recorder addon builds it via the sanctioned
|
|
735
|
+
* `BaseAddon.state()` (retry budget + validated fallback + parse-warning
|
|
736
|
+
* logging); {@link recordingConfigsState} adapts a raw store slice for test
|
|
737
|
+
* fakes. Same store key, same persisted blob shape as before.
|
|
732
738
|
*
|
|
733
739
|
* On read the legacy `mode`/`schedules`/`triggers` fields are derived once into
|
|
734
740
|
* the authoritative `bands` via `migrateConfigToBands`, then persisted, so the
|
|
@@ -743,23 +749,14 @@ var DEFAULT_DEVICE_CONFIG = {
|
|
|
743
749
|
enabled: false,
|
|
744
750
|
bands: []
|
|
745
751
|
};
|
|
746
|
-
function blobState(store) {
|
|
747
|
-
return require_dist.createDurableState({
|
|
748
|
-
key: RECORDING_CONFIGS_KEY,
|
|
749
|
-
schema: RecordingConfigsBlobSchema,
|
|
750
|
-
fallback: {},
|
|
751
|
-
read: () => store.readAddonStore(),
|
|
752
|
-
write: (patch) => store.writeAddonStore(patch)
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
752
|
function serialize(map) {
|
|
756
753
|
const out = {};
|
|
757
754
|
for (const [id, config] of map) out[String(id)] = config;
|
|
758
755
|
return out;
|
|
759
756
|
}
|
|
760
757
|
/** Read every persisted device config, keyed by numeric deviceId. */
|
|
761
|
-
async function readDeviceConfigs(
|
|
762
|
-
const blob = await
|
|
758
|
+
async function readDeviceConfigs(state) {
|
|
759
|
+
const blob = await state.get();
|
|
763
760
|
const map = /* @__PURE__ */ new Map();
|
|
764
761
|
for (const [key, config] of Object.entries(blob)) {
|
|
765
762
|
const id = Number(key);
|
|
@@ -773,15 +770,15 @@ async function readDeviceConfigs(store) {
|
|
|
773
770
|
* re-persisted so the stored shape converges on the authoritative model. An
|
|
774
771
|
* unconfigured device returns {@link DEFAULT_DEVICE_CONFIG}.
|
|
775
772
|
*/
|
|
776
|
-
async function loadDeviceConfig(
|
|
777
|
-
const stored = (await readDeviceConfigs(
|
|
773
|
+
async function loadDeviceConfig(state, deviceId) {
|
|
774
|
+
const stored = (await readDeviceConfigs(state)).get(deviceId);
|
|
778
775
|
if (!stored) return DEFAULT_DEVICE_CONFIG;
|
|
779
776
|
if (stored.bands) return stored;
|
|
780
777
|
const migrated = {
|
|
781
778
|
...stored,
|
|
782
779
|
bands: require_dist.migrateConfigToBands(stored)
|
|
783
780
|
};
|
|
784
|
-
await saveDeviceConfig(
|
|
781
|
+
await saveDeviceConfig(state, deviceId, migrated);
|
|
785
782
|
return migrated;
|
|
786
783
|
}
|
|
787
784
|
/**
|
|
@@ -789,11 +786,11 @@ async function loadDeviceConfig(store, deviceId) {
|
|
|
789
786
|
* `RecordingConfigSchema` at the boundary (untrusted cap input). Returns the
|
|
790
787
|
* validated config that was persisted.
|
|
791
788
|
*/
|
|
792
|
-
async function saveDeviceConfig(
|
|
789
|
+
async function saveDeviceConfig(state, deviceId, config) {
|
|
793
790
|
const validated = require_dist.RecordingConfigSchema.parse(config);
|
|
794
|
-
const map = await readDeviceConfigs(
|
|
791
|
+
const map = await readDeviceConfigs(state);
|
|
795
792
|
map.set(deviceId, validated);
|
|
796
|
-
await
|
|
793
|
+
await state.set(serialize(map));
|
|
797
794
|
return validated;
|
|
798
795
|
}
|
|
799
796
|
/**
|
|
@@ -2052,9 +2049,16 @@ var resolveRecordingHubHostname = require_hub_hostname.resolveHubHostname;
|
|
|
2052
2049
|
* provider until the controller swaps the manifest entry in a later step — this
|
|
2053
2050
|
* file ships dormant (separate tree, not wired into the manifest yet).
|
|
2054
2051
|
*/
|
|
2052
|
+
/** Default designated recording node (`recordingNodeId` global setting). The
|
|
2053
|
+
* stream-broker is hub-only and recording is a single-node global function, so
|
|
2054
|
+
* the hub is the safe default — mirrors pipeline-analytics' post-processing
|
|
2055
|
+
* node gate. */
|
|
2056
|
+
var RECORDING_NODE_DEFAULT = "hub";
|
|
2055
2057
|
var DEFAULT_CONFIG = {
|
|
2056
2058
|
segmentSeconds: 10,
|
|
2057
|
-
watchIntervalMs: 2e3
|
|
2059
|
+
watchIntervalMs: 2e3,
|
|
2060
|
+
recordingNodeId: RECORDING_NODE_DEFAULT,
|
|
2061
|
+
recordingHubHostname: ""
|
|
2058
2062
|
};
|
|
2059
2063
|
/** Playback-marker window padding around a motion event (EventMap only; the
|
|
2060
2064
|
* controller's events-band gating uses the raw trigger ms, not this pad).
|
|
@@ -2069,11 +2073,6 @@ var MARKER_PAD_POST_MS = require_dist.EVENT_PAD_MS.postMs;
|
|
|
2069
2073
|
* form the hub reverse-proxy client path `/addon/recorder/playback`. */
|
|
2070
2074
|
var RECORDER_ADDON_ID = "recorder";
|
|
2071
2075
|
var PLAYBACK_PREFIX = "playback";
|
|
2072
|
-
/** Default designated recording node (`recordingNodeId` global setting). The
|
|
2073
|
-
* stream-broker is hub-only and recording is a single-node global function, so
|
|
2074
|
-
* the hub is the safe default — mirrors pipeline-analytics' post-processing
|
|
2075
|
-
* node gate. */
|
|
2076
|
-
var RECORDING_NODE_DEFAULT = "hub";
|
|
2077
2076
|
/** Node the hub-only `stream-broker` runs on. Every recorder — on the hub or on
|
|
2078
2077
|
* a designated agent — gates on, and pulls from, THIS node's broker. */
|
|
2079
2078
|
var BROKER_HOST_NODE_ID = "hub";
|
|
@@ -2101,9 +2100,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
2101
2100
|
async onInitialize() {
|
|
2102
2101
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
2103
2102
|
this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
|
|
2104
|
-
const
|
|
2105
|
-
const
|
|
2106
|
-
const designated = typeof rawNode === "string" && rawNode.length > 0 ? rawNode : RECORDING_NODE_DEFAULT;
|
|
2103
|
+
const configuredNode = this.config.recordingNodeId;
|
|
2104
|
+
const designated = configuredNode.length > 0 ? configuredNode : RECORDING_NODE_DEFAULT;
|
|
2107
2105
|
this.isRecordingNode = this.nodeId === designated;
|
|
2108
2106
|
if (!this.isRecordingNode) {
|
|
2109
2107
|
this.ctx.logger.info("recorder INERT on this node — recording assigned elsewhere", { meta: {
|
|
@@ -2112,8 +2110,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
2112
2110
|
} });
|
|
2113
2111
|
return { providers: [] };
|
|
2114
2112
|
}
|
|
2115
|
-
const
|
|
2116
|
-
const consumerHostname = this.nodeId === BROKER_HOST_NODE_ID ? void 0 : resolveRecordingHubHostname(typeof rawHubHostname === "string" ? rawHubHostname : void 0, process.env["CAMSTACK_HUB_URL"]);
|
|
2113
|
+
const consumerHostname = this.nodeId === BROKER_HOST_NODE_ID ? void 0 : resolveRecordingHubHostname(this.config.recordingHubHostname, process.env["CAMSTACK_HUB_URL"]);
|
|
2117
2114
|
if (this.nodeId !== BROKER_HOST_NODE_ID) {
|
|
2118
2115
|
this.ctx.logger.info("recorder is a REMOTE recording node — pulling broker streams cross-node", { meta: {
|
|
2119
2116
|
ownNodeId: this.nodeId,
|
|
@@ -2318,7 +2315,9 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
2318
2315
|
* Global settings surfaced in the admin UI (and persisted to the addon
|
|
2319
2316
|
* store). The cluster Recording tab drives `recordingNodeId` via a node
|
|
2320
2317
|
* picker; `recordingHubHostname` is an advanced override for cross-node
|
|
2321
|
-
* pulls. Both
|
|
2318
|
+
* pulls. Both are SHARED cluster keys (not perNode) hydrated into
|
|
2319
|
+
* `this.config` by `resolveConfig()` and take effect on addon restart
|
|
2320
|
+
* (read once in `onInitialize`).
|
|
2322
2321
|
*/
|
|
2323
2322
|
globalSettingsSchema() {
|
|
2324
2323
|
return this.schema({ sections: [{
|
|
@@ -2341,14 +2340,11 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
2341
2340
|
}]
|
|
2342
2341
|
}] });
|
|
2343
2342
|
}
|
|
2344
|
-
/**
|
|
2343
|
+
/** Durable handle config-store reads/writes the deviceId→config blob on —
|
|
2344
|
+
* the sanctioned `BaseAddon.state()` wrapper (same key, same blob shape;
|
|
2345
|
+
* adds the read-retry budget + parse-warning logging over the raw slice). */
|
|
2345
2346
|
configStore() {
|
|
2346
|
-
|
|
2347
|
-
if (!settings) throw new Error("RecorderV2Addon: ctx.settings unavailable — cannot persist recording config");
|
|
2348
|
-
return {
|
|
2349
|
-
readAddonStore: () => settings.readAddonStore(),
|
|
2350
|
-
writeAddonStore: (patch) => settings.writeAddonStore(patch)
|
|
2351
|
-
};
|
|
2347
|
+
return this.state(RECORDING_CONFIGS_KEY, RecordingConfigsBlobSchema, {});
|
|
2352
2348
|
}
|
|
2353
2349
|
};
|
|
2354
2350
|
//#endregion
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as errMsg, G as EventCategory, I as storageEvictableCapability, P as recordingCapability,
|
|
1
|
+
import { B as errMsg, G as EventCategory, I as storageEvictableCapability, P as recordingCapability, Q as selectAssignedProfileSlots, V as BaseAddon, W as DeviceType, c as EVENT_PAD_MS, ct as record, f as RecordingConfigSchema, k as migrateConfigToBands, lt as string, q as hydrateSchema } from "../dist-C6_wgXqF.mjs";
|
|
2
2
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
3
3
|
import { t as createFileDataPlaneHandler } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -725,9 +725,15 @@ function buildRecordingDeviceSchema() {
|
|
|
725
725
|
* (stringified) numeric deviceId — enumerable for boot hydrate (so the addon
|
|
726
726
|
* knows which devices have persisted footage to index without scanning every
|
|
727
727
|
* device store). The whole Zod-validated blob round-trips on every read/write
|
|
728
|
-
* via a {@link
|
|
729
|
-
*
|
|
730
|
-
*
|
|
728
|
+
* via a {@link DurableState} handle, so no per-device field can be silently
|
|
729
|
+
* dropped on persist. A corrupt blob reads back as EMPTY rather than crashing
|
|
730
|
+
* boot.
|
|
731
|
+
*
|
|
732
|
+
* P3b settings-API migration: the functions here consume the durable handle
|
|
733
|
+
* directly — in production the recorder addon builds it via the sanctioned
|
|
734
|
+
* `BaseAddon.state()` (retry budget + validated fallback + parse-warning
|
|
735
|
+
* logging); {@link recordingConfigsState} adapts a raw store slice for test
|
|
736
|
+
* fakes. Same store key, same persisted blob shape as before.
|
|
731
737
|
*
|
|
732
738
|
* On read the legacy `mode`/`schedules`/`triggers` fields are derived once into
|
|
733
739
|
* the authoritative `bands` via `migrateConfigToBands`, then persisted, so the
|
|
@@ -742,23 +748,14 @@ var DEFAULT_DEVICE_CONFIG = {
|
|
|
742
748
|
enabled: false,
|
|
743
749
|
bands: []
|
|
744
750
|
};
|
|
745
|
-
function blobState(store) {
|
|
746
|
-
return createDurableState({
|
|
747
|
-
key: RECORDING_CONFIGS_KEY,
|
|
748
|
-
schema: RecordingConfigsBlobSchema,
|
|
749
|
-
fallback: {},
|
|
750
|
-
read: () => store.readAddonStore(),
|
|
751
|
-
write: (patch) => store.writeAddonStore(patch)
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
751
|
function serialize(map) {
|
|
755
752
|
const out = {};
|
|
756
753
|
for (const [id, config] of map) out[String(id)] = config;
|
|
757
754
|
return out;
|
|
758
755
|
}
|
|
759
756
|
/** Read every persisted device config, keyed by numeric deviceId. */
|
|
760
|
-
async function readDeviceConfigs(
|
|
761
|
-
const blob = await
|
|
757
|
+
async function readDeviceConfigs(state) {
|
|
758
|
+
const blob = await state.get();
|
|
762
759
|
const map = /* @__PURE__ */ new Map();
|
|
763
760
|
for (const [key, config] of Object.entries(blob)) {
|
|
764
761
|
const id = Number(key);
|
|
@@ -772,15 +769,15 @@ async function readDeviceConfigs(store) {
|
|
|
772
769
|
* re-persisted so the stored shape converges on the authoritative model. An
|
|
773
770
|
* unconfigured device returns {@link DEFAULT_DEVICE_CONFIG}.
|
|
774
771
|
*/
|
|
775
|
-
async function loadDeviceConfig(
|
|
776
|
-
const stored = (await readDeviceConfigs(
|
|
772
|
+
async function loadDeviceConfig(state, deviceId) {
|
|
773
|
+
const stored = (await readDeviceConfigs(state)).get(deviceId);
|
|
777
774
|
if (!stored) return DEFAULT_DEVICE_CONFIG;
|
|
778
775
|
if (stored.bands) return stored;
|
|
779
776
|
const migrated = {
|
|
780
777
|
...stored,
|
|
781
778
|
bands: migrateConfigToBands(stored)
|
|
782
779
|
};
|
|
783
|
-
await saveDeviceConfig(
|
|
780
|
+
await saveDeviceConfig(state, deviceId, migrated);
|
|
784
781
|
return migrated;
|
|
785
782
|
}
|
|
786
783
|
/**
|
|
@@ -788,11 +785,11 @@ async function loadDeviceConfig(store, deviceId) {
|
|
|
788
785
|
* `RecordingConfigSchema` at the boundary (untrusted cap input). Returns the
|
|
789
786
|
* validated config that was persisted.
|
|
790
787
|
*/
|
|
791
|
-
async function saveDeviceConfig(
|
|
788
|
+
async function saveDeviceConfig(state, deviceId, config) {
|
|
792
789
|
const validated = RecordingConfigSchema.parse(config);
|
|
793
|
-
const map = await readDeviceConfigs(
|
|
790
|
+
const map = await readDeviceConfigs(state);
|
|
794
791
|
map.set(deviceId, validated);
|
|
795
|
-
await
|
|
792
|
+
await state.set(serialize(map));
|
|
796
793
|
return validated;
|
|
797
794
|
}
|
|
798
795
|
/**
|
|
@@ -2051,9 +2048,16 @@ var resolveRecordingHubHostname = resolveHubHostname;
|
|
|
2051
2048
|
* provider until the controller swaps the manifest entry in a later step — this
|
|
2052
2049
|
* file ships dormant (separate tree, not wired into the manifest yet).
|
|
2053
2050
|
*/
|
|
2051
|
+
/** Default designated recording node (`recordingNodeId` global setting). The
|
|
2052
|
+
* stream-broker is hub-only and recording is a single-node global function, so
|
|
2053
|
+
* the hub is the safe default — mirrors pipeline-analytics' post-processing
|
|
2054
|
+
* node gate. */
|
|
2055
|
+
var RECORDING_NODE_DEFAULT = "hub";
|
|
2054
2056
|
var DEFAULT_CONFIG = {
|
|
2055
2057
|
segmentSeconds: 10,
|
|
2056
|
-
watchIntervalMs: 2e3
|
|
2058
|
+
watchIntervalMs: 2e3,
|
|
2059
|
+
recordingNodeId: RECORDING_NODE_DEFAULT,
|
|
2060
|
+
recordingHubHostname: ""
|
|
2057
2061
|
};
|
|
2058
2062
|
/** Playback-marker window padding around a motion event (EventMap only; the
|
|
2059
2063
|
* controller's events-band gating uses the raw trigger ms, not this pad).
|
|
@@ -2068,11 +2072,6 @@ var MARKER_PAD_POST_MS = EVENT_PAD_MS.postMs;
|
|
|
2068
2072
|
* form the hub reverse-proxy client path `/addon/recorder/playback`. */
|
|
2069
2073
|
var RECORDER_ADDON_ID = "recorder";
|
|
2070
2074
|
var PLAYBACK_PREFIX = "playback";
|
|
2071
|
-
/** Default designated recording node (`recordingNodeId` global setting). The
|
|
2072
|
-
* stream-broker is hub-only and recording is a single-node global function, so
|
|
2073
|
-
* the hub is the safe default — mirrors pipeline-analytics' post-processing
|
|
2074
|
-
* node gate. */
|
|
2075
|
-
var RECORDING_NODE_DEFAULT = "hub";
|
|
2076
2075
|
/** Node the hub-only `stream-broker` runs on. Every recorder — on the hub or on
|
|
2077
2076
|
* a designated agent — gates on, and pulls from, THIS node's broker. */
|
|
2078
2077
|
var BROKER_HOST_NODE_ID = "hub";
|
|
@@ -2100,9 +2099,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
2100
2099
|
async onInitialize() {
|
|
2101
2100
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
2102
2101
|
this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
|
|
2103
|
-
const
|
|
2104
|
-
const
|
|
2105
|
-
const designated = typeof rawNode === "string" && rawNode.length > 0 ? rawNode : RECORDING_NODE_DEFAULT;
|
|
2102
|
+
const configuredNode = this.config.recordingNodeId;
|
|
2103
|
+
const designated = configuredNode.length > 0 ? configuredNode : RECORDING_NODE_DEFAULT;
|
|
2106
2104
|
this.isRecordingNode = this.nodeId === designated;
|
|
2107
2105
|
if (!this.isRecordingNode) {
|
|
2108
2106
|
this.ctx.logger.info("recorder INERT on this node — recording assigned elsewhere", { meta: {
|
|
@@ -2111,8 +2109,7 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
2111
2109
|
} });
|
|
2112
2110
|
return { providers: [] };
|
|
2113
2111
|
}
|
|
2114
|
-
const
|
|
2115
|
-
const consumerHostname = this.nodeId === BROKER_HOST_NODE_ID ? void 0 : resolveRecordingHubHostname(typeof rawHubHostname === "string" ? rawHubHostname : void 0, process.env["CAMSTACK_HUB_URL"]);
|
|
2112
|
+
const consumerHostname = this.nodeId === BROKER_HOST_NODE_ID ? void 0 : resolveRecordingHubHostname(this.config.recordingHubHostname, process.env["CAMSTACK_HUB_URL"]);
|
|
2116
2113
|
if (this.nodeId !== BROKER_HOST_NODE_ID) {
|
|
2117
2114
|
this.ctx.logger.info("recorder is a REMOTE recording node — pulling broker streams cross-node", { meta: {
|
|
2118
2115
|
ownNodeId: this.nodeId,
|
|
@@ -2317,7 +2314,9 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
2317
2314
|
* Global settings surfaced in the admin UI (and persisted to the addon
|
|
2318
2315
|
* store). The cluster Recording tab drives `recordingNodeId` via a node
|
|
2319
2316
|
* picker; `recordingHubHostname` is an advanced override for cross-node
|
|
2320
|
-
* pulls. Both
|
|
2317
|
+
* pulls. Both are SHARED cluster keys (not perNode) hydrated into
|
|
2318
|
+
* `this.config` by `resolveConfig()` and take effect on addon restart
|
|
2319
|
+
* (read once in `onInitialize`).
|
|
2321
2320
|
*/
|
|
2322
2321
|
globalSettingsSchema() {
|
|
2323
2322
|
return this.schema({ sections: [{
|
|
@@ -2340,14 +2339,11 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
2340
2339
|
}]
|
|
2341
2340
|
}] });
|
|
2342
2341
|
}
|
|
2343
|
-
/**
|
|
2342
|
+
/** Durable handle config-store reads/writes the deviceId→config blob on —
|
|
2343
|
+
* the sanctioned `BaseAddon.state()` wrapper (same key, same blob shape;
|
|
2344
|
+
* adds the read-retry budget + parse-warning logging over the raw slice). */
|
|
2344
2345
|
configStore() {
|
|
2345
|
-
|
|
2346
|
-
if (!settings) throw new Error("RecorderV2Addon: ctx.settings unavailable — cannot persist recording config");
|
|
2347
|
-
return {
|
|
2348
|
-
readAddonStore: () => settings.readAddonStore(),
|
|
2349
|
-
writeAddonStore: (patch) => settings.writeAddonStore(patch)
|
|
2350
|
-
};
|
|
2346
|
+
return this.state(RECORDING_CONFIGS_KEY, RecordingConfigsBlobSchema, {});
|
|
2351
2347
|
}
|
|
2352
2348
|
};
|
|
2353
2349
|
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DtoYGpTp.mjs";
|
|
2
2
|
import { a as u, i as d, n as f, o as p, r as m, t as h } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
|
|
3
3
|
import { n as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
|
|
4
4
|
import { n as y, t as b } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
|
|
5
|
-
import { t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-
|
|
5
|
+
import { t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DhY3MZ2C.mjs";
|
|
6
6
|
//#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
|
|
7
7
|
var S = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), C = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), w = (e) => {
|
|
8
8
|
let t = C(e);
|