@camstack/addon-pipeline 1.1.70 → 1.2.0

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 +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/detection-pipeline/index.js +181 -469
  4. package/dist/detection-pipeline/index.mjs +182 -469
  5. package/dist/{dist-CurVt3xC.mjs → dist-DLWXeXC1.mjs} +239 -60
  6. package/dist/{dist-BWn5k0A5.js → dist-DWy-H6bo.js} +250 -59
  7. package/dist/motion-wasm/index.js +1 -1
  8. package/dist/motion-wasm/index.mjs +1 -1
  9. package/dist/pipeline-runner/index.js +24 -2
  10. package/dist/pipeline-runner/index.mjs +24 -2
  11. package/dist/recorder/index.js +1 -1
  12. package/dist/recorder/index.mjs +1 -1
  13. package/dist/session-decode/decode-worker-child.js +177 -12
  14. package/dist/session-decode/decode-worker-child.mjs +177 -12
  15. package/dist/{step-definitions-B7e8rPe3.js → step-definitions-BQG_6dmZ.js} +6 -2
  16. package/dist/{step-definitions-BKm17nce.mjs → step-definitions-CVk19Y0u.mjs} +6 -2
  17. package/dist/stream-broker/_stub.js +2 -2
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-yAAGMET5.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CpB1jG0N.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-BflyNuZv.mjs +26 -0
  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-5Zc9wj-A.mjs +26 -0
  21. package/dist/stream-broker/{hostInit-DUzRsTmt.mjs → hostInit--Zair8Av.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-D6bWMuew.js → MaskShapeCanvas-DI4BY7W2-sOOxiGvP.js} +1 -1
  26. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-C-1enV21.js → MotionZonesSettings-NcxxQN8r-DdTQuT23.js} +1 -1
  27. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-By9pdGP7.js → PrivacyMaskSettings-APgPLF7p-BrR0L3ql.js} +1 -1
  28. package/embed-dist/assets/index-D4MGuECB.css +2 -0
  29. package/embed-dist/assets/{index-BPGh79kn.js → index-Dk21l1_3.js} +14 -14
  30. package/embed-dist/index.html +2 -2
  31. package/package.json +1 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D68Lj69v.mjs +0 -26
  33. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CDBALHSV.mjs +0 -26
  34. package/embed-dist/assets/index-D2pF9Z3W.css +0 -2
