@camstack/addon-pipeline 1.1.55 → 1.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/detection-pipeline/index.js +92 -88
  4. package/dist/detection-pipeline/index.mjs +91 -87
  5. package/dist/{dist-DI57FC8K.js → dist-CK8Ur-OS.js} +1045 -43
  6. package/dist/{dist-RWGGPwVx.mjs → dist-CL531uVA.mjs} +1045 -43
  7. package/dist/motion-wasm/index.js +9 -41
  8. package/dist/motion-wasm/index.mjs +9 -41
  9. package/dist/pipeline-runner/index.js +338 -664
  10. package/dist/pipeline-runner/index.mjs +327 -655
  11. package/dist/recorder/index.js +2 -2
  12. package/dist/recorder/index.mjs +2 -2
  13. package/dist/remote-restream-BeHi78PZ.mjs +25 -0
  14. package/dist/remote-restream-CO36Sr30.js +36 -0
  15. package/dist/session-decode/decode-worker-child.js +5 -3
  16. package/dist/session-decode/decode-worker-child.mjs +5 -3
  17. package/dist/{step-definitions-CP9kVSml.mjs → step-definitions-1d3_vQ6S.mjs} +45 -3
  18. package/dist/{step-definitions-CNBFKjZe.js → step-definitions-DHAxruAQ.js} +45 -3
  19. package/dist/stream-broker/_stub.js +351 -291
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-mDCPzmT3.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Cm_B-hQT.mjs} +2 -2
  21. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Ba7Eyd47.mjs +26 -0
  22. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dn_pxzP-.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-TWCUWhCm.mjs} +2 -2
  23. package/dist/stream-broker/{hostInit-GM_CI22k.mjs → hostInit-DEaqJpGA.mjs} +2 -2
  24. package/dist/stream-broker/index.js +935 -38
  25. package/dist/stream-broker/index.mjs +932 -35
  26. package/dist/stream-broker/remoteEntry.js +1 -1
  27. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-BChW0ntM.js → MaskShapeCanvas-DI4BY7W2-CZUzu3MF.js} +1 -1
  28. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DDzqEbSe.js → MotionZonesSettings-NcxxQN8r-CmZbzLQi.js} +1 -1
  29. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-B879GXaf.js → PrivacyMaskSettings-APgPLF7p-BOKDrH0N.js} +1 -1
  30. package/embed-dist/assets/index-Bmpl9O1K.css +2 -0
  31. package/embed-dist/assets/index-DOsK8CZb.js +81 -0
  32. package/embed-dist/index.html +2 -2
  33. package/package.json +7 -5
  34. package/python/inference_pool.py +183 -29
  35. package/python/postprocessors/__init__.py +2 -0
  36. package/python/postprocessors/ssd.py +128 -0
  37. package/python/requirements-edgetpu.txt +23 -0
  38. package/python/test_inference_pool_layout.py +16 -1
  39. package/python/test_inference_pool_preprocess.py +65 -0
  40. package/dist/remote-source-plane-CHgvzzA6.js +0 -1030
  41. package/dist/remote-source-plane-DU0aRSPv.mjs +0 -1001
  42. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B3gTdHEh.mjs +0 -26
  43. package/embed-dist/assets/index-CC06JBcl.css +0 -2
  44. package/embed-dist/assets/index-CNjQ5rAE.js +0 -81
  45. package/dist/{model-download-service-C-IHWnXx-BPy6aoAx.mjs → model-download-service-C-IHWnXx-3Mmeob3l.mjs} +1 -1
  46. package/dist/{model-download-service-C-IHWnXx-BLXzxpRB.js → model-download-service-C-IHWnXx-D326YNnt.js} +1 -1
@@ -1,14 +1,13 @@
1
1
  import { t as __require } from "../chunk-BdkLduGY.mjs";
