@camstack/addon-pipeline 1.1.3 → 1.1.5

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 (34) hide show
  1. package/dist/audio-analyzer/index.js +17 -19
  2. package/dist/audio-analyzer/index.mjs +17 -19
  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 +195 -65
  8. package/dist/detection-pipeline/index.mjs +195 -65
  9. package/dist/{dist-DnD2tm7T.js → dist-BhpOMzzH.js} +44 -11
  10. package/dist/{dist-BWc-HYQz.mjs → dist-GFzs5acw.mjs} +44 -11
  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 +1 -1
  16. package/dist/recorder/index.mjs +1 -1
  17. package/dist/stream-broker/_stub.js +2 -2
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DmOHs-vv.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DQYrh0ie.mjs} +3 -3
  19. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-FRD2eBuz.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-S6ETjGtN.mjs} +1 -1
  20. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CHcXI1Wf.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-5vetl3CL.mjs} +1 -1
  21. package/dist/stream-broker/{hostInit-CqjZ-6gj.mjs → hostInit-DvWdWFSe.mjs} +3 -3
  22. package/dist/stream-broker/index.js +1 -1
  23. package/dist/stream-broker/index.mjs +1 -1
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-5UPreLSr.js → MaskShapeCanvas-DI4BY7W2-Bulflbu5.js} +1 -1
  26. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-Bxqs-CpZ.js → MotionZonesSettings-NcxxQN8r-DJ9keq4q.js} +1 -1
  27. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-BDMPeMJd.js → PrivacyMaskSettings-APgPLF7p-CRsjkTB1.js} +1 -1
  28. package/embed-dist/assets/index-DBB2e2P1.js +80 -0
  29. package/embed-dist/index.html +1 -1
  30. package/package.json +1 -9
  31. package/python/postprocessors/__init__.py +2 -0
  32. package/python/postprocessors/clip.py +35 -0
  33. package/python/postprocessors/test_clip.py +33 -0
  34. package/embed-dist/assets/index-BgGwqHYl.js +0 -80
@@ -1,5 +1,5 @@
1
1
  import { t as __require } from "../chunk-BdkLduGY.mjs";
2
- import { A as nodePin, B as BaseAddon, C as detectionPipelineCapability, J as hydrateSchema, L as supportedRuntimes$1, P as runtimeDevices$1, T as hfModelUrl, W as EventCategory, Z as parseJsonUnknown, a as COCO_TO_MACRO, et as sleep, i as COCO_80_LABELS, j as pipelineExecutorCapability, p as YAMNET_TO_MACRO, q as createEvent, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, w as evaluateZoneRules, x as defaultDeviceFor$1, z as errMsg } from "../dist-BWc-HYQz.mjs";
2
+ import { A as nodePin, B as BaseAddon, C as detectionPipelineCapability, J as hydrateSchema, L as supportedRuntimes$1, P as runtimeDevices$1, T as hfModelUrl, W as EventCategory, Z as parseJsonUnknown, a as COCO_TO_MACRO, et as sleep, i as COCO_80_LABELS, j as pipelineExecutorCapability, p as YAMNET_TO_MACRO, q as createEvent, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, w as evaluateZoneRules, x as defaultDeviceFor$1, z as errMsg } from "../dist-GFzs5acw.mjs";
3
3
  import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
4
4
  import * as fs from "node:fs";
5
5
  import * as path$1 from "node:path";
@@ -1746,6 +1746,95 @@ var INSTANCE_SEGMENTATION_MODELS = [
1746
1746
  }
1747
1747
  }
1748
1748
  ];