@@ -7924,6 +7924,24 @@ function nodePin(nodeId) {
7924
7924
  function hfModelUrl(repo, path) {
7925
7925
  return `https://huggingface.co/${repo}/resolve/main/${path}`;
7926
7926
  }
7927
+ /**
7928
+ * DEVICE-POOL backend → model format — the SSOT for the multi-device inference
7929
+ * DESCRIPTOR path (Python per-device pools), distinct from {@link BACKEND_TO_FORMAT}
7930
+ * above which is the legacy NODE runtime map (where `coreml → onnx` because
7931
+ * onnxruntime-node's CoreML EP loads `.onnx`). Here a device pool runs the
7932
+ * backend NATIVELY, so `coreml → coreml` and `edgetpu → tflite`. Consumed by
7933
+ * `resolveDeviceEngine` (addon-pipeline) and the orchestrator's
7934
+ * `parseDeviceKey`/`getNodeInferenceDevices` — the previously-duplicated
7935
+ * `BACKEND_FORMAT` maps (R3/node-F2). `cpu`/`cuda` floor to onnx.
7936
+ */
7937
+ var DEVICE_BACKEND_TO_FORMAT = {
7938
+ openvino: "openvino",
7939
+ edgetpu: "tflite",
7940
+ coreml: "coreml",
7941
+ onnx: "onnx",
7942
+ cpu: "onnx",
7943
+ cuda: "onnx"
7944
+ };
7927
7945
  var COCO_80_LABELS = [
7928
7946
  {
7929
7947
  id: "person",
@@ -11941,20 +11959,6 @@ var pipelineExecutorCapability = {
11941
11959
  getSelectedEngine: method(_void(), PipelineEngineChoiceSchema),
11942
11960
  getDefaultSteps: method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)),
11943
11961
  /**
11944
- * Re-run the platform probe for the inference engine and write the
11945
- * detected runtime / backend / device back into the addon's own
11946
- * global settings store. Returns the engine that was picked so the
11947
- * UI can confirm without a second round-trip. Triggers the standard
11948
- * `requiresRestart` auto-restart flow because all three fields are
11949
- * marked restart-required. Replaces the legacy per-agent
11950
- * `pipeline-orchestrator.reprobeAgentHwAccel` surface for the
11951
- * engine dimension — each addon now owns its own probe.
11952
- */
11953
- reprobeEngine: method(_void(), PipelineEngineChoiceSchema, {
11954
- kind: "mutation",
11955
- auth: "admin"
11956
- }),
11957
- /**
11958
11962
  * Per-node detection-engine provisioning snapshot. Returns the live
11959
11963
  * state of the lazy runtime-provisioning machine on `nodeId`
11960
11964
  * (idle / installing / verifying / ready / failed). The UI pairs this
@@ -12585,6 +12589,19 @@ var RunnerLocalLoadSchema = object({
12585
12589
  avgInferenceTimeMs: number(),
12586
12590
  /** Total queue depth across motion + detection queues. */
12587
12591
  queueDepthTotal: number(),
12592
+ /**
12593
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12594
+ * this runner currently has attached cameras on, so the orchestrator's second
12595
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12596
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12597
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12598
+ */
12599
+ devices: array(object({
12600
+ deviceKey: string(),
12601
+ backend: string(),
12602
+ attachedCameras: number(),
12603
+ queueDepthTotal: number()
12604
+ })).default([]),
12588
12605
  /** Hardware capability flags reported by this node. */
12589
12606
  hardware: object({
12590
12607
  hasGpu: boolean(),
@@ -18016,13 +18033,11 @@ var PipelineTemplateSchema = object({
18016
18033
  createdAt: string(),
18017
18034
  updatedAt: string()
18018
18035
  });
18019
- var AgentAddonConfigSchema = object({
18020
- enabled: boolean(),
18036
+ var DeviceStepConfigSchema = object({
18021
18037
  modelId: string().optional(),
18022
- settings: record(string(), unknown()).readonly()
18038
+ settings: record(string(), unknown()).optional()
18023
18039
  });
18024
18040
  var AgentPipelineSettingsSchema = object({
18025
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
18026
18041
  maxCameras: number().int().nonnegative().nullable().default(null),
18027
18042
  /** Per-node detection weight (relative share for the quota balancer). */
18028
18043
  detectWeight: number().positive().optional(),
@@ -18049,13 +18064,18 @@ var AgentPipelineSettingsSchema = object({
18049
18064
  reachableHost: string().optional(),
18050
18065
  /**
18051
18066
  * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
18052
- * weight}. Absent / all-disabled ⇒ the node's single default accelerator
18053
- * (safe default); two+ enabled ⇒ the dispatcher balances detection sessions
18054
- * across them so they run CONCURRENTLY.
18067
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
18068
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
18069
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
18070
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
18071
+ * the default model/settings for every camera landing on that accelerator;
18072
+ * a stepId absent ⇒ the step uses that device's format default.
18055
18073
  */
18056
18074
  inferenceDevices: record(string(), object({
18057
18075
  enabled: boolean(),
18058
- weight: number().positive().optional()
18076
+ weight: number().positive().optional(),
18077
+ maxSessions: number().int().positive().optional(),
18078
+ steps: record(string(), DeviceStepConfigSchema).optional()
18059
18079
  })).optional()
18060
18080
  });
18061
18081
  var CameraPipelineForAgentSchema = object({
@@ -18066,14 +18086,13 @@ var CameraPipelineForAgentSchema = object({
18066
18086
  }).nullable()
18067
18087
  });
18068
18088
  var CameraStepOverridePatchSchema = object({
18069
- enabled: boolean().optional(),
18070
18089
  modelId: string().optional(),
18071
18090
  settings: record(string(), unknown()).readonly().optional()
18072
18091
  });
18073
18092
  var CameraPipelineSettingsSchema = object({
18074
18093
  pinnedAgentNodeId: string().optional(),
18075
18094
  stepToggles: record(string(), boolean()).optional(),
18076
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
18095
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
18077
18096
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
18078
18097
  });
18079
18098
  /**
@@ -18287,6 +18306,44 @@ var CameraStatusSchema = object({
18287
18306
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
18288
18307
  fetchedAt: number()
18289
18308
  });
18309
+ var NodeInferenceDeviceSchema = object({
18310
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
18311
+ key: string(),
18312
+ backend: string(),
18313
+ device: string(),
18314
+ format: _enum(MODEL_FORMATS),
18315
+ /** Whether the node's live probe reports the device as usable right now. */
18316
+ available: boolean(),
18317
+ /**
18318
+ * Whether this device participates in dispatch. AUTO default (spec C2):
18319
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
18320
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
18321
+ * not a balanced target). An explicit stored value always wins; a stored-only
18322
+ * (unavailable) key keeps its stored value.
18323
+ */
18324
+ enabled: boolean(),
18325
+ /** Relative balancer weight for the enabled device (default 1). */
18326
+ weight: number(),
18327
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
18328
+ maxSessions: number().nullable(),
18329
+ /** Object-detection model the executor defaults to for this deviceKey. */
18330
+ defaultModelId: string(),
18331
+ /**
18332
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
18333
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
18334
+ * Absent/empty ⇒ no base (every step uses its device format default). The
18335
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
18336
+ * available per format; this is the stored selection that becomes the
18337
+ * default for EVERY camera landing on this accelerator.
18338
+ */
18339
+ steps: record(string(), DeviceStepConfigSchema).optional()
18340
+ });
18341
+ var NodeInferenceDevicesSchema = object({
18342
+ nodeId: string(),
18343
+ /** False when the node's platform-probe was unreachable (no live device set). */
18344
+ reachable: boolean(),
18345
+ devices: array(NodeInferenceDeviceSchema).readonly()
18346
+ });
18290
18347
  method(object({
18291
18348
  deviceId: number(),
18292
18349
  agentNodeId: string()
@@ -18296,7 +18353,13 @@ method(object({
18296
18353
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
18297
18354
  kind: "mutation",
18298
18355
  auth: "admin"
18299
- }), method(_void(), object({ migrated: number() }), {
18356
+ }), method(object({
18357
+ deviceId: number(),
18358
+ deviceKey: string()
18359
+ }), object({ success: literal(true) }), {
18360
+ kind: "mutation",
18361
+ auth: "admin"
18362
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
18300
18363
  kind: "mutation",
18301
18364
  auth: "admin"
18302
18365
  }), method(_void(), array(PipelineAssignmentSchema).readonly()), method(object({ deviceId: number() }), PipelineAssignmentSchema.nullable()), method(_void(), array(AgentLoadSummarySchema).readonly()), method(_void(), GlobalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(object({ nodeId: string() }), CapabilityBindingsSchema), method(object({
@@ -18330,13 +18393,7 @@ method(object({
18330
18393
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
18331
18394
  nodeId: string(),
18332
18395
  settings: AgentPipelineSettingsSchema
18333
- })).readonly()), method(object({
18334
- agentNodeId: string(),
18335
- defaults: record(string(), AgentAddonConfigSchema)
18336
- }), object({ success: literal(true) }), {
18337
- kind: "mutation",
18338
- auth: "admin"
18339
- }), method(object({ agentNodeId: string() }), object({
18396
+ })).readonly()), method(object({ agentNodeId: string() }), object({
18340
18397
  success: boolean(),
18341
18398
  removed: boolean()
18342
18399
  }), {
@@ -18372,12 +18429,14 @@ method(object({
18372
18429
  agentNodeId: string(),
18373
18430
  inferenceDevices: record(string(), object({
18374
18431
  enabled: boolean(),
18375
- weight: number().positive().optional()
18432
+ weight: number().positive().optional(),
18433
+ maxSessions: number().int().positive().optional(),
18434
+ steps: record(string(), DeviceStepConfigSchema).optional()
18376
18435
  }))
18377
18436
  }), object({ success: literal(true) }), {
18378
18437
  kind: "mutation",
18379
18438
  auth: "admin"
18380
- }), method(object({ agentNodeId: string() }), object({
18439
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
18381
18440
  success: literal(true),
18382
18441
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
18383
18442
  effectiveModelId: string().nullable(),
@@ -18393,9 +18452,10 @@ method(object({
18393
18452
  }), object({ success: literal(true) }), {
18394
18453
  kind: "mutation",
18395
18454
  auth: "admin"
18396
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
18455
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
18397
18456
  deviceId: number(),
18398
18457
  agentNodeId: string(),
18458
+ deviceKey: string(),
18399
18459
  addonId: string(),
18400
18460
  patch: CameraStepOverridePatchSchema.nullable()
18401
18461
  }), object({ success: literal(true) }), {
@@ -18432,14 +18492,13 @@ method(object({
18432
18492
  });
18433
18493
  /**
18434
18494
  * server-management — per-NODE singleton capability for a node's ROOT
18435
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
18436
- * agents).
18495
+ * package lifecycle (runtime-updatable node packages).
18437
18496
  *
18438
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
18439
- * on agents) carries the whole software stack in its npm dep tree, so ONE
18440
- * version describes the node. Updates install into
18441
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
18442
- * starter (probation boot + auto-rollback to N-1).
18497
+ * Every node role runs the SAME root package (`@camstack/server`), which
18498
+ * carries the whole software stack in its npm dep tree, so ONE version
18499
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
18500
+ * on restart via the baked starter (single-copy in-place swap — no probation,
18501
+ * no auto-rollback).
18443
18502
  *
18444
18503
  * Providers:
18445
18504
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -18547,7 +18606,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
18547
18606
  /** Explicit target version; omitted = latest from the registry. */
18548
18607
  version: string().optional() }), ServerUpdateActionResultSchema, {
18549
18608
  kind: "mutation",
18550
- auth: "admin"
18609
+ auth: "admin",
18610
+ timeoutMs: 16 * 6e4
18551
18611
  }), method(_void(), ServerUpdateActionResultSchema, {
18552
18612
  kind: "mutation",
18553
18613
  auth: "admin"
@@ -20678,10 +20738,10 @@ var TopologyCategorySchema = object({
20678
20738
  addons: array(TopologyCategoryAddonSchema).readonly()
20679
20739
  });
20680
20740
  /**
20681
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
20682
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
20683
- * version visibility for the Server management surface. Nullable: offline
20684
- * rows and pre-phase-2 nodes report none.
20741
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
20742
+ * root package for every node role) as reported by its `registerNode`
20743
+ * manifest — version visibility for the Server management surface. Nullable:
20744
+ * offline rows and nodes that never reported one.
20685
20745
  */
20686
20746
  var TopologyRootPackageSchema = object({
20687
20747
  name: string(),
@@ -24804,12 +24864,6 @@ Object.freeze({
24804
24864
  addonId: null,
24805
24865
  access: "view"
24806
24866
  },
24807
- "pipelineExecutor.reprobeEngine": {
24808
- capName: "pipeline-executor",
24809
- capScope: "system",
24810
- addonId: null,
24811
- access: "create"
24812
- },
24813
24867
  "pipelineExecutor.runAudioTest": {
24814
24868
  capName: "pipeline-executor",
24815
24869
  capScope: "system",
@@ -24960,6 +25014,12 @@ Object.freeze({
24960
25014
  addonId: null,
24961
25015
  access: "view"
24962
25016
  },
25017
+ "pipelineOrchestrator.getNodeInferenceDevices": {
25018
+ capName: "pipeline-orchestrator",
25019
+ capScope: "system",
25020
+ addonId: null,
25021
+ access: "view"
25022
+ },
24963
25023
  "pipelineOrchestrator.getPipelineAssignment": {
24964
25024
  capName: "pipeline-orchestrator",
24965
25025
  capScope: "system",
@@ -24972,6 +25032,12 @@ Object.freeze({
24972
25032
  addonId: null,
24973
25033
  access: "view"
24974
25034
  },
25035
+ "pipelineOrchestrator.getPipelineDevicePin": {
25036
+ capName: "pipeline-orchestrator",
25037
+ capScope: "system",
25038
+ addonId: null,
25039
+ access: "view"
25040
+ },
24975
25041
  "pipelineOrchestrator.listAgentSettings": {
24976
25042
  capName: "pipeline-orchestrator",
24977
25043
  capScope: "system",
@@ -25014,12 +25080,6 @@ Object.freeze({
25014
25080
  addonId: null,
25015
25081
  access: "create"
25016
25082
  },
25017
- "pipelineOrchestrator.setAgentAddonDefaults": {
25018
- capName: "pipeline-orchestrator",
25019
- capScope: "system",
25020
- addonId: null,
25021
- access: "create"
25022
- },
25023
25083
  "pipelineOrchestrator.setAgentCapabilities": {
25024
25084
  capName: "pipeline-orchestrator",
25025
25085
  capScope: "system",
@@ -25074,6 +25134,12 @@ Object.freeze({
25074
25134
  addonId: null,
25075
25135
  access: "create"
25076
25136
  },
25137
+ "pipelineOrchestrator.setPipelineDevicePin": {
25138
+ capName: "pipeline-orchestrator",
25139
+ capScope: "system",
25140
+ addonId: null,
25141
+ access: "create"
25142
+ },
25077
25143
  "pipelineOrchestrator.unassignAudio": {
25078
25144
  capName: "pipeline-orchestrator",
25079
25145
  capScope: "system",
@@ -26828,6 +26894,119 @@ function runtimeDevices(id, hw) {
26828
26894
  function defaultDeviceFor(id) {
26829
26895
  return def(id).defaultDevice;
26830
26896
  }
26897
+ /**
26898
+ * Scores all applicable inference backends for the given hardware.
26899
+ *
26900
+ * Rules (hardware-driven only — no Python import probes):
26901
+ * - coreml: score 95, darwin+arm64
26902
+ * - cuda: score 85, nvidia gpu
26903
+ * - openvino: score 90 if intel-npu present, else 80 if intel gpu
26904
+ * - edgetpu: score 70, coral usb present
26905
+ * - cpu: score 50 (universal floor, always included)
26906
+ *
26907
+ * All hardware-matched entries are `available: true`.
26908
+ * Scores are sorted descending; `best` is the first available entry
26909
+ * (or last entry as fallback).
26910
+ */
26911
+ function scoreRuntimes(hw) {
26912
+ const scores = [];
26913
+ if (hw.platform === "darwin" && hw.arch === "arm64") scores.push({
26914
+ runtime: "python",
26915
+ backend: "coreml",
26916
+ format: "coreml",
26917
+ score: 95,
26918
+ reason: "Apple Neural Engine (Python CoreML)",
26919
+ available: true
26920
+ });
26921
+ if (hw.gpu?.type === "nvidia") scores.push({
26922
+ runtime: "python",
26923
+ backend: "cuda",
26924
+ format: "onnx",
26925
+ score: 85,
26926
+ reason: "NVIDIA CUDA (Python ONNX Runtime)",
26927
+ available: true
26928
+ });
26929
+ if (hw.gpu?.type === "intel") {
26930
+ const score = hw.npu?.type === "intel-npu" ? 90 : 80;
26931
+ scores.push({
26932
+ runtime: "python",
26933
+ backend: "openvino",
26934
+ format: "openvino",
26935
+ score,
26936
+ reason: "Intel OpenVINO",
26937
+ available: true
26938
+ });
26939
+ }
26940
+ if (hw.coral != null) scores.push({
26941
+ runtime: "python",
26942
+ backend: "edgetpu",
26943
+ format: "tflite",
26944
+ score: 70,
26945
+ reason: "Coral Edge TPU (Python LiteRT)",
26946
+ available: true
26947
+ });
26948
+ scores.push({
26949
+ runtime: "python",
26950
+ backend: "cpu",
26951
+ format: "onnx",
26952
+ score: 50,
26953
+ reason: "CPU (Python ONNX Runtime)",
26954
+ available: true
26955
+ });
26956
+ const sorted = scores.toSorted((a, b) => b.score - a.score);
26957
+ return {
26958
+ scores: sorted,
26959
+ best: sorted.find((s) => s.available) ?? sorted[sorted.length - 1]
26960
+ };
26961
+ }
26962
+ var DEVICE_FOR_BACKEND = {
26963
+ cuda: "gpu",
26964
+ edgetpu: "usb",
26965
+ coreml: "all",
26966
+ cpu: "cpu"
26967
+ };
26968
+ function device(backend, dev, format, score) {
26969
+ return {
26970
+ key: backend === "cpu" ? "cpu" : `${backend}:${dev}`,
26971
+ backend,
26972
+ device: dev,
26973
+ format,
26974
+ runtime: "python",
26975
+ score,
26976
+ available: true
26977
+ };
26978
+ }
26979
+ /**
26980
+ * Enumerate every usable inference device on a node — one descriptor PER PHYSICAL
26981
+ * ACCELERATOR, so the orchestrator can run and balance across all of them
26982
+ * CONCURRENTLY (each device is its own pool). Critically, OpenVINO is expanded
26983
+ * into its distinct devices (NPU / iGPU / CPU) rather than a single AUTO entry:
26984
+ * the Intel NPU ("AI Boost") and the iGPU are separate accelerators and run in
26985
+ * parallel — collapsing them to AUTO would leave one idle. NPU is scored above
26986
+ * the iGPU (faster for int8 detection AND frees the GPU).
26987
+ */
26988
+ function enumerateInferenceDevices(hw) {
26989
+ const { scores } = scoreRuntimes(hw);
26990
+ const out = [];
26991
+ for (const s of scores) {
26992
+ if (s.backend === "openvino") {
26993
+ if (hw.npu?.type === "intel-npu") out.push(device("openvino", "npu", s.format, s.score + 2));
26994
+ if (hw.gpu?.type === "intel") out.push(device("openvino", "gpu", s.format, s.score));
26995
+ continue;
26996
+ }
26997
+ if (s.backend === "edgetpu") {
26998
+ const coralCount = Math.max(1, hw.coral?.count ?? 1);
26999
+ for (let i = 0; i < coralCount; i++) {
27000
+ const dev = i === 0 ? "usb" : `usb:${i}`;
27001
+ out.push(device("edgetpu", dev, s.format, s.score));
27002
+ }
27003
+ continue;
27004
+ }
27005
+ const dev = DEVICE_FOR_BACKEND[s.backend] ?? s.backend;
27006
+ out.push(device(s.backend, dev, s.format, s.score));
27007
+ }
27008
+ return out;
27009
+ }
26831
27010
  //#endregion
26832
27011
  Object.defineProperty(exports, "APPLE_SA_TO_MACRO", {
26833
27012
  enumerable: true,
@@ -26877,6 +27056,12 @@ Object.defineProperty(exports, "DEFAULT_AUDIO_ANALYZER_CONFIG", {
26877
27056
  return DEFAULT_AUDIO_ANALYZER_CONFIG;
26878
27057
  }
26879
27058
  });
27059
+ Object.defineProperty(exports, "DEVICE_BACKEND_TO_FORMAT", {
27060
+ enumerable: true,
27061
+ get: function() {
27062
+ return DEVICE_BACKEND_TO_FORMAT;
27063
+ }
27064
+ });
26880
27065
  Object.defineProperty(exports, "DeviceFeature", {
26881
27066
  enumerable: true,
26882
27067
  get: function() {
@@ -27021,6 +27206,12 @@ Object.defineProperty(exports, "discriminatedUnion", {
27021
27206
  return discriminatedUnion;
27022
27207
  }
27023
27208
  });
27209
+ Object.defineProperty(exports, "enumerateInferenceDevices", {
27210
+ enumerable: true,
27211
+ get: function() {
27212
+ return enumerateInferenceDevices;
27213
+ }
27214
+ });
27024
27215
  Object.defineProperty(exports, "errMsg", {
27025
27216
  enumerable: true,
27026
27217
  get: function() {
@@ -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-BWn5k0A5.js");
5
+ const require_dist = require("../dist-DWy-H6bo.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 { C as evaluateZoneRules, K as hydrateSchema, O as motionDetectionCapability, V as BaseAddon, W as DeviceType } from "../dist-CurVt3xC.mjs";
1
+ import { A as motionDetectionCapability, J as hydrateSchema, K as DeviceType, T as evaluateZoneRules, U as BaseAddon } from "../dist-DLWXeXC1.mjs";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-D6vf50IK.js");
6
- const require_dist = require("../dist-BWn5k0A5.js");
6
+ const require_dist = require("../dist-DWy-H6bo.js");
7
7
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
8
8
  const require_sheet_geometry = require("../sheet-geometry-BR-ip0CP.js");
9
9
  const require_worker_protocol = require("../worker-protocol-PP4jKHHJ.js");
10
- const require_step_definitions = require("../step-definitions-B7e8rPe3.js");
10
+ const require_step_definitions = require("../step-definitions-BQG_6dmZ.js");
11
11
  let node_child_process = require("node:child_process");
12
12
  let node_url = require("node:url");
13
13
  let sharp = require("sharp");
@@ -1132,6 +1132,26 @@ var PipelineRunner = class {
1132
1132
  }
1133
1133
  };
1134
1134
  //#endregion
1135
+ //#region src/pipeline-runner/device-load.ts
1136
+ /** The backend prefix of a `<backend>:<device>` key (bare `cpu` → `cpu`). */
1137
+ function backendOf(deviceKey) {
1138
+ const colon = deviceKey.indexOf(":");
1139
+ return colon >= 0 ? deviceKey.slice(0, colon) : deviceKey;
1140
+ }
1141
+ function summarizeDeviceLoad(attachedDeviceKeys) {
1142
+ const counts = /* @__PURE__ */ new Map();
1143
+ for (const key of attachedDeviceKeys) {
1144
+ if (!key) continue;
1145
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1146
+ }
1147
+ return Array.from(counts.entries()).map(([deviceKey, attachedCameras]) => ({
1148
+ deviceKey,
1149
+ backend: backendOf(deviceKey),
1150
+ attachedCameras,
1151
+ queueDepthTotal: 0
1152
+ })).toSorted((a, b) => a.deviceKey.localeCompare(b.deviceKey));
1153
+ }
1154
+ //#endregion
1135
1155
  //#region src/session-decode/fork-decode-worker.ts
1136
1156
  /**
1137
1157
  * Real `fork` implementation for the session-decode coordinator — Epic C P1,
@@ -3459,6 +3479,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
3459
3479
  if (cm.phase === "active") activeCameras++;
3460
3480
  totalActualFps += cm.actualFps;
3461
3481
  }
3482
+ const devices = summarizeDeviceLoad(Array.from(this.attached.values(), (cam) => cam.config.deviceKey));
3462
3483
  return {
3463
3484
  nodeId: this.nodeId,
3464
3485
  attachedCameras: this.attached.size,
@@ -3466,6 +3487,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
3466
3487
  avgInferenceFps: totalActualFps,
3467
3488
  avgInferenceTimeMs: metrics.avgInferenceTimeMs,
3468
3489
  queueDepthTotal: metrics.queueDepth,
3490
+ devices,
3469
3491
  hardware: {
3470
3492
  hasGpu: false,
3471
3493
  inferenceBackend: void 0
@@ -1,8 +1,8 @@
1
- import { B as errMsg, G as createEvent, V as BaseAddon, dt as EventCategory, et as _enum, it as lazy, j as pipelineRunnerCapability, k as nodePin, lt as string, nt as boolean, ot as number, st as object, tt as array, x as defineCustomActions, y as customAction } from "../dist-CurVt3xC.mjs";
1
+ import { H as errMsg, N as pipelineRunnerCapability, S as defineCustomActions, U as BaseAddon, b as customAction, ct as number, dt as string, it as boolean, j as nodePin, lt as object, nt as _enum, ot as lazy, pt as EventCategory, q as createEvent, rt as array } from "../dist-DLWXeXC1.mjs";
2
2
  import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
3
3
  import { r as resolveHubHostname } from "../sheet-geometry-DcpuGTCm.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-CyVJTZEO.mjs";
5
- import { a as getStepDefinition } from "../step-definitions-BKm17nce.mjs";
5
+ import { a as getStepDefinition } from "../step-definitions-CVk19Y0u.mjs";
6
6
  import { fork } from "node:child_process";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import sharp from "sharp";
@@ -1126,6 +1126,26 @@ var PipelineRunner = class {
1126
1126
  }
1127
1127
  };
1128
1128
  //#endregion
1129
+ //#region src/pipeline-runner/device-load.ts
1130
+ /** The backend prefix of a `<backend>:<device>` key (bare `cpu` → `cpu`). */
1131
+ function backendOf(deviceKey) {
1132
+ const colon = deviceKey.indexOf(":");
1133
+ return colon >= 0 ? deviceKey.slice(0, colon) : deviceKey;
1134
+ }
1135
+ function summarizeDeviceLoad(attachedDeviceKeys) {
1136
+ const counts = /* @__PURE__ */ new Map();
1137
+ for (const key of attachedDeviceKeys) {
1138
+ if (!key) continue;
1139
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1140
+ }
1141
+ return Array.from(counts.entries()).map(([deviceKey, attachedCameras]) => ({
1142
+ deviceKey,
1143
+ backend: backendOf(deviceKey),
1144
+ attachedCameras,
1145
+ queueDepthTotal: 0
1146
+ })).toSorted((a, b) => a.deviceKey.localeCompare(b.deviceKey));
1147
+ }
1148
+ //#endregion
1129
1149
  //#region src/session-decode/fork-decode-worker.ts
1130
1150
  /**
1131
1151
  * Real `fork` implementation for the session-decode coordinator — Epic C P1,
@@ -3453,6 +3473,7 @@ var PipelineRunnerAddon = class extends BaseAddon {
3453
3473
  if (cm.phase === "active") activeCameras++;
3454
3474
  totalActualFps += cm.actualFps;
3455
3475
  }
3476
+ const devices = summarizeDeviceLoad(Array.from(this.attached.values(), (cam) => cam.config.deviceKey));
3456
3477
  return {
3457
3478
  nodeId: this.nodeId,
3458
3479
  attachedCameras: this.attached.size,
@@ -3460,6 +3481,7 @@ var PipelineRunnerAddon = class extends BaseAddon {
3460
3481
  avgInferenceFps: totalActualFps,
3461
3482
  avgInferenceTimeMs: metrics.avgInferenceTimeMs,
3462
3483
  queueDepthTotal: metrics.queueDepth,
3484
+ devices,
3463
3485
  hardware: {
3464
3486
  hasGpu: false,
3465
3487
  inferenceBackend: void 0
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("../chunk-D6vf50IK.js");
2
- const require_dist = require("../dist-BWn5k0A5.js");
2
+ const require_dist = require("../dist-DWy-H6bo.js");
3
3
  const require_sheet_geometry = require("../sheet-geometry-BR-ip0CP.js");
4
4
  const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Dv-oFwjN.js");
5
5
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { B as errMsg, D as migrateConfigToBands, I as storageEvictableCapability, K as hydrateSchema, M as recordingCapability, N as recordingExportCapability, P as resolveScrubThumbnailGeometry, Q as selectAssignedProfileSlots, V as BaseAddon, W as DeviceType, ct as record, d as OpsLogEntrySchema, dt as EventCategory, f as RecordingConfigSchema, k as nodePin, l as ExportRecordSchema, lt as string, q as isEvent, s as EVENT_PAD_MS, tt as array } from "../dist-CurVt3xC.mjs";
1
+ import { F as recordingExportCapability, H as errMsg, I as resolveScrubThumbnailGeometry, J as hydrateSchema, K as DeviceType, P as recordingCapability, R as storageEvictableCapability, U as BaseAddon, Y as isEvent, c as EVENT_PAD_MS, dt as string, et as selectAssignedProfileSlots, f as OpsLogEntrySchema, j as nodePin, k as migrateConfigToBands, p as RecordingConfigSchema, pt as EventCategory, rt as array, u as ExportRecordSchema, ut as record } from "../dist-DLWXeXC1.mjs";
2
2
  import { n as computeSheetGeometry, r as resolveHubHostname, t as allTilePlacements } from "../sheet-geometry-DcpuGTCm.mjs";
3
3
  import { n as createFileDataPlaneHandler, s as parseRangeHeader, t as contentTypeFor } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
4
4
  import { spawn } from "node:child_process";