2
- import { D as nodePin, H as createEvent, I as errMsg, L as BaseAddon, O as pipelineExecutorCapability, P as supportedRuntimes$1, U as hydrateSchema, V as EventCategory, X as sleep, b as detectionPipelineCapability, f as YAMNET_TO_MACRO, j as runtimeDevices$1, q as parseJsonUnknown, t as APPLE_SA_TO_MACRO, v as defaultDeviceFor$1, x as evaluateZoneRules } from "../dist-RWGGPwVx.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-CP9kVSml.mjs";
2
+ import { D as nodePin, H as createEvent, I as errMsg, L as BaseAddon, O as pipelineExecutorCapability, P as supportedRuntimes$1, U as hydrateSchema, V as EventCategory, X as sleep, b as detectionPipelineCapability, f as YAMNET_TO_MACRO, j as runtimeDevices$1, q as parseJsonUnknown, t as APPLE_SA_TO_MACRO, v as defaultDeviceFor$1, x as evaluateZoneRules } from "../dist-CL531uVA.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-1d3_vQ6S.mjs";
4
4
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
5
- import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-C-IHWnXx-BPy6aoAx.mjs";
6
- import { FrameRingReaderCache } from "@camstack/shm-ring";
5
+ import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
7
6
  import { spawn } from "node:child_process";
7
+ import sharp from "sharp";
8
8
  import * as fs from "node:fs";
9
9
  import * as path$1 from "node:path";
10
10
  import * as os from "node:os";
11
- import sharp from "sharp";
12
11
  //#region src/detection-pipeline/runtimes.ts
