@camstack/addon-pipeline 1.0.1 → 1.0.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.
Files changed (31) hide show
  1. package/dist/audio-analyzer/index.js +5 -5
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/audio-codec-nodeav/index.js +1 -1
  4. package/dist/audio-codec-nodeav/index.mjs +1 -1
  5. package/dist/decoder-nodeav/index.js +1 -1
  6. package/dist/decoder-nodeav/index.mjs +1 -1
  7. package/dist/detection-pipeline/index.js +276 -61
  8. package/dist/detection-pipeline/index.mjs +268 -55
  9. package/dist/{dist-7ewQjTle.js → dist-C1goFC50.js} +4 -4
  10. package/dist/{dist-C5jnNl0n.mjs → dist-XRXnZrVC.mjs} +4 -4
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/pipeline-runner/index.js +1 -1
  14. package/dist/pipeline-runner/index.mjs +1 -1
  15. package/dist/recorder/index.js +3 -3
  16. package/dist/recorder/index.mjs +2 -2
  17. package/dist/stream-broker/_stub.js +1 -1
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-qX99--rF.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-dGO6_Xee.mjs} +3 -3
  19. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B0Z2W5UM.mjs +26 -0
  20. package/dist/stream-broker/{hostInit-Bx41KdYV.mjs → hostInit-ClKL4kbX.mjs} +3 -3
  21. package/dist/stream-broker/index.js +3 -3
  22. package/dist/stream-broker/index.mjs +2 -2
  23. package/dist/stream-broker/remoteEntry.js +1 -1
  24. package/embed-dist/assets/MaskShapeCanvas-DI4BY7W2-DEYqSd2k.js +33 -0
  25. package/embed-dist/assets/MotionZonesSettings-C1EEbk2V-CRvYkSju.js +1 -0
  26. package/embed-dist/assets/PrivacyMaskSettings-APgPLF7p-CIq73-7y.js +1 -0
  27. package/embed-dist/assets/index-C5Az4io-.js +80 -0
  28. package/embed-dist/index.html +1 -1
  29. package/package.json +3 -3
  30. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C9WX5HNw.mjs +0 -26
  31. package/embed-dist/assets/index-B8VlSD0-.js +0 -150
@@ -1,9 +1,9 @@
1
1
  import { t as __require } from "../chunk-BdkLduGY.mjs";
2
- import { A as detectionPipelineCapability, E as createEvent, M as evaluateZoneRules, N as hfModelUrl, P as hydrateSchema, U as pipelineExecutorCapability, V as parseJsonUnknown, i as BaseAddon, j as errMsg, o as COCO_80_LABELS, p as EventCategory, q as sleep, r as AUDIO_MACRO_LABELS, s as COCO_TO_MACRO, t as APPLE_SA_TO_MACRO, v as YAMNET_TO_MACRO } from "../dist-C5jnNl0n.mjs";
2
+ import { A as detectionPipelineCapability, E as createEvent, M as evaluateZoneRules, N as hfModelUrl, P as hydrateSchema, U as pipelineExecutorCapability, V as parseJsonUnknown, i as BaseAddon, j as errMsg, o as COCO_80_LABELS, p as EventCategory, q as sleep, r as AUDIO_MACRO_LABELS, s as COCO_TO_MACRO, t as APPLE_SA_TO_MACRO, v as YAMNET_TO_MACRO } from "../dist-XRXnZrVC.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import * as path$1 from "node:path";
5
5
  import * as os from "node:os";
6
- import { deleteModelFromDisk, ensureModel, isModelDownloaded } from "@camstack/core";
6
+ import { deleteModelFromDisk, ensureModel, isModelDownloaded } from "@camstack/system";
7
7
  import { spawn } from "node:child_process";
8
8
  import sharp from "sharp";
9
9
  //#region src/detection-pipeline/engine/shared-inference-pool.ts
@@ -3865,6 +3865,18 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3865
3865
  device: "cuda"
3866
3866
  };
3867
3867
  } catch {}