1749
+ var CLIP_EMBEDDING_MODELS = [
1750
+ {
1751
+ id: "mobileclip-s0",
1752
+ name: "MobileCLIP S0",
1753
+ description: "MobileCLIP S0 — Apple ultra-fast CLIP vision encoder, 512-dim, 256×256 (40 MB ONNX)",
1754
+ inputSize: {
1755
+ width: 256,
1756
+ height: 256
1757
+ },
1758
+ labels: [{
1759
+ id: "embedding",
1760
+ name: "CLIP Embedding"
1761
+ }],
1762
+ preprocessMode: "resize",
1763
+ inputNormalization: "none",
1764
+ formats: {
1765
+ onnx: {
1766
+ url: hf("clip/mobileclip-s0/onnx/camstack-mobileclip-s0-vision.onnx"),
1767
+ sizeMB: 40
1768
+ },
1769
+ openvino: ovFormat(hf("clip/mobileclip-s0/openvino/camstack-mobileclip-s0-vision.xml"), 25),
1770
+ coreml: {
1771
+ url: hf("clip/mobileclip-s0/coreml/camstack-mobileclip-s0-vision.mlpackage"),
1772
+ sizeMB: 30,
1773
+ isDirectory: true,
1774
+ files: [...MLPACKAGE_FILES],
1775
+ runtimes: ["python"]
1776
+ }
1777
+ }
1778
+ },
1779
+ {
1780
+ id: "mobileclip-s1",
1781
+ name: "MobileCLIP S1",
1782
+ description: "MobileCLIP S1 — Apple balanced CLIP vision encoder, 512-dim, 256×256 (90 MB ONNX)",
1783
+ inputSize: {
1784
+ width: 256,
1785
+ height: 256
1786
+ },
1787
+ labels: [{
1788
+ id: "embedding",
1789
+ name: "CLIP Embedding"
1790
+ }],
1791
+ preprocessMode: "resize",
1792
+ inputNormalization: "none",
1793
+ formats: {
1794
+ onnx: {
1795
+ url: hf("clip/mobileclip-s1/onnx/camstack-mobileclip-s1-vision.onnx"),
1796
+ sizeMB: 90
1797
+ },
1798
+ openvino: ovFormat(hf("clip/mobileclip-s1/openvino/camstack-mobileclip-s1-vision.xml"), 55),
1799
+ coreml: {
1800
+ url: hf("clip/mobileclip-s1/coreml/camstack-mobileclip-s1-vision.mlpackage"),
1801
+ sizeMB: 65,
1802
+ isDirectory: true,
1803
+ files: [...MLPACKAGE_FILES],
1804
+ runtimes: ["python"]
1805
+ }
1806
+ }
1807
+ },
1808
+ {
1809
+ id: "mobileclip-s2",
1810
+ name: "MobileCLIP S2",
1811
+ description: "MobileCLIP S2 — Apple high-accuracy CLIP vision encoder, 512-dim, 256×256 (150 MB ONNX)",
1812
+ inputSize: {
1813
+ width: 256,
1814
+ height: 256
1815
+ },
1816
+ labels: [{
1817
+ id: "embedding",
1818
+ name: "CLIP Embedding"
1819
+ }],
1820
+ preprocessMode: "resize",
1821
+ inputNormalization: "none",
1822
+ formats: {
1823
+ onnx: {
1824
+ url: hf("clip/mobileclip-s2/onnx/camstack-mobileclip-s2-vision.onnx"),
1825
+ sizeMB: 150
1826
+ },
1827
+ openvino: ovFormat(hf("clip/mobileclip-s2/openvino/camstack-mobileclip-s2-vision.xml"), 90),
1828
+ coreml: {
1829
+ url: hf("clip/mobileclip-s2/coreml/camstack-mobileclip-s2-vision.mlpackage"),
1830
+ sizeMB: 110,
1831
+ isDirectory: true,
1832
+ files: [...MLPACKAGE_FILES],
1833
+ runtimes: ["python"]
1834
+ }
1835
+ }
1836
+ }
1837
+ ];
1749
1838
  var AUDIO_CLASSIFIER_MODELS = [{
1750
1839
  id: "yamnet-onnx",
1751
1840
  name: "YAMNet",
@@ -2017,6 +2106,21 @@ var STEP_FACE_EMBEDDING = new PipelineStepBase({
2017
2106
  defaultModelId: "arcface-r100",
2018
2107
  defaultConfidence: 0
2019
2108
  });
2109
+ var STEP_CLIP_EMBEDDING = new PipelineStepBase({
2110
+ id: "clip-embedding",
2111
+ name: "CLIP Embedding",
2112
+ slot: "classifier",
2113
+ postprocessor: "clip",
2114
+ extractMode: "crop-roi",
2115
+ inputClasses: null,
2116
+ outputClasses: ["embedding"],
2117
+ models: [...CLIP_EMBEDDING_MODELS],
2118
+ defaultModelId: "mobileclip-s1",
2119
+ enabledByDefault: false,
2120
+ defaultConfidence: 0,
2121
+ defaultMinParentScore: .4,
2122
+ group: "CLIP"
2123
+ });
2020
2124
  var STEP_PLATE_DETECTION = new PipelineStepBase({
2021
2125
  id: "plate-detection",
2022
2126
  name: "Plate Detection",
@@ -2106,6 +2210,7 @@ var ALL_PIPELINE_STEPS = [
2106
2210
  STEP_OBJECT_DETECTION_INSTANCE,
2107
2211
  STEP_FACE_DETECTION,
2108
2212
  STEP_FACE_EMBEDDING,
2213
+ STEP_CLIP_EMBEDDING,
2109
2214
  STEP_PLATE_DETECTION,
2110
2215
  STEP_PLATE_OCR,
2111
2216
  new AnimalClassifierStep(),
@@ -2656,6 +2761,22 @@ function postprocessArcface(output, _stepDef) {
2656
2761
  embeddingNorm: normalized
2657
2762
  };
2658
2763
  }
2764
+ function postprocessClip(output, _stepDef) {
2765
+ const structured = tryStructured(output);
2766
+ if (structured) return structured;
2767
+ const tensor = output.tensor;
2768
+ if (!tensor) throw new Error("CLIP postprocessor: no tensor in engine output");
2769
+ let sumSq = 0;
2770
+ for (let i = 0; i < tensor.length; i++) sumSq += tensor[i] * tensor[i];
2771
+ const norm = Math.sqrt(sumSq);
2772
+ const normalized = Array.from({ length: tensor.length });
2773
+ for (let i = 0; i < tensor.length; i++) normalized[i] = norm === 0 ? 0 : tensor[i] / norm;
2774
+ return {
2775
+ kind: "embedding",
2776
+ embedding: Array.from(tensor),
2777
+ embeddingNorm: normalized
2778
+ };
2779
+ }
2659
2780
  function postprocessSoftmax(output, stepDef) {
2660
2781
  const structured = tryStructured(output);
2661
2782
  if (structured) return structured;
@@ -2715,6 +2836,67 @@ function postprocessCtc(output, stepDef) {
2715
2836
  confidence: seqLen > 0 ? totalScore / seqLen : 0
2716
2837
  };
2717
2838
  }
2839
+ /**
2840
+ * Bounding box [x, y, w, h] (mask-pixel space) of the largest 4-connected
2841
+ * foreground component. Port of `_largest_component_bbox` from
2842
+ * `python/postprocessors/saliency.py`.
2843
+ *
2844
+ * Returns null when there are no foreground pixels.
2845
+ */
2846
+ function largestComponentBbox(binary, w, h) {
2847
+ const seeds = [];
2848
+ for (let row = 0; row < h; row++) for (let col = 0; col < w; col++) if ((binary[row * w + col] ?? 0) > 0) seeds.push([row, col]);
2849
+ if (seeds.length === 0) return null;
2850
+ const visited = new Uint8Array(w * h);
2851
+ let best = null;
2852
+ for (const [sy, sx] of seeds) {
2853
+ const seedIdx = sy * w + sx;
2854
+ if (visited[seedIdx] === 1) continue;
2855
+ const stack = [[sy, sx]];
2856
+ visited[seedIdx] = 1;
2857
+ let minX = sx;
2858
+ let maxX = sx;
2859
+ let minY = sy;
2860
+ let maxY = sy;
2861
+ let area = 0;
2862
+ while (stack.length > 0) {
2863
+ const [cy, cx] = stack.pop();
2864
+ area += 1;
2865
+ if (cx < minX) minX = cx;
2866
+ if (cx > maxX) maxX = cx;
2867
+ if (cy < minY) minY = cy;
2868
+ if (cy > maxY) maxY = cy;
2869
+ const neighbours = [
2870
+ [cy - 1, cx],
2871
+ [cy + 1, cx],
2872
+ [cy, cx - 1],
2873
+ [cy, cx + 1]
2874
+ ];
2875
+ for (const [ny, nx] of neighbours) {
2876
+ if (ny < 0 || ny >= h || nx < 0 || nx >= w) continue;
2877
+ const nIdx = ny * w + nx;
2878
+ if ((binary[nIdx] ?? 0) > 0 && visited[nIdx] !== 1) {
2879
+ visited[nIdx] = 1;
2880
+ stack.push([ny, nx]);
2881
+ }
2882
+ }
2883
+ }
2884
+ if (best === null || area > best.area) best = {
2885
+ area,
2886
+ minX,
2887
+ minY,
2888
+ maxX,
2889
+ maxY
2890
+ };
2891
+ }
2892
+ if (best === null) return null;
2893
+ return [
2894
+ best.minX,
2895
+ best.minY,
2896
+ best.maxX - best.minX + 1,
2897
+ best.maxY - best.minY + 1
2898
+ ];
2899
+ }
2718
2900
  function postprocessSaliency(output, _stepDef) {
2719
2901
  const structured = tryStructured(output);
2720
2902
  if (structured) return structured;
@@ -2723,9 +2905,18 @@ function postprocessSaliency(output, _stepDef) {
2723
2905
  const binary = new Uint8Array(tensor.length);
2724
2906
  for (let i = 0; i < tensor.length; i++) binary[i] = 1 / (1 + Math.exp(-tensor[i])) > .5 ? 255 : 0;
2725
2907
  const side = Math.round(Math.sqrt(tensor.length));
2908
+ const mask = Buffer.from(binary).toString("base64");
2909
+ const maskBbox = largestComponentBbox(binary, side, side);
2910
+ if (maskBbox !== null) return {
2911
+ kind: "mask",
2912
+ mask,
2913
+ maskWidth: side,
2914
+ maskHeight: side,
2915
+ maskBbox
2916
+ };
2726
2917
  return {
2727
2918
  kind: "mask",
2728
- mask: Buffer.from(binary).toString("base64"),
2919
+ mask,
2729
2920
  maskWidth: side,
2730
2921
  maskHeight: side
2731
2922
  };
@@ -2872,6 +3063,7 @@ var DISPATCH = {
2872
3063
  "yolo-seg": postprocessYoloSeg,
2873
3064
  scrfd: postprocessScrfd,
2874
3065
  arcface: postprocessArcface,
3066
+ clip: postprocessClip,
2875
3067
  softmax: postprocessSoftmax,
2876
3068
  ctc: postprocessCtc,
2877
3069
  saliency: postprocessSaliency,
@@ -5943,41 +6135,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5943
6135
  return readStored(raw, isEngineChoice);
5944
6136
  }
5945
6137
  /**
5946
- * Synchronous availability probe for a Python inference backend. Runs a
5947
- * short `<python> -c "import <mod>"` with a 3s timeout. Used at
5948
- * `loadEngine` time to reject a persisted backend choice the Python
5949
- * interpreter on this host can't actually import — without this the
5950
- * detection-pipeline child process exits with code 1 the moment
5951
- * `inference_pool.py` hits its `from openvino.runtime import Core`.
5952
- *
5953
- * MUST probe the EMBEDDED portable interpreter (`pythonPath`, resolved via
5954
- * `ctx.deps.ensurePython()`) — that's where `installPythonRequirements`
5955
- * puts the backend packages and what `SharedInferencePool` actually spawns.
5956
- * Probing the system `python3` checks the wrong site-packages (and on a
5957
- * slim container there is no system python3 at all → every backend would
5958
- * be reported unavailable, silently disabling ML). Falls back to `python3`
5959
- * only when no embedded interpreter is resolved.
5960
- */
5961
- static isPythonBackendAvailable(backend, pythonPath) {
5962
- const mod = {
5963
- coreml: "coremltools",
5964
- openvino: "openvino.runtime",
5965
- onnx: "onnxruntime"
5966
- }[backend];
5967
- if (mod === null) return true;
5968
- if (!mod) return false;
5969
- try {
5970
- const { execFileSync } = __require("node:child_process");
5971
- execFileSync(pythonPath || "python3", ["-c", `import ${mod}`], {
5972
- timeout: 3e3,
5973
- stdio: "pipe"
5974
- });
5975
- return true;
5976
- } catch {
5977
- return false;
5978
- }
5979
- }
5980
- /**
5981
6138
  * Re-run the platform probe for the inference engine and persist the
5982
6139
  * result into the addon's own global-settings keys. Because those
5983
6140
  * fields are declared `requiresRestart: true`, the
@@ -7030,33 +7187,6 @@ var DetectionPipelineAddon = class extends BaseAddon {
7030
7187
  numWorkers: num(t["numWorkers"], 1)
7031
7188
  };
7032
7189
  }
7033
- /**
7034
- * Resolve the directory models are downloaded into, resilient across nodes.
7035
- * The `models` storage location is GLOBAL (hub-seeded) and can resolve to a
7036
- * path that exists only on the hub — e.g. Docker's `/data/models`, which an
7037
- * agent on a different filesystem (a Mac) cannot create (`ENOENT mkdir
7038
- * /data/models`). Verify the resolved dir is creatable; otherwise fall back to
7039
- * this node's LOCAL addon data-dir so models always land on writable disk.
7040
- */
7041
- async resolveModelsDir() {
7042
- const fallback = path$1.join(this.ctx.dataDir, "models");
7043
- const candidate = await this.ctx.api.storage.resolve.query({
7044
- location: "models",
7045
- relativePath: ""
7046
- }).catch(() => null) ?? fallback;
7047
- try {
7048
- await fs.promises.mkdir(candidate, { recursive: true });
7049
- return candidate;
7050
- } catch (err) {
7051
- this.ctx.logger.warn("models dir not creatable on this node — using local data-dir", { meta: {
7052
- resolved: candidate,
7053
- fallback,
7054
- error: err instanceof Error ? err.message : String(err)
7055
- } });
7056
- await fs.promises.mkdir(fallback, { recursive: true });
7057
- return fallback;
7058
- }
7059
- }
7060
7190
  async onInitialize() {
7061
7191
  const modelsDir = await this.resolveModelsDir();
7062
7192
  if (!this.ctx.settings) throw new Error("DetectionPipelineAddon: ctx.settings not available");
@@ -7090,7 +7220,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
7090
7220
  });
7091
7221
  this.probePhaseUnsub = this.ctx.eventBus?.subscribe({ category: EventCategory.PlatformProbePhase }, (event) => {
7092
7222
  if (event.data?.phase !== "done") return;
7093
- this.provider.repickEngineOnProbeReady().catch((err) => {
7223
+ this.provider?.repickEngineOnProbeReady().catch((err) => {
7094
7224
  this.ctx.logger.warn("repick on platform-probe done failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
7095
7225
  });
7096
7226
  }) ?? null;
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-BV7rLc6Y.mjs
4630
+ //#region ../types/dist/sleep-NOH4yRwj.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5742,15 +5742,24 @@ var BaseAddon = class {
5742
5742
  } catch {}
5743
5743
  }
5744
5744
  /**
5745
- * Resolve the shared models directory path via the storage capability.
5746
- * Falls back to `camstack-data/models` if storage is unavailable.
5747
- * Used by inference addons (detection-pipeline, audio-classifier, embedding-encoder).
5745
+ * Resolve the shared, node-local models directory: `<nodeDataDir>/models`.
5746
+ *
5747
+ * This is the SINGLE centralized models-dir resolver for every inference addon
5748
+ * (detection-pipeline, audio-analyzer, embedding-encoder, model-studio). It is
5749
+ * anchored at the node's own writable data root (`ctx.nodeDataDir`, i.e.
5750
+ * `CAMSTACK_DATA` ?? the kernel boot dir) — never the hub-singleton `storage`
5751
+ * cap, which returns the hub's `/data/models` to every node and breaks any node
5752
+ * whose real data dir differs (e.g. the macOS electron agent).
5753
+ *
5754
+ * `node:path`/`node:fs` are imported dynamically so the `@camstack/types` root
5755
+ * barrel (re-exported into browser bundles) stays free of node builtins.
5748
5756
  */
5749
5757
  async resolveModelsDir() {
5750
- return this.ctx.api.storage.resolve.query({
5751
- location: "models",
5752
- relativePath: ""
5753
- }).catch(() => "camstack-data/models");
5758
+ const { join } = await import("node:path");
5759
+ const { promises: fsp } = await import("node:fs");
5760
+ const dir = join(this.ctx.nodeDataDir, "models");
5761
+ await fsp.mkdir(dir, { recursive: true });
5762
+ return dir;
5754
5763
  }
5755
5764
  /**
5756
5765
  * Access the runtime capability registry for in-process provider lookups.
@@ -13840,7 +13849,8 @@ method(object({
13840
13849
  sessionId: string().optional()
13841
13850
  }), ConvertResultSchema, {
13842
13851
  kind: "mutation",
13843
- auth: "admin"
13852
+ auth: "admin",
13853
+ timeoutMs: 6e5
13844
13854
  });
13845
13855
  var AddonHttpRouteSchema = object({
13846
13856
  method: _enum([
@@ -15667,7 +15677,14 @@ var NotificationRuleConditionsSchema = object({
15667
15677
  /** Match against `event.data.eventType` token (e.g. `'press_long'`). When non-empty, only events
15668
15678
  * carrying a matching `data.eventType` string pass this condition. Rules without this field are
15669
15679
  * unaffected (back-compat). Distinct from `rule.eventTypes` which holds EventCategory strings. */
15670
- eventTypeTokens: array(string()).readonly().optional()
15680
+ eventTypeTokens: array(string()).readonly().optional(),
15681
+ /** Match detections whose CLIP image embedding is semantically similar to this free-text
15682
+ * description. Requires the embedding-encoder cap to have pre-warmed the text vector.
15683
+ * `minSimilarity` is the cosine similarity threshold in [0, 1]. */
15684
+ clipDescription: object({
15685
+ text: string().min(1),
15686
+ minSimilarity: number().min(0).max(1)
15687
+ }).optional()
15671
15688
  });
15672
15689
  var NotificationRuleTemplateSchema = object({
15673
15690
  title: string(),
@@ -15940,6 +15957,16 @@ var TrackedDetectionSchema = object({
15940
15957
  zones: array(string()).readonly(),
15941
15958
  state: TrackStateSchema
15942
15959
  });
15960
+ var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
15961
+ var SearchObjectEventsInput = object({
15962
+ text: string(),
15963
+ deviceId: number().optional(),
15964
+ since: number().optional(),
15965
+ until: number().optional(),
15966
+ classFilter: string().optional(),
15967
+ limit: number().default(50),
15968
+ minScore: number().min(0).max(1).default(.2)
15969
+ });
15943
15970
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
15944
15971
  deviceId: number(),
15945
15972
  trackId: string()
@@ -15974,7 +16001,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15974
16001
  }), method(object({
15975
16002
  eventId: string(),
15976
16003
  kind: MediaFileKindEnum.optional()
15977
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), object({
16004
+ }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
15978
16005
  deviceId: number(),
15979
16006
  timestamp: number(),
15980
16007
  frameWidth: number(),
@@ -20657,6 +20684,12 @@ Object.freeze({
20657
20684
  addonId: null,
20658
20685
  access: "create"
20659
20686
  },
20687
+ "pipelineAnalytics.searchObjectEvents": {
20688
+ capName: "pipeline-analytics",
20689
+ capScope: "device",
20690
+ addonId: null,
20691
+ access: "view"
20692
+ },
20660
20693
  "pipelineExecutor.cacheFrameInPool": {
20661
20694
  capName: "pipeline-executor",
20662
20695
  capScope: "system",
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-BV7rLc6Y.mjs
4630
+ //#region ../types/dist/sleep-NOH4yRwj.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5742,15 +5742,24 @@ var BaseAddon = class {
5742
5742
  } catch {}
5743
5743
  }
5744
5744
  /**
5745
- * Resolve the shared models directory path via the storage capability.
5746
- * Falls back to `camstack-data/models` if storage is unavailable.
5747
- * Used by inference addons (detection-pipeline, audio-classifier, embedding-encoder).
5745
+ * Resolve the shared, node-local models directory: `<nodeDataDir>/models`.
5746
+ *
5747
+ * This is the SINGLE centralized models-dir resolver for every inference addon
5748
+ * (detection-pipeline, audio-analyzer, embedding-encoder, model-studio). It is
5749
+ * anchored at the node's own writable data root (`ctx.nodeDataDir`, i.e.
5750
+ * `CAMSTACK_DATA` ?? the kernel boot dir) — never the hub-singleton `storage`
5751
+ * cap, which returns the hub's `/data/models` to every node and breaks any node
5752
+ * whose real data dir differs (e.g. the macOS electron agent).
5753
+ *
5754
+ * `node:path`/`node:fs` are imported dynamically so the `@camstack/types` root
5755
+ * barrel (re-exported into browser bundles) stays free of node builtins.
5748
5756
  */
5749
5757
  async resolveModelsDir() {
5750
- return this.ctx.api.storage.resolve.query({
5751
- location: "models",
5752
- relativePath: ""
5753
- }).catch(() => "camstack-data/models");
5758
+ const { join } = await import("node:path");
5759
+ const { promises: fsp } = await import("node:fs");
5760
+ const dir = join(this.ctx.nodeDataDir, "models");
5761
+ await fsp.mkdir(dir, { recursive: true });
5762
+ return dir;
5754
5763
  }
5755
5764
  /**
5756
5765
  * Access the runtime capability registry for in-process provider lookups.
@@ -13840,7 +13849,8 @@ method(object({
13840
13849
  sessionId: string().optional()
13841
13850
  }), ConvertResultSchema, {
13842
13851
  kind: "mutation",
13843
- auth: "admin"
13852
+ auth: "admin",
13853
+ timeoutMs: 6e5
13844
13854
  });
13845
13855
  var AddonHttpRouteSchema = object({
13846
13856
  method: _enum([
@@ -15667,7 +15677,14 @@ var NotificationRuleConditionsSchema = object({
15667
15677
  /** Match against `event.data.eventType` token (e.g. `'press_long'`). When non-empty, only events
15668
15678
  * carrying a matching `data.eventType` string pass this condition. Rules without this field are
15669
15679
  * unaffected (back-compat). Distinct from `rule.eventTypes` which holds EventCategory strings. */
15670
- eventTypeTokens: array(string()).readonly().optional()
15680
+ eventTypeTokens: array(string()).readonly().optional(),
15681
+ /** Match detections whose CLIP image embedding is semantically similar to this free-text
15682
+ * description. Requires the embedding-encoder cap to have pre-warmed the text vector.
15683
+ * `minSimilarity` is the cosine similarity threshold in [0, 1]. */
15684
+ clipDescription: object({
15685
+ text: string().min(1),
15686
+ minSimilarity: number().min(0).max(1)
15687
+ }).optional()
15671
15688
  });
15672
15689
  var NotificationRuleTemplateSchema = object({
15673
15690
  title: string(),
@@ -15940,6 +15957,16 @@ var TrackedDetectionSchema = object({
15940
15957
  zones: array(string()).readonly(),
15941
15958
  state: TrackStateSchema
15942
15959
  });
15960
+ var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
15961
+ var SearchObjectEventsInput = object({
15962
+ text: string(),
15963
+ deviceId: number().optional(),
15964
+ since: number().optional(),
15965
+ until: number().optional(),
15966
+ classFilter: string().optional(),
15967
+ limit: number().default(50),
15968
+ minScore: number().min(0).max(1).default(.2)
15969
+ });
15943
15970
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
15944
15971
  deviceId: number(),
15945
15972
  trackId: string()
@@ -15974,7 +16001,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
15974
16001
  }), method(object({
15975
16002
  eventId: string(),
15976
16003
  kind: MediaFileKindEnum.optional()
15977
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), object({
16004
+ }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
15978
16005
  deviceId: number(),
15979
16006
  timestamp: number(),
15980
16007
  frameWidth: number(),
@@ -20657,6 +20684,12 @@ Object.freeze({
20657
20684
  addonId: null,
20658
20685
  access: "create"
20659
20686
  },
20687
+ "pipelineAnalytics.searchObjectEvents": {
20688
+ capName: "pipeline-analytics",
20689
+ capScope: "device",
20690
+ addonId: null,
20691
+ access: "view"
20692
+ },
20660
20693
  "pipelineExecutor.cacheFrameInPool": {
20661
20694
  capName: "pipeline-executor",
20662
20695
  capScope: "system",
@@ -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-DnD2tm7T.js");
5
+ const require_dist = require("../dist-BhpOMzzH.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 BaseAddon, J as hydrateSchema, U as DeviceType, k as motionDetectionCapability, w as evaluateZoneRules } from "../dist-BWc-HYQz.mjs";
1
+ import { B as BaseAddon, J as hydrateSchema, U as DeviceType, k as motionDetectionCapability, w as evaluateZoneRules } from "../dist-GFzs5acw.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-DnD2tm7T.js");
5
+ const require_dist = require("../dist-BhpOMzzH.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 { A as nodePin, B as BaseAddon, M as pipelineRunnerCapability, S as defineCustomActions, W as EventCategory, X as makeSourceBrokerId, at as lazy, ct as object, nt as array, q as createEvent, rt as boolean, st as number, tt as _enum, ut as string, y as customAction, z as errMsg } from "../dist-BWc-HYQz.mjs";
1
+ import { A as nodePin, B as BaseAddon, M as pipelineRunnerCapability, S as defineCustomActions, W as EventCategory, X as makeSourceBrokerId, at as lazy, ct as object, nt as array, q as createEvent, rt as boolean, st as number, tt as _enum, ut as string, y as customAction, z as errMsg } from "../dist-GFzs5acw.mjs";
2
2
  import { FrameRingReaderCache } from "@camstack/shm-ring";
3
3
  //#region src/pipeline-runner/frame-queue.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
2
- const require_dist = require("../dist-DnD2tm7T.js");
2
+ const require_dist = require("../dist-BhpOMzzH.js");
3
3
  let node_fs = require("node:fs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_model_download_service_C_IHWnXx.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { $ as selectAssignedProfileSlots, B as BaseAddon, F as storageEvictableCapability, J as hydrateSchema, K as createDurableState, N as recordingCapability, O as migrateConfigToBands, W as EventCategory, d as RecordingConfigSchema, lt as record, ut as string, z as errMsg } from "../dist-BWc-HYQz.mjs";
1
+ import { $ as selectAssignedProfileSlots, B as BaseAddon, F as storageEvictableCapability, J as hydrateSchema, K as createDurableState, N as recordingCapability, O as migrateConfigToBands, W as EventCategory, d as RecordingConfigSchema, lt as record, ut as string, z as errMsg } from "../dist-GFzs5acw.mjs";
2
2
  import { t as createFileDataPlaneHandler } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
3
3
  import { promises } from "node:fs";
4
4
  import path from "node:path";
@@ -1,8 +1,8 @@
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-CHcXI1Wf.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-5vetl3CL.mjs";
3
3
  import { n as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
4
4
  import { n as y, t as b } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
5
- import { t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-FRD2eBuz.mjs";
5
+ import { t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-S6ETjGtN.mjs";
6
6
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
7
7
  var S = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), C = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), w = (e) => {
8
8
  let t = C(e);
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.1.3",
6
+ version: "1.1.5",
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.1.3",
21
+ version: "1.1.5",
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.1.3",
36
+ version: "1.1.5",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_stream_broker_widgets",