13
12
  var KNOWN_PLATFORMS = [
14
13
  "darwin",
@@ -44,6 +43,9 @@ function npuInfoFrom(hw) {
44
43
  if (!type) return null;
45
44
  return { type };
46
45
  }
46
+ function coralInfoFrom(hw) {
47
+ return hw.coral?.type === "coral-edgetpu" ? { type: "coral-edgetpu" } : null;
48
+ }
47
49
  function envToHardwareInfo(env) {
48
50
  return {
49
51
  platform: toKnownPlatform(env.platform),
@@ -53,7 +55,8 @@ function envToHardwareInfo(env) {
53
55
  totalRAM_MB: 0,
54
56
  availableRAM_MB: 0,
55
57
  gpu: env.hardware ? gpuInfoFrom(env.hardware) : null,
56
- npu: env.hardware ? npuInfoFrom(env.hardware) : null
58
+ npu: env.hardware ? npuInfoFrom(env.hardware) : null,
59
+ coral: env.hardware ? coralInfoFrom(env.hardware) : null
57
60
  };
58
61
  }
59
62
  function probedToHardwareInfo(hw) {
@@ -66,7 +69,8 @@ function probedToHardwareInfo(hw) {
66
69
  totalRAM_MB: 0,
67
70
  availableRAM_MB: 0,
68
71
  gpu: gpuInfoFrom(hw),
69
- npu: npuInfoFrom(hw)
72
+ npu: npuInfoFrom(hw),
73
+ coral: coralInfoFrom(hw)
70
74
  };
71
75
  }
72
76
  var RUNTIME_DETAIL = {
@@ -99,6 +103,15 @@ var RUNTIME_DETAIL = {
99
103
  maxBatchSize: 8,
100
104
  numWorkers: 1
101
105
  }
106
+ },
107
+ edgetpu: {
108
+ label: "Coral Edge TPU",
109
+ pythonRequirements: ["requirements.txt", "requirements-edgetpu.txt"],
110
+ tuning: {
111
+ concurrency: 1,
112
+ batchMode: "none",
113
+ numWorkers: 1
114
+ }
102
115
  }
103
116
  };
104
117
  /**
@@ -126,11 +139,12 @@ function defaultDeviceFor(id) {
126
139
  var RUNTIME_FORMAT = {
127
140
  onnx: "onnx",
128
141
  openvino: "openvino",
129
- coreml: "coreml"
142
+ coreml: "coreml",
143
+ edgetpu: "tflite"
130
144
  };
131
145
  /**
132
146
  * Returns the model format required for a given runtime.
133
- * Returns the locally-typed format string ('onnx' | 'openvino' | 'coreml')
147
+ * Returns the locally-typed format string ('onnx' | 'openvino' | 'coreml' | 'tflite')
134
148
  * matching the engine-provisioner's own ModelFormat type.
135
149
  */
136
150
  function modelFormatFor(id) {
@@ -159,6 +173,7 @@ function shouldInstallOpenvino(env) {
159
173
  var PREFERENCE = [
160
174
  "coreml",
161
175
  "openvino",
176
+ "edgetpu",
162
177
  "onnx"
163
178
  ];
164
179
  /**
@@ -928,6 +943,9 @@ function serializeModelConfig(config) {
928
943
  postprocessor: config.postprocessor,
929
944
  confidence: config.confidence
930
945
  };
946
+ if (config.inputWidth !== void 0) result["inputWidth"] = config.inputWidth;
947
+ if (config.inputHeight !== void 0) result["inputHeight"] = config.inputHeight;
948
+ if (config.inputChannels !== void 0) result["inputChannels"] = config.inputChannels;
931
949
  if (config.labels) result["labels"] = config.labels;
932
950
  if (config.charset) result["charset"] = config.charset;
933
951
  if (config.numClasses) result["numClasses"] = config.numClasses;
@@ -1223,12 +1241,14 @@ var BACKEND_TO_POOL_RUNTIME = {
1223
1241
  openvino: "openvino",
1224
1242
  onnx: "onnxruntime",
1225
1243
  cpu: "onnxruntime",
1226
- cuda: "onnxruntime"
1244
+ cuda: "onnxruntime",
1245
+ edgetpu: "edgetpu"
1227
1246
  };
1228
1247
  var RUNTIME_TO_FORMAT = {
1229
1248
  coreml: "coreml",
1230
1249
  openvino: "openvino",
1231
- onnxruntime: "onnx"
1250
+ onnxruntime: "onnx",
1251
+ edgetpu: "tflite"
1232
1252
  };
1233
1253
  var EngineFactory = class {
1234
1254
  pool = null;
@@ -1347,13 +1367,15 @@ var EngineFactory = class {
1347
1367
  const concurrency = this.opts.concurrency ?? {
1348
1368
  coreml: 1,
1349
1369
  openvino: 1,
1350
- onnxruntime: 4
1370
+ onnxruntime: 4,
1371
+ edgetpu: 1
1351
1372
  }[poolRuntime];
1352
1373
  const isAutoDefault = this.opts.concurrency === void 0;
1353
1374
  const defaultBatchMode = {
1354
1375
  coreml: "window",
1355
1376
  openvino: "none",
1356
- onnxruntime: "list"
1377
+ onnxruntime: "list",
1378
+ edgetpu: "none"
1357
1379
  };
1358
1380
  const tuning = this.opts.tuning ?? {};
1359
1381
  const resolvedTuning = {
@@ -1391,7 +1413,10 @@ var EngineFactory = class {
1391
1413
  const urlParts = formatEntry.url.split("/");
1392
1414
  const filename = urlParts[urlParts.length - 1] ?? `${modelId}.${format}`;
1393
1415
  const modelPath = `${this.opts.modelsDir}/${filename}`;
1394
- const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height);
1416
+ const inputWidth = modelEntry.inputSize.width;
1417
+ const inputHeight = modelEntry.inputSize.height;
1418
+ const inputSize = Math.max(inputWidth, inputHeight);
1419
+ const inputChannels = modelEntry.inputChannels ?? 3;
1395
1420
  let labels = def.labels;
1396
1421
  if (!labels && modelEntry.extraFiles) {
1397
1422
  const labelsFile = modelEntry.extraFiles.find((f) => f.filename.endsWith("-labels.json"));
@@ -1403,8 +1428,11 @@ var EngineFactory = class {
1403
1428
  return {
1404
1429
  path: modelPath,
1405
1430
  inputSize,
1431
+ inputWidth,
1432
+ inputHeight,
1433
+ inputChannels,
1406
1434
  preprocessMode: modelEntry.preprocessMode ?? "letterbox",
1407
- postprocessor: def.postprocessor,
1435
+ postprocessor: modelEntry.postprocessor ?? def.postprocessor,
1408
1436
  confidence: def.defaultConfidence,
1409
1437
  labels,
1410
1438
  charset: def.charset,
@@ -1603,6 +1631,11 @@ function postprocessYolo(output, stepDef) {
1603
1631
  detections: simpleNms(dets, .45)
1604
1632
  };
1605
1633
  }
1634
+ function postprocessSsd(output, _stepDef) {
1635
+ const structured = tryStructured(output);
1636
+ if (structured) return structured;
1637
+ throw new Error("SSD postprocessing runs in the Python inference pool (ssd.py); no TypeScript raw-tensor path.");
1638
+ }
1606
1639
  function postprocessScrfd(output, stepDef) {
1607
1640
  const structured = tryStructured(output);
1608
1641
  if (structured) return structured;
@@ -2025,6 +2058,7 @@ function postprocessYamnet(output, _stepDef) {
2025
2058
  }
2026
2059
  var DISPATCH = {
2027
2060
  yolo: postprocessYolo,
2061
+ ssd: postprocessSsd,
2028
2062
  "yolo-seg": postprocessYoloSeg,
2029
2063
  scrfd: postprocessScrfd,
2030
2064
  arcface: postprocessArcface,
@@ -2835,14 +2869,17 @@ var MIN_FACE_DETECTION_FLOOR_PX = 6;
2835
2869
  var NATIVE_FACE_CROP_MAX_WIDTH = 320;
2836
2870
  /**
2837
2871
  * StepOutput kinds that synthesize a full-frame detection when they run at
2838
- * the tree ROOT (see `synthesizeRootDetection`) — classifier/embedding steps
2839
- * promoted to root by `pipelineRunner.runDetailSubtree` (task 6b), e.g.
2840
- * `clip-embedding`, `animal-classifier`. Detector (`'detections'`) roots take
2841
- * the normal path; `'text'`/`'mask'` roots are out of scope and still
2842
- * `continue` with no output, matching pre-existing behaviour.
2872
+ * the tree ROOT (see `synthesizeRootDetection`) — classifier/embedding/OCR
2873
+ * steps promoted to root by `pipelineRunner.runDetailSubtree` (task 6b), e.g.
2874
+ * `clip-embedding`, `animal-classifier`, `plate-ocr`. Detector (`'detections'`)
2875
+ * roots take the normal path. `'text'` is included so a `plate-ocr` step run
2876
+ * standalone on an already-cut plate crop (parent class `plate`) synthesizes a
2877
+ * full-frame detection carrying the recognized text as a label, instead of
2878
+ * silently dropping — the OCR chain otherwise only surfaces text when nested
2879
+ * under `plate-detection`. `'mask'` roots remain out of scope.
2843
2880
  */
2844
2881
  function isEnrichmentOutput(output) {
2845
- return output.kind === "classifications" || output.kind === "embedding";
2882
+ return output.kind === "classifications" || output.kind === "embedding" || output.kind === "text";
2846
2883
  }
2847
2884
  var PipelineExecutor = class {
2848
2885
  opts;
@@ -2852,12 +2889,19 @@ var PipelineExecutor = class {
2852
2889
  /** Per-key last-warn timestamps, so a persistently-failing/skipping child
2853
2890
  * step surfaces a diagnostic without spamming the log every frame. */
2854
2891
  warnThrottleAt = /* @__PURE__ */ new Map();
2855
- throttledWarn(key, everyMs, message, meta) {
2892
+ throttledWarn(key, everyMs, message, meta, deviceId) {
2856
2893
  const logger = this.opts.logger;
2857
2894
  if (!logger) return;
2858
2895
  const now = Date.now();
2859
2896
  if (now - (this.warnThrottleAt.get(key) ?? 0) < everyMs) return;
2860
2897
  this.warnThrottleAt.set(key, now);
2898
+ if (deviceId !== void 0) {
2899
+ logger.warn(message, {
2900
+ tags: { deviceId },
2901
+ meta
2902
+ });
2903
+ return;
2904
+ }
2861
2905
  logger.warn(message, { meta });
2862
2906
  }
2863
2907
  /**
@@ -2867,15 +2911,15 @@ var PipelineExecutor = class {
2867
2911
  * native crop, the NATIVE face size so an operator sees the size that actually
2868
2912
  * gated. Throttled so it isn't per-frame spam.
2869
2913
  */
2870
- warnFaceTooSmall(child, detShortSidePx, minFaceSizePx, nativeFaceShortSidePx) {
2871
- this.throttledWarn(`face-small:${child.stepId}`, 3e4, "face detected but too small to embed — skipping", {
2914
+ warnFaceTooSmall(child, detShortSidePx, minFaceSizePx, nativeFaceShortSidePx, deviceId) {
2915
+ this.throttledWarn(`face-small:${child.stepId}:${deviceId}`, 3e4, "face detected but too small to embed — skipping", {
2872
2916
  step: child.stepId,
2873
2917
  model: child.modelId,
2874
2918
  faceShortSidePx: Math.round(detShortSidePx),
2875
2919
  minFaceSizePx,
2876
2920
  gatedOn: nativeFaceShortSidePx === void 0 ? "detection" : "native",
2877
2921
  ...nativeFaceShortSidePx === void 0 ? {} : { nativeFaceShortSidePx: Math.round(nativeFaceShortSidePx) }
2878
- });
2922
+ }, deviceId);
2879
2923
  }
2880
2924
  /**
2881
2925
  * Execute the full pipeline tree against a frame.
@@ -2918,7 +2962,7 @@ var PipelineExecutor = class {
2918
2962
  const mutable = this.synthesizeRootDetection(rootOutput, rootStep, idGen, rootMs, imageWidth, imageHeight);
2919
2963
  applyChildOutput(mutable, rootStep, rootOutput, rootMs, ctx);
2920
2964
  try {
2921
- await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, nativeFaceCropProvider);
2965
+ await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeFaceCropProvider);
2922
2966
  } catch (err) {
2923
2967
  this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
2924
2968
  tags: { deviceId },
@@ -2948,7 +2992,7 @@ var PipelineExecutor = class {
2948
2992
  continue;
2949
2993
  }
2950
2994
  try {
2951
- await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, nativeFaceCropProvider);
2995
+ await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeFaceCropProvider);
2952
2996
  } catch (err) {
2953
2997
  this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
2954
2998
  tags: { deviceId },
@@ -3073,7 +3117,7 @@ var PipelineExecutor = class {
3073
3117
  });
3074
3118
  return output;
3075
3119
  }
3076
- async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, plane, nativeFaceCropProvider) {
3120
+ async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, plane, deviceId, nativeFaceCropProvider) {
3077
3121
  for (const child of children) {
3078
3122
  if (plane === "frame" && child.definition.inputClasses !== null) continue;
3079
3123
  if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
@@ -3091,13 +3135,13 @@ var PipelineExecutor = class {
3091
3135
  const minFaceSize = typeof child.settings?.["minFaceSize"] === "number" ? child.settings["minFaceSize"] : DEFAULT_MIN_FACE_SIZE_PX;
3092
3136
  const detShortSide = bboxShortSide(parentDetection.bbox);
3093
3137
  if (detShortSide < MIN_FACE_DETECTION_FLOOR_PX) {
3094
- this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0);
3138
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0, deviceId);
3095
3139
  continue;
3096
3140
  }
3097
3141
  const lms = parentDetection.landmarks;
3098
3142
  if (lms && lms.length >= 5) {
3099
3143
  if (!nativeFaceCropProvider && detShortSide < minFaceSize) {
3100
- this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0);
3144
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0, deviceId);
3101
3145
  continue;
3102
3146
  }
3103
3147
  const aligned = await buildAlignedFaceCrop({
@@ -3114,7 +3158,7 @@ var PipelineExecutor = class {
3114
3158
  });
3115
3159
  const gateShortSide = aligned.nativeFaceShortSidePx ?? detShortSide;
3116
3160
  if (gateShortSide < minFaceSize) {
3117
- this.warnFaceTooSmall(child, detShortSide, minFaceSize, aligned.source === "native" ? gateShortSide : void 0);
3161
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, aligned.source === "native" ? gateShortSide : void 0, deviceId);
3118
3162
  continue;
3119
3163
  }
3120
3164
  cropJpegBuf = aligned.jpeg;
@@ -3123,7 +3167,7 @@ var PipelineExecutor = class {
3123
3167
  parentDetection.faceAlignedCrop = cropJpegBuf.toString("base64");
3124
3168
  } else {
3125
3169
  if (detShortSide < minFaceSize) {
3126
- this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0);
3170
+ this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0, deviceId);
3127
3171
  continue;
3128
3172
  }
3129
3173
  const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight);
@@ -3155,15 +3199,15 @@ var PipelineExecutor = class {
3155
3199
  y: l.y + py1
3156
3200
  }));
3157
3201
  }
3158
- await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, plane, nativeFaceCropProvider);
3202
+ await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, plane, deviceId, nativeFaceCropProvider);
3159
3203
  }
3160
3204
  }
3161
3205
  } catch (err) {
3162
- this.throttledWarn(`child-fail:${child.stepId}`, 15e3, "pipeline child step failed", {
3206
+ this.throttledWarn(`child-fail:${child.stepId}:${deviceId}`, 15e3, "pipeline child step failed", {
3163
3207
  step: child.stepId,
3164
3208
  model: child.modelId,
3165
3209
  error: errMsg(err)
3166
- });
3210
+ }, deviceId);
3167
3211
  }
3168
3212
  }
3169
3213
  }
@@ -3618,7 +3662,8 @@ function toProbedHardware(hw) {
3618
3662
  if (!hw) return null;
3619
3663
  return {
3620
3664
  npu: hw.npu ? { type: hw.npu.type } : null,
3621
- gpu: hw.gpu ? { type: hw.gpu.type } : null
3665
+ gpu: hw.gpu ? { type: hw.gpu.type } : null,
3666
+ coral: hw.coral ? { type: hw.coral.type } : null
3622
3667
  };
3623
3668
  }
3624
3669
  var ONNX_FLOOR = {
@@ -3724,16 +3769,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3724
3769
  */
3725
3770
  loggedLiveDispatchIssues = /* @__PURE__ */ new Set();
3726
3771
  /**
3727
- * CB5 shm passthrough — the process-local, LONG-LIVED reader cache for
3728
- * `runPipeline({ frameHandle })`. Opens each named shm segment ONCE and
3729
- * reuses the mapping for every later handle on the same ring (never
3730
- * open/close per-call). Lazily created on first handle, unmapped in
3731
- * {@link shutdown}. The executor and the runner are both hub-local
3732
- * processes sharing `/dev/shm`, so a LOCAL handle resolves to a zero-copy
3733
- * read here.
3734
- */
3735
- frameHandleReaders = null;
3736
- /**
3737
3772
  * Lazy detection-engine runtime provisioner (Phase 2). Owns the
3738
3773
  * idle → installing → verifying → ready state machine for the
3739
3774
  * currently-selected engine and is the SOLE authority the inference
@@ -3930,14 +3965,15 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3930
3965
  */
3931
3966
  resolvedEngineBackend() {
3932
3967
  const backend = this.currentEngine.backend;
3933
- return backend === "openvino" || backend === "coreml" ? backend : "onnx";
3968
+ return backend === "openvino" || backend === "coreml" || backend === "edgetpu" ? backend : "onnx";
3934
3969
  }
3935
3970
  /** Map a backend string to a known RuntimeId, flooring to onnx. */
3936
3971
  toRuntimeId(backend) {
3937
3972
  return [
3938
3973
  "onnx",
3939
3974
  "openvino",
3940
- "coreml"
3975
+ "coreml",
3976
+ "edgetpu"
3941
3977
  ].find((id) => id === backend) ?? "onnx";
3942
3978
  }
3943
3979
  /**
@@ -4600,35 +4636,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
4600
4636
  } });
4601
4637
  return { success: true };
4602
4638
  }
4603
- /** This executor's cluster node id (suffix-stripped, matching the runner). */
4604
- ownNodeId() {
4605
- const raw = this.addonCtx?.kernel?.localNodeId ?? "hub";
4606
- return raw.includes("/") ? raw.split("/")[0] : raw;
4607
- }
4608
- /**
4609
- * CB5 — resolve a shm `FrameHandle` to pixels zero-copy. Returns `null`
4610
- * when:
4611
- * - the handle is FOREIGN (its segment lives on another node — a local
4612
- * `shm_open` would flood ENOENT; the runner only sends LOCAL handles, so
4613
- * this is a defensive guard against an unexpected topology);
4614
- * - the ring slot was recycled before the read (latest-wins drop) or the
4615
- * segment could not be mapped.
4616
- * A `null` degrades the caller to an empty `FrameResult` — the runner ships
4617
- * pixels via `frame` as the fallback.
4618
- */
4619
- resolveFrameHandle(handle) {
4620
- if (handle.nodeId !== this.ownNodeId()) return null;
4621
- if (!this.frameHandleReaders) this.frameHandleReaders = new FrameRingReaderCache(this.log);
4622
- const decoded = this.frameHandleReaders.read(handle);
4623
- if (!decoded) return null;
4624
- return {
4625
- data: decoded.data,
4626
- width: decoded.width,
4627
- height: decoded.height,
4628
- format: decoded.format,
4629
- timestamp: decoded.timestamp
4630
- };
4631
- }
4632
4639
  /**
4633
4640
  * Minimal detection-free `FrameResult` returned when a `frameHandle` read
4634
4641
  * misses (recycled slot / foreign / unmappable). Shape-identical to a
@@ -4721,12 +4728,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
4721
4728
  ].filter((s) => s !== null);
4722
4729
  if (sources.length === 0) throw new Error("runPipeline requires exactly one of: frame, frameHandle, image, imageBase64, referenceImage");
4723
4730
  if (sources.length > 1) throw new Error(`runPipeline received conflicting image sources: ${sources.join(", ")}`);
4724
- let handleFrame = null;
4725
- if (input.frameHandle) {
4726
- handleFrame = this.resolveFrameHandle(input.frameHandle);
4727
- if (!handleFrame) return this.emptyFrameHandleResult(input);
4728
- }
4729
- const runtimeFrame = input.frame ?? handleFrame ?? void 0;
4731
+ if (input.frameHandle) return this.emptyFrameHandleResult(input);
4732
+ const runtimeFrame = input.frame ?? void 0;
4730
4733
  const decodeT0 = performance.now();
4731
4734
  if (runtimeFrame) {
4732
4735
  const frame = runtimeFrame;
@@ -5481,8 +5484,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5481
5484
  this.executor = null;
5482
5485
  }
5483
5486
  for (const id of Array.from(this.deviceProxies.keys())) this.releaseDeviceProxy(id);
5484
- this.frameHandleReaders?.close();
5485
- this.frameHandleReaders = null;
5486
5487
  }
5487
5488
  /**
5488
5489
  * Resolve and cache a {@link DeviceProxy} for the given camera. Pins
@@ -6218,7 +6219,8 @@ var BACKEND_TO_FORMAT = {
6218
6219
  cpu: "onnx",
6219
6220
  coreml: "coreml",
6220
6221
  openvino: "openvino",
6221
- onnx: "onnx"
6222
+ onnx: "onnx",
6223
+ edgetpu: "tflite"
6222
6224
  };
6223
6225
  /**
6224
6226
  * Every runtime the registry knows about, in offer order. Used only to build
@@ -6229,7 +6231,8 @@ var BACKEND_TO_FORMAT = {
6229
6231
  var ALL_RUNTIME_IDS = [
6230
6232
  "onnx",
6231
6233
  "openvino",
6232
- "coreml"
6234
+ "coreml",
6235
+ "edgetpu"
6233
6236
  ];
6234
6237
  /**
6235
6238
  * Narrow an arbitrary stored backend string to a known `RuntimeId`. Legacy or
@@ -6697,6 +6700,7 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends BaseAddon {
6697
6700
  return {
6698
6701
  npu: hw.npu ? { type: hw.npu.type } : null,
6699
6702
  gpu: hw.gpu ? { type: hw.gpu.type } : null,
6703
+ coral: hw.coral ? { type: hw.coral.type } : null,
6700
6704
  platform: hw.platform,
6701
6705
  arch: hw.arch
6702
6706
  };