3868
+ try {
3869
+ const fsmod = __require("node:fs");
3870
+ const isIntel = __require("node:os").cpus()[0]?.model?.includes("Intel") ?? false;
3871
+ const hasIgpu = fsmod.existsSync("/dev/dri/renderD128");
3872
+ const hasNpu = fsmod.existsSync("/dev/accel/accel0") || fsmod.existsSync("/dev/accel");
3873
+ if (isIntel && (hasIgpu || hasNpu)) return {
3874
+ runtime: "python",
3875
+ backend: "openvino",
3876
+ format: "openvino",
3877
+ device: "auto"
3878
+ };
3879
+ } catch {}
3868
3880
  return {
3869
3881
  runtime: "python",
3870
3882
  backend: "onnx",
@@ -3876,22 +3888,68 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3876
3888
  async setApi(addonCtx) {
3877
3889
  this.addonCtx = addonCtx;
3878
3890
  }
3891
+ /**
3892
+ * Fetch platform-probe data for engine + device gating. Returns null for
3893
+ * both fields when the probe cap is not yet reachable (cold-start / probe
3894
+ * addon not installed), so callers fall back to the full static catalog.
3895
+ */
3896
+ async fetchProbeGatingData() {
3897
+ try {
3898
+ const api = this.addonCtx?.api;
3899
+ if (!api) return {
3900
+ availableBackends: null,
3901
+ hardware: null
3902
+ };
3903
+ const caps = await api.platformProbe.getCapabilities.query();
3904
+ if (!caps?.scores) return {
3905
+ availableBackends: null,
3906
+ hardware: null
3907
+ };
3908
+ return {
3909
+ availableBackends: caps.scores.filter((s) => s.runtime === "python" && s.available).map((s) => s.backend),
3910
+ hardware: caps.hardware ?? null
3911
+ };
3912
+ } catch {
3913
+ return {
3914
+ availableBackends: null,
3915
+ hardware: null
3916
+ };
3917
+ }
3918
+ }
3879
3919
  async getSchema(engine) {
3880
3920
  if (!engine || !engine.runtime) engine = await this.getSelectedEngine();
3881
3921
  const format = engine.format;
3882
3922
  const slots = buildSchemaSlots(format, this.modelsDir);
3923
+ const { availableBackends, hardware } = await this.fetchProbeGatingData();
3883
3924
  return {
3884
- availableEngines: this.getAvailableEnginesWithDevices(),
3925
+ availableEngines: this.getAvailableEnginesWithDevices(availableBackends, hardware),
3885
3926
  selectedEngine: { ...engine },
3886
3927
  slots
3887
3928
  };
3888
3929
  }
3889
3930
  async getAvailableEngines() {
3890
- return this.getAvailableEnginesWithDevices().map((e) => e.engine);
3931
+ const { availableBackends, hardware } = await this.fetchProbeGatingData();
3932
+ return this.getAvailableEnginesWithDevices(availableBackends, hardware).map((e) => e.engine);
3891
3933
  }
3892
- getAvailableEnginesWithDevices() {
3893
- const engines = [];
3894
- if (process.platform === "darwin") engines.push({
3934
+ /**
3935
+ * Build the engine catalog filtered by probe data.
3936
+ *
3937
+ * When `availableBackends` is not null, only engines whose backend is
3938
+ * reported as available by the platform-probe cap are included. This
3939
+ * prevents the benchmark picker from offering (e.g.) CoreML on an Intel
3940
+ * Linux host where the Python coremltools package is absent.
3941
+ *
3942
+ * When `hardware` is not null, the per-engine device list is narrowed to
3943
+ * entries that are valid on this host's hardware (no Intel NPU → no 'npu'
3944
+ * device for OpenVINO, no NVIDIA GPU → no 'cuda' for ONNX, etc.).
3945
+ *
3946
+ * When either argument is null (probe unreachable / cold-start), the full
3947
+ * static catalog is returned unchanged — conservative fallback so the UI
3948
+ * still renders all options rather than going blank.
3949
+ */
3950
+ getAvailableEnginesWithDevices(availableBackends, hardware) {
3951
+ const allEngines = [];
3952
+ if (process.platform === "darwin") allEngines.push({
3895
3953
  engine: {
3896
3954
  runtime: "python",
3897
3955
  backend: "coreml",
@@ -3900,7 +3958,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3900
3958
  devices: COREML_DEVICES,
3901
3959
  defaultDevice: "all"
3902
3960
  });
3903
- engines.push({
3961
+ allEngines.push({
3904
3962
  engine: {
3905
3963
  runtime: "python",
3906
3964
  backend: "openvino",
@@ -3909,7 +3967,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3909
3967
  devices: OPENVINO_DEVICES,
3910
3968
  defaultDevice: "auto"
3911
3969
  });
3912
- engines.push({
3970
+ allEngines.push({
3913
3971
  engine: {
3914
3972
  runtime: "python",
3915
3973
  backend: "onnx",
@@ -3918,7 +3976,19 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
3918
3976
  devices: ONNX_PYTHON_DEVICES,
3919
3977
  defaultDevice: "cpu"
3920
3978
  });
3921
- return engines;
3979
+ if (!availableBackends) return allEngines;
3980
+ return allEngines.filter((e) => availableBackends.includes(e.engine.backend)).map((e) => {
3981
+ if (!hardware) return e;
3982
+ const filtered = filterDeviceOptionsByHardware(e.devices.map((d) => ({
3983
+ value: d.id,
3984
+ label: d.label
3985
+ })), e.engine.backend, hardware);
3986
+ const allowedIds = new Set(filtered.map((o) => o.value));
3987
+ return {
3988
+ ...e,
3989
+ devices: e.devices.filter((d) => allowedIds.has(d.id))
3990
+ };
3991
+ });
3922
3992
  }
3923
3993
  async getDefaultSteps(engine) {
3924
3994
  return buildDefaultStepTree(engine.format);
@@ -4010,7 +4080,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
4010
4080
  };
4011
4081
  })
4012
4082
  }));
4013
- const engines = this.getAvailableEnginesWithDevices();
4083
+ const { availableBackends, hardware } = await this.fetchProbeGatingData();
4084
+ const engines = this.getAvailableEnginesWithDevices(availableBackends, hardware);
4014
4085
  const nodeBackends = [];
4015
4086
  const pythonBackends = [];
4016
4087
  for (const e of engines) if (e.engine.runtime === "node") nodeBackends.push({
@@ -5111,19 +5182,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5111
5182
  */
5112
5183
  async loadEngine() {
5113
5184
  const store = await this.readStore();
5114
- const runtime = store["engineRuntime"];
5115
- const backend = store["engineBackend"];
5116
- if (typeof runtime === "string" && typeof backend === "string" && runtime && backend) {
5185
+ const storedRuntime = store["engineRuntime"];
5186
+ const storedBackend = store["engineBackend"];
5187
+ if (typeof storedBackend === "string" && storedBackend.length > 0) {
5188
+ const backend = storedBackend;
5117
5189
  const storedDevice = typeof store["engineDevice"] === "string" ? String(store["engineDevice"]) : "";
5118
5190
  const detected = DetectionPipelineProvider.detectBestEngine();
5119
- if (runtime === "python" && !DetectionPipelineProvider.isPythonBackendAvailable(backend, this.executorOptions.pythonPath ?? "")) {
5191
+ const migratedBackend = typeof storedRuntime === "string" && storedRuntime === "node" && backend === "cpu" ? "onnx" : backend;
5192
+ if (!DetectionPipelineProvider.isPythonBackendAvailable(migratedBackend, this.executorOptions.pythonPath ?? "")) {
5120
5193
  this.log.warn("Stored engine backend unavailable on this node — falling back to detected best", { meta: {
5121
- stored: `${runtime}/${backend}`,
5122
- fallback: `${detected.runtime}/${detected.backend}`
5194
+ stored: migratedBackend,
5195
+ fallback: `${detected.backend}`
5123
5196
  } });
5124
5197
  return detected;
5125
5198
  }
5126
- const migratedBackend = runtime === "node" && backend === "cpu" ? "onnx" : backend;
5127
5199
  const device = storedDevice || detected.device;
5128
5200
  return {
5129
5201
  runtime: "python",
@@ -5293,11 +5365,42 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5293
5365
  return { success: true };
5294
5366
  }
5295
5367
  async reprobeEngine() {
5296
- const best = DetectionPipelineProvider.detectBestEngine();
5297
- const probedLabel = `${best.runtime}/${best.backend}/${best.device ?? "default"}`;
5298
- await this.writeStore({ probedBestEngine: probedLabel });
5299
- this.log.info("Re-probed engine", { meta: {
5300
- runtime: best.runtime,
5368
+ const api = this.addonCtx?.api;
5369
+ let best;
5370
+ if (api) try {
5371
+ const caps = await api.platformProbe.getCapabilities.query();
5372
+ const bs = caps?.bestScore;
5373
+ if (bs && bs.runtime === "python") {
5374
+ const probeBackend = bs.backend;
5375
+ const probeDevice = (() => {
5376
+ const hw = caps.hardware;
5377
+ if (probeBackend === "coreml") return hw?.npu?.type === "apple-ane" ? "ane" : "all";
5378
+ if (probeBackend === "openvino") {
5379
+ if (hw?.npu?.type === "intel-npu") return "npu";
5380
+ if (hw?.gpu?.type === "intel") return "gpu";
5381
+ return "auto";
5382
+ }
5383
+ if (probeBackend === "onnx") return hw?.gpu?.type === "nvidia" ? "cuda" : "cpu";
5384
+ return "cpu";
5385
+ })();
5386
+ best = {
5387
+ runtime: "python",
5388
+ backend: probeBackend,
5389
+ format: backendToFormat(probeBackend),
5390
+ device: probeDevice
5391
+ };
5392
+ } else best = DetectionPipelineProvider.detectBestEngine();
5393
+ } catch {
5394
+ best = DetectionPipelineProvider.detectBestEngine();
5395
+ }
5396
+ else best = DetectionPipelineProvider.detectBestEngine();
5397
+ const probedLabel = `${best.backend}/${best.device ?? "default"}`;
5398
+ await this.writeStore({
5399
+ probedBestEngine: probedLabel,
5400
+ engineBackend: best.backend,
5401
+ engineDevice: best.device ?? "cpu"
5402
+ });
5403
+ this.log.info("Re-probed engine — wrote back engineBackend + engineDevice", { meta: {
5301
5404
  backend: best.backend,
5302
5405
  device: best.device ?? null,
5303
5406
  probedBestEngine: probedLabel
@@ -5693,10 +5796,34 @@ function resolveAddonPythonDir() {
5693
5796
  for (const c of candidates) if (fs.existsSync(path$1.join(c, "inference_pool.py"))) return c;
5694
5797
  throw new Error(`addon-pipeline/detection-pipeline: python/ dir not found. Searched:\n${candidates.join("\n")}`);
5695
5798
  }
5696
- var RUNTIMES = [{
5697
- value: "python",
5698
- label: "Python (CoreML / OpenVINO / ONNX Runtime)"
5699
- }];
5799
+ /**
5800
+ * Returns true when proactive OpenVINO installation is warranted.
5801
+ *
5802
+ * Gate: Linux host + Intel iGPU or Intel NPU detected.
5803
+ *
5804
+ * Intentionally addon-local — addons are self-contained and cannot import
5805
+ * `@camstack/system` internals (see architecture invariant: no cross-addon
5806
+ * imports). This mirrors the logic in `resolveRuntimePackages` from that
5807
+ * package without importing it.
5808
+ *
5809
+ * darwin is never true: coremltools handles Apple Silicon + Intel Mac.
5810
+ * linux-amd (or any non-Intel linux GPU) is never true: the openvino
5811
+ * package has no AMD backend and would fail at import time.
5812
+ *
5813
+ * @internal exported only for unit tests in the same package
5814
+ */
5815
+ function shouldInstallOpenvino(hardware) {
5816
+ if (hardware.platform !== "linux") return false;
5817
+ const hasIntelGpu = hardware.gpu?.type === "intel";
5818
+ const hasIntelNpu = hardware.npu?.type === "intel-npu";
5819
+ return hasIntelGpu || hasIntelNpu;
5820
+ }
5821
+ /**
5822
+ * Full catalog of execution providers. The settings UI only shows the subset
5823
+ * reported as available by the platform-probe cap (`getGlobalSettings` gates
5824
+ * options by probe scores). Kept as the static universe so the static schema
5825
+ * (returned before a live ctx is available) still lists all fields.
5826
+ */
5700
5827
  var BACKENDS_BY_RUNTIME = { python: [
5701
5828
  {
5702
5829
  value: "coreml",
@@ -5767,6 +5894,53 @@ var DEVICES_BY_BACKEND = {
5767
5894
  label: "CPU"
5768
5895
  }]
5769
5896
  };
5897
+ /**
5898
+ * Filter the per-backend device option list by what the platform probe
5899
+ * reports as available hardware on this host. Rules (derived from the
5900
+ * confirmed target model):
5901
+ *
5902
+ * coreml:
5903
+ * - `ane` only when `hardware.npu?.type === 'apple-ane'`
5904
+ * - `gpu` and `all` always shown (CPU+GPU present on every Mac)
5905
+ * - `cpu` always shown
5906
+ *
5907
+ * openvino:
5908
+ * - `npu` only when `hardware.npu?.type === 'intel-npu'`
5909
+ * - `gpu` only when `hardware.gpu?.type === 'intel'`
5910
+ * - `auto` and `cpu` always shown
5911
+ *
5912
+ * onnx:
5913
+ * - `cuda` only when `hardware.gpu?.type === 'nvidia'`
5914
+ * - `coreml` only when `hardware.npu?.type === 'apple-ane'` (CoreML EP)
5915
+ * - `cpu` always shown
5916
+ *
5917
+ * When `hardware` is null (probe unreachable), the full catalog is returned
5918
+ * unchanged so the UI still renders all options.
5919
+ */
5920
+ function filterDeviceOptionsByHardware(options, backend, hardware) {
5921
+ if (!hardware) return options;
5922
+ const hasAppleAne = hardware.npu?.type === "apple-ane";
5923
+ const hasIntelNpu = hardware.npu?.type === "intel-npu";
5924
+ const hasIntelGpu = hardware.gpu?.type === "intel";
5925
+ const hasNvidiaGpu = hardware.gpu?.type === "nvidia";
5926
+ switch (backend) {
5927
+ case "coreml": return options.filter((o) => {
5928
+ if (o.value === "ane") return hasAppleAne;
5929
+ return true;
5930
+ });
5931
+ case "openvino": return options.filter((o) => {
5932
+ if (o.value === "npu") return hasIntelNpu;
5933
+ if (o.value === "gpu") return hasIntelGpu;
5934
+ return true;
5935
+ });
5936
+ case "onnx": return options.filter((o) => {
5937
+ if (o.value === "cuda") return hasNvidiaGpu;
5938
+ if (o.value === "coreml") return hasAppleAne;
5939
+ return true;
5940
+ });
5941
+ default: return options;
5942
+ }
5943
+ }
5770
5944
  var BACKEND_TO_FORMAT = {
5771
5945
  cpu: "onnx",
5772
5946
  coreml: "coreml",
@@ -5898,13 +6072,13 @@ var DetectionPipelineAddon = class extends BaseAddon {
5898
6072
  title: "Object detection",
5899
6073
  tab: "engine",
5900
6074
  order: 0,
5901
- description: "Vision-detection backend (object detection, face detection, plate OCR). Chain: runtime backend hardware. Backend options depend on runtime; hardware options depend on backend. All three restart the addon on change.",
6075
+ description: "Vision-detection execution provider (object detection, face detection, plate OCR). Inference always runs via the embedded Python runtime. Provider and hardware options are gated by what the platform probe reports as available on this host. Changes restart the addon.",
5902
6076
  fields: [
5903
6077
  this.field({
5904
6078
  type: "text",
5905
6079
  key: "probedBestEngine",
5906
6080
  label: "Probed best",
5907
- description: "Auto-detected best engine for this host (format: runtime/backend/device). Click the refresh icon to re-run the probe — the detected values get written back into the three fields below.",
6081
+ description: "Auto-detected best engine for this host (format: provider/device, e.g. \"openvino/npu\"). Click the refresh icon to re-run the probe — the detected provider and hardware device are written back into the two fields below, overwriting any manual override.",
5908
6082
  readonlyField: true,
5909
6083
  default: "",
5910
6084
  actions: [{
@@ -5913,19 +6087,10 @@ var DetectionPipelineAddon = class extends BaseAddon {
5913
6087
  tooltip: "Re-probe engine"
5914
6088
  }]
5915
6089
  }),
5916
- this.field({
5917
- type: "select",
5918
- key: "engineRuntime",
5919
- label: "Runtime",
5920
- options: [...RUNTIMES],
5921
- default: DEFAULT_CONFIG.engineRuntime,
5922
- immediate: true,
5923
- requiresRestart: true
5924
- }),
5925
6090
  this.field({
5926
6091
  type: "select",
5927
6092
  key: "engineBackend",
5928
- label: "Backend",
6093
+ label: "Execution provider",
5929
6094
  options: [...BACKENDS_BY_RUNTIME.python],
5930
6095
  default: DEFAULT_CONFIG.engineBackend,
5931
6096
  immediate: true,
@@ -6063,14 +6228,16 @@ var DetectionPipelineAddon = class extends BaseAddon {
6063
6228
  ...overlay
6064
6229
  } : stored;
6065
6230
  const runtime = "python";
6066
- const availableBackends = await this.resolveAvailableBackends(ctx, runtime);
6231
+ const probeResult = await this.resolveProbeData(ctx, runtime);
6232
+ const availableBackends = probeResult.availableBackends;
6233
+ const hardware = probeResult.hardware;
6067
6234
  const runtimeBackends = availableBackends.length > 0 ? BACKENDS_BY_RUNTIME[runtime].filter((b) => availableBackends.includes(b.value)) : BACKENDS_BY_RUNTIME[runtime];
6068
6235
  const storedBackend = typeof merged.engineBackend === "string" ? merged.engineBackend : "";
6069
- const backend = runtimeBackends.find((b) => b.value === storedBackend)?.value ?? runtimeBackends[0]?.value ?? "coreml";
6070
- const deviceOptions = DEVICES_BY_BACKEND[backend] ?? [{
6236
+ const backend = runtimeBackends.find((b) => b.value === storedBackend)?.value ?? probeResult.bestBackend ?? runtimeBackends[0]?.value ?? "coreml";
6237
+ const deviceOptions = filterDeviceOptionsByHardware(DEVICES_BY_BACKEND[backend] ?? [{
6071
6238
  value: "cpu",
6072
6239
  label: "CPU"
6073
- }];
6240
+ }], backend, hardware);
6074
6241
  const storedDevice = typeof merged.engineDevice === "string" ? merged.engineDevice : "";
6075
6242
  const device = deviceOptions.find((d) => d.value === storedDevice)?.value ?? deviceOptions[0]?.value ?? "cpu";
6076
6243
  const raw = {
@@ -6120,28 +6287,47 @@ var DetectionPipelineAddon = class extends BaseAddon {
6120
6287
  }, raw);
6121
6288
  }
6122
6289
  /**
6123
- * Ask the platform-probe cap which backends are actually installable
6124
- * on this node (checks for `openvino`, `coremltools`, `onnxruntime`
6125
- * Python modules + hardware presence). Returns a subset of the static
6126
- * catalog; an empty list signals the probe wasn't available so the
6127
- * caller should fall back to the full catalog rather than hiding
6128
- * every option.
6290
+ * Fetch the platform-probe capabilities and return:
6291
+ * - `availableBackends`: backends the probe reports as available for `runtime`
6292
+ * (empty caller falls back to full catalog).
6293
+ * - `hardware`: the probed hardware info (null when probe not reachable).
6294
+ * - `bestBackend`: the backend from the probe's `bestScore` (null when unavailable).
6295
+ *
6296
+ * A single probe call is made so both backend AND device gating use the
6297
+ * same snapshot without doubling the cap round-trip.
6129
6298
  */
6130
- async resolveAvailableBackends(ctx, runtime) {
6299
+ async resolveProbeData(ctx, runtime) {
6131
6300
  try {
6132
6301
  const api = ctx?.api;
6133
- if (!api) return [];
6302
+ if (!api) return {
6303
+ availableBackends: [],
6304
+ hardware: null,
6305
+ bestBackend: null
6306
+ };
6134
6307
  const caps = await api.platformProbe.getCapabilities.query();
6135
- if (!caps?.scores) return [];
6308
+ if (!caps?.scores) return {
6309
+ availableBackends: [],
6310
+ hardware: null,
6311
+ bestBackend: null
6312
+ };
6136
6313
  const out = /* @__PURE__ */ new Set();
6137
6314
  for (const s of caps.scores) {
6138
6315
  if (s.runtime !== runtime) continue;
6139
6316
  if (!s.available) continue;
6140
6317
  out.add(s.backend);
6141
6318
  }
6142
- return [...out];
6319
+ const bestBackend = caps.bestScore?.runtime === runtime ? caps.bestScore.backend ?? null : null;
6320
+ return {
6321
+ availableBackends: [...out],
6322
+ hardware: caps.hardware ?? null,
6323
+ bestBackend
6324
+ };
6143
6325
  } catch {
6144
- return [];
6326
+ return {
6327
+ availableBackends: [],
6328
+ hardware: null,
6329
+ bestBackend: null
6330
+ };
6145
6331
  }
6146
6332
  }
6147
6333
  /**
@@ -6182,6 +6368,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
6182
6368
  const pythonPath = await this.ctx.deps.ensurePython();
6183
6369
  if (pythonPath) this.pythonPath = pythonPath;
6184
6370
  else this.ctx.logger.warn("Embedded Python unavailable — runtime=\"python\" pipelines will fail until the download succeeds.");
6371
+ await this.proactivelyInstallOpenvino();
6185
6372
  const effectiveTuning = this.resolveBackendTuning();
6186
6373
  this.provider = new DetectionPipelineProvider(this.ctx.settings, modelsDir, this.ctx.logger, this.ctx.eventBus ?? null, () => ({ sections: [] }), {
6187
6374
  concurrency: effectiveTuning.concurrency,
@@ -6247,6 +6434,32 @@ var DetectionPipelineAddon = class extends BaseAddon {
6247
6434
  getModelCatalog() {
6248
6435
  return ALL_STEPS.flatMap((s) => [...s.models]);
6249
6436
  }
6437
+ /**
6438
+ * Proactively install the OpenVINO Python package when Intel hardware
6439
+ * (iGPU or NPU) is detected. Called once from `onInitialize`, before the
6440
+ * provider is constructed, so the module is available when the platform
6441
+ * probe runs its next `import openvino.runtime` check.
6442
+ *
6443
+ * Failure is non-fatal: a warning is logged and the addon continues with
6444
+ * the onnx-cpu baseline. The hardware query itself is also best-effort —
6445
+ * if the platform-probe cap isn't wired yet the error is caught here.
6446
+ */
6447
+ async proactivelyInstallOpenvino() {
6448
+ if (!this.pythonAddonDir || !this.pythonPath) return;
6449
+ try {
6450
+ const hardware = await this.ctx.api.platformProbe.getHardware.query();
6451
+ if (!shouldInstallOpenvino(hardware)) return;
6452
+ this.ctx.logger.info("Intel hardware detected — proactively installing OpenVINO Python package", { meta: {
6453
+ gpu: hardware.gpu?.type ?? null,
6454
+ npu: hardware.npu?.type ?? null
6455
+ } });
6456
+ const requirementsFile = path$1.join(this.pythonAddonDir, "requirements-openvino.txt");
6457
+ await this.ctx.deps.installPythonRequirements(requirementsFile);
6458
+ this.ctx.logger.info("Proactive OpenVINO install complete");
6459
+ } catch (err) {
6460
+ this.ctx.logger.warn("Proactive OpenVINO install failed — falling back to onnx-cpu baseline", { meta: { error: err instanceof Error ? err.message : String(err) } });
6461
+ }
6462
+ }
6250
6463
  async onShutdown() {
6251
6464
  if (this.engineMetricsTimer) {
6252
6465
  clearInterval(this.engineMetricsTimer);
@@ -6335,4 +6548,4 @@ var DetectionPipelineAddon = class extends BaseAddon {
6335
6548
  }
6336
6549
  };
6337
6550
  //#endregion
6338
- export { ALL_STEPS, DetectionPipelineProvider, backendToFormat, DetectionPipelineAddon as default, getDefaultModelForFormat, getStepDefinition };
6551
+ export { ALL_STEPS, DetectionPipelineProvider, backendToFormat, DetectionPipelineAddon as default, filterDeviceOptionsByHardware, getDefaultModelForFormat, getStepDefinition, shouldInstallOpenvino };
@@ -13314,7 +13314,7 @@ method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchem
13314
13314
  * rebuilds without manual reload.
13315
13315
  *
13316
13316
  * The hub-local builtin `addon-pages-aggregator` (see
13317
- * `@camstack/core/builtins/addon-pages-aggregator`) registers the
13317
+ * `@camstack/system/builtins/addon-pages-aggregator`) registers the
13318
13318
  * provider. Splitting the public aggregator from the raw collection
13319
13319
  * keeps both ends in codegen — there's no hand-written
13320
13320
  * `addon-pages.router.ts` wrapper anymore.
@@ -13503,7 +13503,7 @@ var addonWidgetsSourceCapability = {
13503
13503
  * manual reload — same scheme used by `addon-pages`.
13504
13504
  *
13505
13505
  * The hub-local builtin `addon-widgets-aggregator` (see
13506
- * `@camstack/core/builtins/addon-widgets-aggregator`) registers the
13506
+ * `@camstack/system/builtins/addon-widgets-aggregator`) registers the
13507
13507
  * provider. Splitting the public aggregator from the raw collection
13508
13508
  * keeps both ends in codegen — there's no hand-written wrapper.
13509
13509
  */
@@ -16569,7 +16569,7 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
16569
16569
  * clients — they reverse-connect to the hub. Exposing their interfaces
16570
16570
  * via the same surface would leak internal topology with no upside.
16571
16571
  *
16572
- * Implementation in `@camstack/core/builtins/local-network/`.
16572
+ * Implementation in `@camstack/system/builtins/local-network/`.
16573
16573
  */
16574
16574
  /** Coarse classification derived from the interface name + IP range. */
16575
16575
  var InterfaceKindEnum = _enum([
@@ -17116,7 +17116,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
17116
17116
  * jitter, and observed/peak bandwidth per device + per client.
17117
17117
  *
17118
17118
  * Implementation lives in the server's `NetworkQualityService` (thin
17119
- * wrapper over the shared `NetworkQualityTracker` from `@camstack/core`).
17119
+ * wrapper over the shared `NetworkQualityTracker` from `@camstack/system`).
17120
17120
  * The provider is registered from `trpc.router.ts` against the existing
17121
17121
  * service instance — no addon owns this state.
17122
17122
  *
@@ -13314,7 +13314,7 @@ method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchem
13314
13314
  * rebuilds without manual reload.
13315
13315
  *
13316
13316
  * The hub-local builtin `addon-pages-aggregator` (see
13317
- * `@camstack/core/builtins/addon-pages-aggregator`) registers the
13317
+ * `@camstack/system/builtins/addon-pages-aggregator`) registers the
13318
13318
  * provider. Splitting the public aggregator from the raw collection
13319
13319
  * keeps both ends in codegen — there's no hand-written
13320
13320
  * `addon-pages.router.ts` wrapper anymore.
@@ -13503,7 +13503,7 @@ var addonWidgetsSourceCapability = {
13503
13503
  * manual reload — same scheme used by `addon-pages`.
13504
13504
  *
13505
13505
  * The hub-local builtin `addon-widgets-aggregator` (see
13506
- * `@camstack/core/builtins/addon-widgets-aggregator`) registers the
13506
+ * `@camstack/system/builtins/addon-widgets-aggregator`) registers the
13507
13507
  * provider. Splitting the public aggregator from the raw collection
13508
13508
  * keeps both ends in codegen — there's no hand-written wrapper.
13509
13509
  */
@@ -16569,7 +16569,7 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
16569
16569
  * clients — they reverse-connect to the hub. Exposing their interfaces
16570
16570
  * via the same surface would leak internal topology with no upside.
16571
16571
  *
16572
- * Implementation in `@camstack/core/builtins/local-network/`.
16572
+ * Implementation in `@camstack/system/builtins/local-network/`.
16573
16573
  */
16574
16574
  /** Coarse classification derived from the interface name + IP range. */
16575
16575
  var InterfaceKindEnum = _enum([
@@ -17116,7 +17116,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
17116
17116
  * jitter, and observed/peak bandwidth per device + per client.
17117
17117
  *
17118
17118
  * Implementation lives in the server's `NetworkQualityService` (thin
17119
- * wrapper over the shared `NetworkQualityTracker` from `@camstack/core`).
17119
+ * wrapper over the shared `NetworkQualityTracker` from `@camstack/system`).
17120
17120
  * The provider is registered from `trpc.router.ts` against the existing
17121
17121
  * service instance — no addon owns this state.
17122
17122
  *
@@ -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-7ewQjTle.js");
5
+ const require_dist = require("../dist-C1goFC50.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_path = require("node:path");
8
8
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -1,4 +1,4 @@
1
- import { B as motionDetectionCapability, M as evaluateZoneRules, P as hydrateSchema, d as DeviceType, i as BaseAddon } from "../dist-C5jnNl0n.mjs";
1
+ import { B as motionDetectionCapability, M as evaluateZoneRules, P as hydrateSchema, d as DeviceType, i as BaseAddon } from "../dist-XRXnZrVC.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
@@ -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-7ewQjTle.js");
5
+ const require_dist = require("../dist-C1goFC50.js");
6
6
  let _camstack_shm_ring = require("@camstack/shm-ring");
7
7
  //#region src/pipeline-runner/frame-queue.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { $ as boolean, D as customAction, E as createEvent, I as makeSourceBrokerId, Q as array, W as pipelineRunnerCapability, Z as _enum, i as BaseAddon, it as object, j as errMsg, k as defineCustomActions, ot as string, p as EventCategory, rt as number, tt as lazy } from "../dist-C5jnNl0n.mjs";
1
+ import { $ as boolean, D as customAction, E as createEvent, I as makeSourceBrokerId, Q as array, W as pipelineRunnerCapability, Z as _enum, i as BaseAddon, it as object, j as errMsg, k as defineCustomActions, ot as string, p as EventCategory, rt as number, tt as lazy } from "../dist-XRXnZrVC.mjs";
2
2
  import { FrameRingReaderCache } from "@camstack/shm-ring";
3
3
  //#region src/pipeline-runner/frame-queue.ts
4
4
  /**
@@ -1,9 +1,9 @@
1
1
  const require_chunk = require("../chunk-D6vf50IK.js");
2
- const require_dist = require("../dist-7ewQjTle.js");
2
+ const require_dist = require("../dist-C1goFC50.js");
3
3
  let node_fs = require("node:fs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path);
6
- let _camstack_core = require("@camstack/core");
6
+ let _camstack_system = require("@camstack/system");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/recorder/segment-path.ts
9
9
  function segmentRelPath(deviceId, profile, startMs, durMs, bytes) {
@@ -1935,7 +1935,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
1935
1935
  store: this.segmentStore
1936
1936
  });
1937
1937
  try {
1938
- const handler = (0, _camstack_core.createFileDataPlaneHandler)({ getRoots: () => this.playbackRoots() });
1938
+ const handler = (0, _camstack_system.createFileDataPlaneHandler)({ getRoots: () => this.playbackRoots() });
1939
1939
  const served = await this.ctx.dataPlane?.serve({
1940
1940
  prefix: PLAYBACK_PREFIX,
1941
1941
  access: "authenticated",
@@ -1,7 +1,7 @@
1
- import { G as recordingCapability, J as storageEvictableCapability, K as selectAssignedProfileSlots, P as hydrateSchema, T as createDurableState, at as record, g as RecordingConfigSchema, i as BaseAddon, j as errMsg, ot as string, p as EventCategory, z as migrateConfigToBands } from "../dist-C5jnNl0n.mjs";
1
+ import { G as recordingCapability, J as storageEvictableCapability, K as selectAssignedProfileSlots, P as hydrateSchema, T as createDurableState, at as record, g as RecordingConfigSchema, i as BaseAddon, j as errMsg, ot as string, p as EventCategory, z as migrateConfigToBands } from "../dist-XRXnZrVC.mjs";
2
2
  import { promises } from "node:fs";
3
3
  import path from "node:path";
4
- import { createFileDataPlaneHandler } from "@camstack/core";
4
+ import { createFileDataPlaneHandler } from "@camstack/system";
5
5
  import { spawn } from "node:child_process";
6
6
  //#region src/recorder/segment-path.ts
7
7
  function segmentRelPath(deviceId, profile, startMs, durMs, bytes) {
@@ -1,5 +1,5 @@
1
1
  import { a as e, i as t, n, o as r, r as i, t as a } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
2
- import { a as o, c as s, i as c, l, n as u, o as d, r as f, s as p, t as m, u as h } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C9WX5HNw.mjs";
2
+ import { a as o, c as s, i as c, l, n as u, o as d, r as f, s as p, t as m, u as h } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B0Z2W5UM.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
5
  import { t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Tf-HACFd.mjs";
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.0.1",
6
+ version: "1.0.3",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_stream_broker_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.0.1",
21
+ version: "1.0.3",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_stream_broker_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.0.1",
36
+ version: "1.0.3",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_stream_broker_widgets",