@camstack/addon-pipeline 1.2.1 → 1.2.3
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 +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +2 -2
- package/dist/detection-pipeline/index.mjs +2 -2
- package/dist/{dist-E0K373T1.js → dist-D7-LF079.js} +68 -7
- package/dist/{dist-C3cDjL49.mjs → dist-DVjpngiC.mjs} +68 -7
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +198 -21
- package/dist/pipeline-runner/index.mjs +198 -21
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/session-decode/decode-worker-child.js +55 -7
- package/dist/session-decode/decode-worker-child.mjs +55 -7
- package/dist/{step-definitions-ty97QrzE.mjs → step-definitions-BPwND8Zg.mjs} +1 -1
- package/dist/{step-definitions-DMne6GHq.js → step-definitions-BfXdDokR.js} +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B2g-vEQt.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BozTkBJG.mjs} +3 -3
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-UGa9yj7_.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CQC_f4Yv.mjs} +1 -1
- package/dist/stream-broker/{hostInit-C3C5oZGy.mjs → hostInit-BUab2768.mjs} +3 -3
- package/dist/stream-broker/index.js +1 -1
- package/dist/stream-broker/index.mjs +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-CyVJTZEO.mjs → worker-protocol-D7RzZIla.mjs} +16 -2
- package/dist/{worker-protocol-PP4jKHHJ.js → worker-protocol-DextwlTX.js} +21 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-BM5dXUL6.js → MaskShapeCanvas-DI4BY7W2-L6rscpzL.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-C7QpBII3.js → MotionZonesSettings-NcxxQN8r-CK9BVkOT.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DgvSlqrq.js → PrivacyMaskSettings-APgPLF7p-CNcYwFLg.js} +1 -1
- package/embed-dist/assets/{index-C3bYXV0t.js → index-CBhJRpiC.js} +13 -13
- package/embed-dist/assets/index-Dv5H-32C.css +2 -0
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/embed-dist/assets/index-D4MGuECB.css +0 -2
|
@@ -3,7 +3,7 @@ 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-D7-LF079.js");
|
|
7
7
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
8
8
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Dv-oFwjN.js");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { D as mapAudioLabelToMacro, H as errMsg, J as hydrateSchema, U as BaseAddon, _ as audioAnalysisCapability, d as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, v as audioAnalyzerCapability } from "../dist-
|
|
2
|
+
import { D as mapAudioLabelToMacro, H as errMsg, J as hydrateSchema, U as BaseAddon, _ as audioAnalysisCapability, d as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, v as audioAnalyzerCapability } from "../dist-DVjpngiC.mjs";
|
|
3
3
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
4
4
|
import { i as downloadFile } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -3,8 +3,8 @@ 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-
|
|
7
|
-
const require_step_definitions = require("../step-definitions-
|
|
6
|
+
const require_dist = require("../dist-D7-LF079.js");
|
|
7
|
+
const require_step_definitions = require("../step-definitions-BfXdDokR.js");
|
|
8
8
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
9
9
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Dv-oFwjN.js");
|
|
10
10
|
let node_child_process = require("node:child_process");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { B as supportedRuntimes$1, C as detectionPipelineCapability, H as errMsg, J as hydrateSchema, L as runtimeDevices$1, M as pipelineExecutorCapability, Q as parseJsonUnknown, T as evaluateZoneRules, U as BaseAddon, dt as string, ft as union, h as YAMNET_TO_MACRO, j as nodePin, lt as object, pt as EventCategory, q as createEvent, rt as array, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, tt as sleep, w as enumerateInferenceDevices, x as defaultDeviceFor$1 } from "../dist-
|
|
3
|
-
import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-
|
|
2
|
+
import { B as supportedRuntimes$1, C as detectionPipelineCapability, H as errMsg, J as hydrateSchema, L as runtimeDevices$1, M as pipelineExecutorCapability, Q as parseJsonUnknown, T as evaluateZoneRules, U as BaseAddon, dt as string, ft as union, h as YAMNET_TO_MACRO, j as nodePin, lt as object, pt as EventCategory, q as createEvent, rt as array, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, tt as sleep, w as enumerateInferenceDevices, x as defaultDeviceFor$1 } from "../dist-DVjpngiC.mjs";
|
|
3
|
+
import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-BPwND8Zg.mjs";
|
|
4
4
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
5
5
|
import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
@@ -11860,7 +11860,8 @@ var PipelineStepInputSchema = lazy(() => object({
|
|
|
11860
11860
|
modelId: string().optional(),
|
|
11861
11861
|
enabled: boolean().default(true),
|
|
11862
11862
|
children: array(PipelineStepInputSchema).optional(),
|
|
11863
|
-
settings: record(string(), unknown()).optional()
|
|
11863
|
+
settings: record(string(), unknown()).optional(),
|
|
11864
|
+
jumpDeviceKey: string().optional()
|
|
11864
11865
|
}));
|
|
11865
11866
|
var ModelSubstitutionSchema = object({
|
|
11866
11867
|
addonId: string(),
|
|
@@ -12318,8 +12319,24 @@ var NativeCropBboxSchema = object({
|
|
|
12318
12319
|
});
|
|
12319
12320
|
/** Result of a best-effort native-resolution crop (`getNativeCrop`). */
|
|
12320
12321
|
var NativeCropResultSchema = object({
|
|
12321
|
-
/**
|
|
12322
|
-
|
|
12322
|
+
/**
|
|
12323
|
+
* Packed rgb (24-bit) pixels of the crop. Present on the DEFAULT (raw) path —
|
|
12324
|
+
* same-node (in-process / UDS) callers get zero-copy RGB and encode locally.
|
|
12325
|
+
* OMITTED when the caller requested `encodeJpeg` (the cross-node compressed
|
|
12326
|
+
* path below), where shipping raw RGB is both an invariant violation and, for
|
|
12327
|
+
* a native full frame (~26 MB at 4K), larger than Moleculer's 10 MB
|
|
12328
|
+
* `maxPacketSize` → the packet is dropped and the call 60s-times-out. That is
|
|
12329
|
+
* the cross-node native-media miss: `bytes` is replaced by `jpeg`.
|
|
12330
|
+
*/
|
|
12331
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
12332
|
+
/**
|
|
12333
|
+
* Base64 JPEG of the crop — the COMPRESSED cross-node payload. The OWNING node
|
|
12334
|
+
* (which holds the native surface) encodes it in-process, so a native 4K frame
|
|
12335
|
+
* ships as ~0.5–2 MB (well under `maxPacketSize`) and NO raw pixels ever cross
|
|
12336
|
+
* a process boundary (CLAUDE.md invariant #21). Present ONLY when the request
|
|
12337
|
+
* set `encodeJpeg: true`; `bytes` is then absent.
|
|
12338
|
+
*/
|
|
12339
|
+
jpeg: string().optional(),
|
|
12323
12340
|
width: number().int().positive(),
|
|
12324
12341
|
height: number().int().positive(),
|
|
12325
12342
|
/**
|
|
@@ -12465,6 +12482,20 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
|
|
|
12465
12482
|
hubHostnameOverride: string().optional()
|
|
12466
12483
|
})]).describe("Per-camera frame-source mode for the runner (P2c)");
|
|
12467
12484
|
/**
|
|
12485
|
+
* One ENABLED inference device on the runner's node, as the step-tree
|
|
12486
|
+
* device-jump resolver sees it (phase 1). The orchestrator populates this
|
|
12487
|
+
* roster on the attach payload whenever the camera is elected onto a specific
|
|
12488
|
+
* `deviceKey` and the node has ≥2 enabled devices — it is the candidate set the
|
|
12489
|
+
* runner auto-jumps an enrichment step to when the elected device's format
|
|
12490
|
+
* cannot run that step's model. `weight`/`maxSessions` mirror the balancer's
|
|
12491
|
+
* per-device knobs so the auto choice is weighted-least-loaded.
|
|
12492
|
+
*/
|
|
12493
|
+
var RunnerInferenceDeviceSchema = object({
|
|
12494
|
+
deviceKey: string(),
|
|
12495
|
+
weight: number().positive().default(1),
|
|
12496
|
+
maxSessions: number().int().positive().nullable().default(null)
|
|
12497
|
+
});
|
|
12498
|
+
/**
|
|
12468
12499
|
* Camera assignment payload sent by `addon-pipeline-orchestrator` to a
|
|
12469
12500
|
* specific runner instance via `attachCamera`. Carries everything the
|
|
12470
12501
|
* runner needs to subscribe to the local broker and execute inference.
|
|
@@ -12582,7 +12613,16 @@ var RunnerCameraConfigSchema = object({
|
|
|
12582
12613
|
* omitted ⇒ the runner's default device. The engine itself stays node-local —
|
|
12583
12614
|
* this only selects WHICH device pool of that node runs the session.
|
|
12584
12615
|
*/
|
|
12585
|
-
deviceKey: string().optional()
|
|
12616
|
+
deviceKey: string().optional(),
|
|
12617
|
+
/**
|
|
12618
|
+
* Step-tree device-jump roster (phase 1): the node's ENABLED inference
|
|
12619
|
+
* devices, populated by the orchestrator ONLY when `deviceKey` is set and the
|
|
12620
|
+
* node has ≥2 enabled devices. The runner uses it to AUTO-jump an enrichment
|
|
12621
|
+
* step whose model has no build for the elected device's format onto another
|
|
12622
|
+
* enabled device of the SAME node (weighted-least-loaded). Absent/single-entry
|
|
12623
|
+
* ⇒ no jump possible; the step runs on `deviceKey`.
|
|
12624
|
+
*/
|
|
12625
|
+
inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
12586
12626
|
});
|
|
12587
12627
|
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
12588
12628
|
/**
|
|
@@ -12702,7 +12742,18 @@ var pipelineRunnerCapability = {
|
|
|
12702
12742
|
getNativeCrop: method(object({
|
|
12703
12743
|
handle: FrameHandleSchema,
|
|
12704
12744
|
bbox: NativeCropBboxSchema,
|
|
12705
|
-
maxWidth: number().int().positive().optional()
|
|
12745
|
+
maxWidth: number().int().positive().optional(),
|
|
12746
|
+
/**
|
|
12747
|
+
* When `true`, the runner encodes the resolved crop to JPEG ON THE
|
|
12748
|
+
* OWNING NODE and returns it in `jpeg` (base64) INSTEAD of raw `bytes`.
|
|
12749
|
+
* Callers set this for CROSS-NODE fetches (`handle.nodeId` is a remote
|
|
12750
|
+
* agent) so the compressed payload fits Moleculer's `maxPacketSize` and
|
|
12751
|
+
* no raw pixels cross the process boundary. Same-node callers omit it
|
|
12752
|
+
* and keep the zero-copy raw `bytes` path. Additive + optional: a
|
|
12753
|
+
* pre-encode runner (version skew) ignores it and returns `bytes`, so
|
|
12754
|
+
* the caller falls back to encoding locally.
|
|
12755
|
+
*/
|
|
12756
|
+
encodeJpeg: boolean().optional()
|
|
12706
12757
|
}), NativeCropResultSchema.nullable()),
|
|
12707
12758
|
/**
|
|
12708
12759
|
* Two-plane design: run the DETAIL subtree (crop children —
|
|
@@ -17726,7 +17777,8 @@ var MediaFileKindEnum = _enum([
|
|
|
17726
17777
|
"faceCrop",
|
|
17727
17778
|
"plateCrop",
|
|
17728
17779
|
"keyFrame",
|
|
17729
|
-
"keyFrameSmall"
|
|
17780
|
+
"keyFrameSmall",
|
|
17781
|
+
"thumbnailSmall"
|
|
17730
17782
|
]);
|
|
17731
17783
|
var MediaFileSchema = object({
|
|
17732
17784
|
key: string(),
|
|
@@ -18057,7 +18109,16 @@ var PipelineTemplateSchema = object({
|
|
|
18057
18109
|
});
|
|
18058
18110
|
var DeviceStepConfigSchema = object({
|
|
18059
18111
|
modelId: string().optional(),
|
|
18060
|
-
settings: record(string(), unknown()).optional()
|
|
18112
|
+
settings: record(string(), unknown()).optional(),
|
|
18113
|
+
/**
|
|
18114
|
+
* Step-tree device jump (same-node, phase 1) — OPTIONAL manual override.
|
|
18115
|
+
* When set, this step runs on the named enabled device of the SAME node
|
|
18116
|
+
* (`<backend>:<device>`) instead of the camera's elected device, bypassing
|
|
18117
|
+
* the runtime AUTO-jump. The orchestrator rejects a `jumpDeviceKey` pointing
|
|
18118
|
+
* at a disabled/absent device on that node at save time. Absent ⇒ the runner
|
|
18119
|
+
* auto-jumps only when the effective device's format cannot run the step.
|
|
18120
|
+
*/
|
|
18121
|
+
jumpDeviceKey: string().optional()
|
|
18061
18122
|
});
|
|
18062
18123
|
var AgentPipelineSettingsSchema = object({
|
|
18063
18124
|
maxCameras: number().int().nonnegative().nullable().default(null),
|
|
@@ -11860,7 +11860,8 @@ var PipelineStepInputSchema = lazy(() => object({
|
|
|
11860
11860
|
modelId: string().optional(),
|
|
11861
11861
|
enabled: boolean().default(true),
|
|
11862
11862
|
children: array(PipelineStepInputSchema).optional(),
|
|
11863
|
-
settings: record(string(), unknown()).optional()
|
|
11863
|
+
settings: record(string(), unknown()).optional(),
|
|
11864
|
+
jumpDeviceKey: string().optional()
|
|
11864
11865
|
}));
|
|
11865
11866
|
var ModelSubstitutionSchema = object({
|
|
11866
11867
|
addonId: string(),
|
|
@@ -12318,8 +12319,24 @@ var NativeCropBboxSchema = object({
|
|
|
12318
12319
|
});
|
|
12319
12320
|
/** Result of a best-effort native-resolution crop (`getNativeCrop`). */
|
|
12320
12321
|
var NativeCropResultSchema = object({
|
|
12321
|
-
/**
|
|
12322
|
-
|
|
12322
|
+
/**
|
|
12323
|
+
* Packed rgb (24-bit) pixels of the crop. Present on the DEFAULT (raw) path —
|
|
12324
|
+
* same-node (in-process / UDS) callers get zero-copy RGB and encode locally.
|
|
12325
|
+
* OMITTED when the caller requested `encodeJpeg` (the cross-node compressed
|
|
12326
|
+
* path below), where shipping raw RGB is both an invariant violation and, for
|
|
12327
|
+
* a native full frame (~26 MB at 4K), larger than Moleculer's 10 MB
|
|
12328
|
+
* `maxPacketSize` → the packet is dropped and the call 60s-times-out. That is
|
|
12329
|
+
* the cross-node native-media miss: `bytes` is replaced by `jpeg`.
|
|
12330
|
+
*/
|
|
12331
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
12332
|
+
/**
|
|
12333
|
+
* Base64 JPEG of the crop — the COMPRESSED cross-node payload. The OWNING node
|
|
12334
|
+
* (which holds the native surface) encodes it in-process, so a native 4K frame
|
|
12335
|
+
* ships as ~0.5–2 MB (well under `maxPacketSize`) and NO raw pixels ever cross
|
|
12336
|
+
* a process boundary (CLAUDE.md invariant #21). Present ONLY when the request
|
|
12337
|
+
* set `encodeJpeg: true`; `bytes` is then absent.
|
|
12338
|
+
*/
|
|
12339
|
+
jpeg: string().optional(),
|
|
12323
12340
|
width: number().int().positive(),
|
|
12324
12341
|
height: number().int().positive(),
|
|
12325
12342
|
/**
|
|
@@ -12465,6 +12482,20 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
|
|
|
12465
12482
|
hubHostnameOverride: string().optional()
|
|
12466
12483
|
})]).describe("Per-camera frame-source mode for the runner (P2c)");
|
|
12467
12484
|
/**
|
|
12485
|
+
* One ENABLED inference device on the runner's node, as the step-tree
|
|
12486
|
+
* device-jump resolver sees it (phase 1). The orchestrator populates this
|
|
12487
|
+
* roster on the attach payload whenever the camera is elected onto a specific
|
|
12488
|
+
* `deviceKey` and the node has ≥2 enabled devices — it is the candidate set the
|
|
12489
|
+
* runner auto-jumps an enrichment step to when the elected device's format
|
|
12490
|
+
* cannot run that step's model. `weight`/`maxSessions` mirror the balancer's
|
|
12491
|
+
* per-device knobs so the auto choice is weighted-least-loaded.
|
|
12492
|
+
*/
|
|
12493
|
+
var RunnerInferenceDeviceSchema = object({
|
|
12494
|
+
deviceKey: string(),
|
|
12495
|
+
weight: number().positive().default(1),
|
|
12496
|
+
maxSessions: number().int().positive().nullable().default(null)
|
|
12497
|
+
});
|
|
12498
|
+
/**
|
|
12468
12499
|
* Camera assignment payload sent by `addon-pipeline-orchestrator` to a
|
|
12469
12500
|
* specific runner instance via `attachCamera`. Carries everything the
|
|
12470
12501
|
* runner needs to subscribe to the local broker and execute inference.
|
|
@@ -12582,7 +12613,16 @@ var RunnerCameraConfigSchema = object({
|
|
|
12582
12613
|
* omitted ⇒ the runner's default device. The engine itself stays node-local —
|
|
12583
12614
|
* this only selects WHICH device pool of that node runs the session.
|
|
12584
12615
|
*/
|
|
12585
|
-
deviceKey: string().optional()
|
|
12616
|
+
deviceKey: string().optional(),
|
|
12617
|
+
/**
|
|
12618
|
+
* Step-tree device-jump roster (phase 1): the node's ENABLED inference
|
|
12619
|
+
* devices, populated by the orchestrator ONLY when `deviceKey` is set and the
|
|
12620
|
+
* node has ≥2 enabled devices. The runner uses it to AUTO-jump an enrichment
|
|
12621
|
+
* step whose model has no build for the elected device's format onto another
|
|
12622
|
+
* enabled device of the SAME node (weighted-least-loaded). Absent/single-entry
|
|
12623
|
+
* ⇒ no jump possible; the step runs on `deviceKey`.
|
|
12624
|
+
*/
|
|
12625
|
+
inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
12586
12626
|
});
|
|
12587
12627
|
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
12588
12628
|
/**
|
|
@@ -12702,7 +12742,18 @@ var pipelineRunnerCapability = {
|
|
|
12702
12742
|
getNativeCrop: method(object({
|
|
12703
12743
|
handle: FrameHandleSchema,
|
|
12704
12744
|
bbox: NativeCropBboxSchema,
|
|
12705
|
-
maxWidth: number().int().positive().optional()
|
|
12745
|
+
maxWidth: number().int().positive().optional(),
|
|
12746
|
+
/**
|
|
12747
|
+
* When `true`, the runner encodes the resolved crop to JPEG ON THE
|
|
12748
|
+
* OWNING NODE and returns it in `jpeg` (base64) INSTEAD of raw `bytes`.
|
|
12749
|
+
* Callers set this for CROSS-NODE fetches (`handle.nodeId` is a remote
|
|
12750
|
+
* agent) so the compressed payload fits Moleculer's `maxPacketSize` and
|
|
12751
|
+
* no raw pixels cross the process boundary. Same-node callers omit it
|
|
12752
|
+
* and keep the zero-copy raw `bytes` path. Additive + optional: a
|
|
12753
|
+
* pre-encode runner (version skew) ignores it and returns `bytes`, so
|
|
12754
|
+
* the caller falls back to encoding locally.
|
|
12755
|
+
*/
|
|
12756
|
+
encodeJpeg: boolean().optional()
|
|
12706
12757
|
}), NativeCropResultSchema.nullable()),
|
|
12707
12758
|
/**
|
|
12708
12759
|
* Two-plane design: run the DETAIL subtree (crop children —
|
|
@@ -17726,7 +17777,8 @@ var MediaFileKindEnum = _enum([
|
|
|
17726
17777
|
"faceCrop",
|
|
17727
17778
|
"plateCrop",
|
|
17728
17779
|
"keyFrame",
|
|
17729
|
-
"keyFrameSmall"
|
|
17780
|
+
"keyFrameSmall",
|
|
17781
|
+
"thumbnailSmall"
|
|
17730
17782
|
]);
|
|
17731
17783
|
var MediaFileSchema = object({
|
|
17732
17784
|
key: string(),
|
|
@@ -18057,7 +18109,16 @@ var PipelineTemplateSchema = object({
|
|
|
18057
18109
|
});
|
|
18058
18110
|
var DeviceStepConfigSchema = object({
|
|
18059
18111
|
modelId: string().optional(),
|
|
18060
|
-
settings: record(string(), unknown()).optional()
|
|
18112
|
+
settings: record(string(), unknown()).optional(),
|
|
18113
|
+
/**
|
|
18114
|
+
* Step-tree device jump (same-node, phase 1) — OPTIONAL manual override.
|
|
18115
|
+
* When set, this step runs on the named enabled device of the SAME node
|
|
18116
|
+
* (`<backend>:<device>`) instead of the camera's elected device, bypassing
|
|
18117
|
+
* the runtime AUTO-jump. The orchestrator rejects a `jumpDeviceKey` pointing
|
|
18118
|
+
* at a disabled/absent device on that node at save time. Absent ⇒ the runner
|
|
18119
|
+
* auto-jumps only when the effective device's format cannot run the step.
|
|
18120
|
+
*/
|
|
18121
|
+
jumpDeviceKey: string().optional()
|
|
18061
18122
|
});
|
|
18062
18123
|
var AgentPipelineSettingsSchema = object({
|
|
18063
18124
|
maxCameras: number().int().nonnegative().nullable().default(null),
|
|
@@ -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-D7-LF079.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 { A as motionDetectionCapability, J as hydrateSchema, K as DeviceType, T as evaluateZoneRules, U as BaseAddon } from "../dist-
|
|
1
|
+
import { A as motionDetectionCapability, J as hydrateSchema, K as DeviceType, T as evaluateZoneRules, U as BaseAddon } from "../dist-DVjpngiC.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-D7-LF079.js");
|
|
7
7
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
8
8
|
const require_sheet_geometry = require("../sheet-geometry-BR-ip0CP.js");
|
|
9
|
-
const require_worker_protocol = require("../worker-protocol-
|
|
10
|
-
const require_step_definitions = require("../step-definitions-
|
|
9
|
+
const require_worker_protocol = require("../worker-protocol-DextwlTX.js");
|
|
10
|
+
const require_step_definitions = require("../step-definitions-BfXdDokR.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");
|
|
@@ -1680,11 +1680,18 @@ var SessionDecodeSession = class {
|
|
|
1680
1680
|
});
|
|
1681
1681
|
return;
|
|
1682
1682
|
case "nativeCropMiss":
|
|
1683
|
+
if (raw.reason !== void 0) this.logger.debug("session-decode: native crop miss", { meta: {
|
|
1684
|
+
requestId: raw.requestId,
|
|
1685
|
+
reason: raw.reason
|
|
1686
|
+
} });
|
|
1683
1687
|
this.settleNativeCrop(raw.requestId, null);
|
|
1684
1688
|
return;
|
|
1685
1689
|
case "error":
|
|
1686
1690
|
this.settleError(raw.message, raw.frameId);
|
|
1687
1691
|
return;
|
|
1692
|
+
case "log":
|
|
1693
|
+
this.logger[raw.level](raw.message);
|
|
1694
|
+
return;
|
|
1688
1695
|
}
|
|
1689
1696
|
}
|
|
1690
1697
|
handleExit(info) {
|
|
@@ -2253,13 +2260,93 @@ async function runDetailSubtree(deps, input) {
|
|
|
2253
2260
|
if (!crop) return null;
|
|
2254
2261
|
const details = [];
|
|
2255
2262
|
for (const step of steps) {
|
|
2256
|
-
const
|
|
2263
|
+
const deviceKey = deps.resolveStepDevice?.(input.deviceId, step);
|
|
2264
|
+
const result = await deps.runPipeline([step], crop.jpeg, input.deviceId, deviceKey);
|
|
2257
2265
|
if (!result) continue;
|
|
2258
2266
|
for (const detection of result.detections) details.push(toDetailResult(detection, step.addonId, crop));
|
|
2259
2267
|
}
|
|
2260
2268
|
return { details };
|
|
2261
2269
|
}
|
|
2262
2270
|
//#endregion
|
|
2271
|
+
//#region src/pipeline-runner/device-jump.ts
|
|
2272
|
+
/**
|
|
2273
|
+
* Step-tree device jump (same-node, phase 1) — the PURE resolution core.
|
|
2274
|
+
*
|
|
2275
|
+
* Given the camera's elected ("effective") inference device and the node's
|
|
2276
|
+
* enabled-device roster, decide which device ONE enrichment step should run on:
|
|
2277
|
+
*
|
|
2278
|
+
* 1. A manual `jumpDeviceKey` override wins when it names a candidate that can
|
|
2279
|
+
* run the step (bypasses the auto choice; operator intent).
|
|
2280
|
+
* 2. Otherwise, if the effective device's format can run the step's model →
|
|
2281
|
+
* NO jump (the step inherits the effective device — the common case).
|
|
2282
|
+
* 3. Otherwise AUTO-jump: among the node's OTHER enabled devices whose format
|
|
2283
|
+
* can run the step (and that are under their `maxSessions` cap), pick the
|
|
2284
|
+
* weighted-least-loaded one — mirroring the balancer's per-device core.
|
|
2285
|
+
* 4. If nothing on the node can run it → `no-candidate` (the caller emits the
|
|
2286
|
+
* loud once-per-(camera,step) warn that replaces today's silent degrade).
|
|
2287
|
+
*
|
|
2288
|
+
* Framework-free + side-effect-free so the whole decision table is unit-testable
|
|
2289
|
+
* without a live runner/catalog. The `canRun(format)` predicate is injected by
|
|
2290
|
+
* the runner (which owns the model catalog); this module never imports it.
|
|
2291
|
+
*/
|
|
2292
|
+
/** The `ModelFormat` of a `<backend>:<device>` key, or `undefined` for an unknown backend. */
|
|
2293
|
+
function formatOf(deviceKey) {
|
|
2294
|
+
const colon = deviceKey.indexOf(":");
|
|
2295
|
+
return require_dist.DEVICE_BACKEND_TO_FORMAT[colon >= 0 ? deviceKey.slice(0, colon) : deviceKey];
|
|
2296
|
+
}
|
|
2297
|
+
/** True when the step's model has a build for `deviceKey`'s format. */
|
|
2298
|
+
function canRunOn(deviceKey, canRun) {
|
|
2299
|
+
const format = formatOf(deviceKey);
|
|
2300
|
+
return format !== void 0 && canRun(format);
|
|
2301
|
+
}
|
|
2302
|
+
/** Effective weighted load of a device (session count ÷ weight); lower = preferred. */
|
|
2303
|
+
function weightedLoad(d) {
|
|
2304
|
+
return d.attachedCameras / (d.weight > 0 ? d.weight : 1);
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* Weighted-least-loaded pick with a deterministic `deviceKey` tie-break —
|
|
2308
|
+
* mirrors the node/device balancer's decision core (least `load/weight`, ties
|
|
2309
|
+
* broken stably). `devices` is assumed non-empty.
|
|
2310
|
+
*/
|
|
2311
|
+
function pickWeightedLeastLoaded(devices) {
|
|
2312
|
+
return devices.reduce((best, d) => {
|
|
2313
|
+
const dScore = weightedLoad(d);
|
|
2314
|
+
const bScore = weightedLoad(best);
|
|
2315
|
+
if (dScore < bScore) return d;
|
|
2316
|
+
if (dScore > bScore) return best;
|
|
2317
|
+
return d.deviceKey < best.deviceKey ? d : best;
|
|
2318
|
+
});
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Resolve the device one enrichment step runs on. See the module header for the
|
|
2322
|
+
* decision table. Pure — never throws, no I/O.
|
|
2323
|
+
*/
|
|
2324
|
+
function resolveStepDevice(input) {
|
|
2325
|
+
const { effectiveDeviceKey, manualJumpDeviceKey, canRun } = input;
|
|
2326
|
+
if (manualJumpDeviceKey !== void 0 && manualJumpDeviceKey !== effectiveDeviceKey) {
|
|
2327
|
+
const target = input.devices.find((d) => d.deviceKey === manualJumpDeviceKey);
|
|
2328
|
+
if (target && canRunOn(target.deviceKey, canRun)) return {
|
|
2329
|
+
kind: "jumped",
|
|
2330
|
+
deviceKey: target.deviceKey,
|
|
2331
|
+
reason: "manual"
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
if (canRunOn(effectiveDeviceKey, canRun)) return {
|
|
2335
|
+
kind: "no-jump",
|
|
2336
|
+
deviceKey: effectiveDeviceKey
|
|
2337
|
+
};
|
|
2338
|
+
const runnable = input.devices.filter((d) => d.deviceKey !== effectiveDeviceKey && (d.maxSessions === null || d.attachedCameras < d.maxSessions) && canRunOn(d.deviceKey, canRun));
|
|
2339
|
+
if (runnable.length === 0) return {
|
|
2340
|
+
kind: "no-candidate",
|
|
2341
|
+
deviceKey: effectiveDeviceKey
|
|
2342
|
+
};
|
|
2343
|
+
return {
|
|
2344
|
+
kind: "jumped",
|
|
2345
|
+
deviceKey: pickWeightedLeastLoaded(runnable).deviceKey,
|
|
2346
|
+
reason: "auto"
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
2349
|
+
//#endregion
|
|
2263
2350
|
//#region src/pipeline-runner/index.ts
|
|
2264
2351
|
var DEFAULT_CONFIG = {
|
|
2265
2352
|
maxQueueDepth: 30,
|
|
@@ -3224,18 +3311,48 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3224
3311
|
}
|
|
3225
3312
|
}
|
|
3226
3313
|
const tier = resolveNativeCropSource(isFullFrame, nativeResult !== null);
|
|
3227
|
-
if (tier === "native" && nativeResult) return
|
|
3228
|
-
...nativeResult,
|
|
3229
|
-
tier
|
|
3230
|
-
};
|
|
3314
|
+
if (tier === "native" && nativeResult) return this.encodeNativeCropResult(nativeResult, tier, input.encodeJpeg);
|
|
3231
3315
|
if (tier !== "ram-fullframe") return null;
|
|
3232
3316
|
const crop = this.retainedFrames?.crop(input.handle, input.bbox, input.maxWidth);
|
|
3233
|
-
|
|
3317
|
+
if (!crop) return null;
|
|
3318
|
+
return this.encodeNativeCropResult({
|
|
3319
|
+
bytes: crop.bytes,
|
|
3320
|
+
width: crop.width,
|
|
3321
|
+
height: crop.height
|
|
3322
|
+
}, tier, input.encodeJpeg);
|
|
3323
|
+
}
|
|
3324
|
+
/**
|
|
3325
|
+
* Shape a resolved RGB crop into a `getNativeCrop` result. Raw path (default):
|
|
3326
|
+
* return the zero-copy `bytes` — same-node callers encode locally. Compressed
|
|
3327
|
+
* path (`encodeJpeg`, the CROSS-NODE case): encode to JPEG in-process, on the
|
|
3328
|
+
* OWNING node, and return `jpeg` (base64) with NO `bytes` — the payload then
|
|
3329
|
+
* fits Moleculer's `maxPacketSize` and no raw pixels cross the process
|
|
3330
|
+
* boundary (CLAUDE.md invariant #21). On an encode failure the crop is a miss
|
|
3331
|
+
* (`null`) — the caller retries — never a silent raw-RGB cross-node fallback.
|
|
3332
|
+
*/
|
|
3333
|
+
async encodeNativeCropResult(crop, tier, encodeJpeg) {
|
|
3334
|
+
if (encodeJpeg !== true) return {
|
|
3234
3335
|
bytes: crop.bytes,
|
|
3235
3336
|
width: crop.width,
|
|
3236
3337
|
height: crop.height,
|
|
3237
3338
|
tier
|
|
3238
|
-
}
|
|
3339
|
+
};
|
|
3340
|
+
try {
|
|
3341
|
+
const sharp$3 = (await import("sharp")).default;
|
|
3342
|
+
return {
|
|
3343
|
+
jpeg: (await sharp$3(Buffer.from(crop.bytes), { raw: {
|
|
3344
|
+
width: crop.width,
|
|
3345
|
+
height: crop.height,
|
|
3346
|
+
channels: 3
|
|
3347
|
+
} }).jpeg({ quality: 90 }).toBuffer()).toString("base64"),
|
|
3348
|
+
width: crop.width,
|
|
3349
|
+
height: crop.height,
|
|
3350
|
+
tier
|
|
3351
|
+
};
|
|
3352
|
+
} catch (err) {
|
|
3353
|
+
this.ctx.logger.warn("getNativeCrop: cross-node JPEG encode failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
3354
|
+
return null;
|
|
3355
|
+
}
|
|
3239
3356
|
}
|
|
3240
3357
|
/**
|
|
3241
3358
|
* Two-plane design: run the DETAIL subtree (crop children) for a single
|
|
@@ -3250,10 +3367,72 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3250
3367
|
return runDetailSubtree({
|
|
3251
3368
|
resolveChildSteps: (deviceId, parentClassName, filter) => this.resolveDetailChildSteps(deviceId, parentClassName, filter),
|
|
3252
3369
|
getNativeCrop: (deviceId, handle, paddedBbox) => this.getNativeCropForDetail(deviceId, handle, paddedBbox),
|
|
3253
|
-
runPipeline: (steps, imageJpeg, deviceId) => this.runDetailPipeline(steps, imageJpeg, deviceId)
|
|
3370
|
+
runPipeline: (steps, imageJpeg, deviceId, deviceKey) => this.runDetailPipeline(steps, imageJpeg, deviceId, deviceKey),
|
|
3371
|
+
resolveStepDevice: (deviceId, step) => this.resolveDetailStepDevice(deviceId, step)
|
|
3254
3372
|
}, input);
|
|
3255
3373
|
}
|
|
3256
3374
|
/**
|
|
3375
|
+
* Step-tree device jump (phase 1) — pick which same-node inference device an
|
|
3376
|
+
* enrichment `step` runs on. AUTO-jumps only when the camera's elected device
|
|
3377
|
+
* cannot run the step's model (its format has no build); an operator
|
|
3378
|
+
* `step.jumpDeviceKey` overrides. Returns the resolved `<backend>:<device>`
|
|
3379
|
+
* key, or `undefined` (⇒ the runner's per-camera default pool) when there is
|
|
3380
|
+
* nothing to route: no elected device, or no jump roster on the attach.
|
|
3381
|
+
*
|
|
3382
|
+
* When NO enabled device on the node can run the step, logs a LOUD warn once
|
|
3383
|
+
* per (camera, step) — killing the historical SILENT per-child degrade (an
|
|
3384
|
+
* `edgetpu` root would otherwise drop faces/plates/clip with no signal).
|
|
3385
|
+
*/
|
|
3386
|
+
resolveDetailStepDevice(deviceId, step) {
|
|
3387
|
+
const config = this.attached.get(deviceId)?.config;
|
|
3388
|
+
const effectiveDeviceKey = config?.deviceKey;
|
|
3389
|
+
if (!effectiveDeviceKey) return void 0;
|
|
3390
|
+
const roster = config?.inferenceDevices;
|
|
3391
|
+
if (!roster || roster.length < 2) return effectiveDeviceKey;
|
|
3392
|
+
const attachedByKey = this.perDeviceAttachedCounts();
|
|
3393
|
+
const devices = roster.map((d) => ({
|
|
3394
|
+
deviceKey: d.deviceKey,
|
|
3395
|
+
weight: d.weight,
|
|
3396
|
+
maxSessions: d.maxSessions,
|
|
3397
|
+
attachedCameras: attachedByKey.get(d.deviceKey) ?? 0
|
|
3398
|
+
}));
|
|
3399
|
+
const def = tryGetStepDefinition(step.addonId);
|
|
3400
|
+
const canRun = (format) => def === null || def.models.some((m) => m.formats[format]);
|
|
3401
|
+
const result = resolveStepDevice({
|
|
3402
|
+
effectiveDeviceKey,
|
|
3403
|
+
devices,
|
|
3404
|
+
...step.jumpDeviceKey !== void 0 ? { manualJumpDeviceKey: step.jumpDeviceKey } : {},
|
|
3405
|
+
canRun
|
|
3406
|
+
});
|
|
3407
|
+
if (result.kind === "no-candidate") this.warnStepHasNoRunnableDevice(deviceId, step.addonId, effectiveDeviceKey);
|
|
3408
|
+
return result.deviceKey;
|
|
3409
|
+
}
|
|
3410
|
+
/** Live attached-camera count per inference `deviceKey` on this runner. */
|
|
3411
|
+
perDeviceAttachedCounts() {
|
|
3412
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3413
|
+
for (const cam of this.attached.values()) {
|
|
3414
|
+
const key = cam.config.deviceKey;
|
|
3415
|
+
if (key === void 0) continue;
|
|
3416
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
3417
|
+
}
|
|
3418
|
+
return counts;
|
|
3419
|
+
}
|
|
3420
|
+
/** `${deviceId}:${addonId}` pairs already warned about a no-runnable-device step. */
|
|
3421
|
+
warnedNoJumpDevice = /* @__PURE__ */ new Set();
|
|
3422
|
+
warnStepHasNoRunnableDevice(deviceId, addonId, effectiveDeviceKey) {
|
|
3423
|
+
const key = `${deviceId}:${addonId}`;
|
|
3424
|
+
if (this.warnedNoJumpDevice.has(key)) return;
|
|
3425
|
+
this.warnedNoJumpDevice.add(key);
|
|
3426
|
+
this.ctx?.logger.warn("enrichment step has no runnable inference device on this node — producing NO results for it", {
|
|
3427
|
+
tags: { deviceId },
|
|
3428
|
+
meta: {
|
|
3429
|
+
step: addonId,
|
|
3430
|
+
electedDeviceKey: effectiveDeviceKey,
|
|
3431
|
+
hint: "the elected device format has no build of this step, and no enabled same-node device can run it — enable a compatible accelerator or add a jumpDeviceKey"
|
|
3432
|
+
}
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
/**
|
|
3257
3436
|
* Real `resolveChildSteps` dep: `null` when the camera isn't attached
|
|
3258
3437
|
* (matches the cap doc — "camera not attached" is a distinct case from
|
|
3259
3438
|
* "attached but no matching child steps", which returns an empty array
|
|
@@ -3275,16 +3454,12 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3275
3454
|
async getNativeCropForDetail(_deviceId, handle, paddedBbox) {
|
|
3276
3455
|
const native = await this.getNativeCrop({
|
|
3277
3456
|
handle,
|
|
3278
|
-
bbox: paddedBbox
|
|
3457
|
+
bbox: paddedBbox,
|
|
3458
|
+
encodeJpeg: true
|
|
3279
3459
|
});
|
|
3280
|
-
if (!native) return null;
|
|
3281
|
-
const sharp$3 = (await import("sharp")).default;
|
|
3460
|
+
if (!native?.jpeg) return null;
|
|
3282
3461
|
return {
|
|
3283
|
-
jpeg:
|
|
3284
|
-
width: native.width,
|
|
3285
|
-
height: native.height,
|
|
3286
|
-
channels: 3
|
|
3287
|
-
} }).jpeg({ quality: 90 }).toBuffer(),
|
|
3462
|
+
jpeg: Buffer.from(native.jpeg, "base64"),
|
|
3288
3463
|
width: native.width,
|
|
3289
3464
|
height: native.height,
|
|
3290
3465
|
frameSpace: denormalizeRect(paddedBbox, handle.width, handle.height)
|
|
@@ -3344,10 +3519,10 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3344
3519
|
})();
|
|
3345
3520
|
}
|
|
3346
3521
|
/** Real `runPipeline` dep: node-local `pipelineExecutor.runPipeline` on the `'full'` plane. */
|
|
3347
|
-
async runDetailPipeline(steps, imageJpeg, deviceId) {
|
|
3522
|
+
async runDetailPipeline(steps, imageJpeg, deviceId, stepDeviceKey) {
|
|
3348
3523
|
const api = this.ctxIfReady?.api;
|
|
3349
3524
|
if (!api) return null;
|
|
3350
|
-
const detailDeviceKey = this.attached.get(deviceId)?.config?.deviceKey;
|
|
3525
|
+
const detailDeviceKey = stepDeviceKey ?? this.attached.get(deviceId)?.config?.deviceKey;
|
|
3351
3526
|
try {
|
|
3352
3527
|
return await api.pipelineExecutor.runPipeline.mutate({
|
|
3353
3528
|
steps: [...steps],
|
|
@@ -3424,6 +3599,8 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3424
3599
|
this.lastDecodeUnexpectedEndAt.delete(deviceId);
|
|
3425
3600
|
this.thumbSampler?.forget(deviceId);
|
|
3426
3601
|
this.packageGovernor.forget(deviceId);
|
|
3602
|
+
const warnPrefix = `${deviceId}:`;
|
|
3603
|
+
for (const key of this.warnedNoJumpDevice) if (key.startsWith(warnPrefix)) this.warnedNoJumpDevice.delete(key);
|
|
3427
3604
|
this.attached.delete(deviceId);
|
|
3428
3605
|
this.lastMotionAt.delete(deviceId);
|
|
3429
3606
|
this.lastEmittedCameraMetrics.delete(deviceId);
|