@camstack/addon-provider-dreo 0.1.23 → 0.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 (3) hide show
  1. package/dist/addon.js +194 -104
  2. package/dist/addon.mjs +194 -104
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7120,6 +7120,17 @@ var ModelCatalogEntrySchema = object({
7120
7120
  "imagenet",
7121
7121
  "none"
7122
7122
  ]).optional(),
7123
+ /**
7124
+ * The model already applies softmax IN-GRAPH — its raw output is a
7125
+ * probability distribution, not logits. When set, the `softmax`
7126
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7127
+ * probability vector collapses it toward uniform (top-1 score craters far
7128
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7129
+ * the output is raw logits and the postprocessor applies softmax (the normal
7130
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7131
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7132
+ */
7133
+ outputProbabilities: boolean().optional(),
7123
7134
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7124
7135
  /**
7125
7136
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12410,10 +12421,7 @@ var ConfigUISchemaNullableBridge = custom();
12410
12421
  var InferenceCapabilitiesBridge = custom();
12411
12422
  var ModelAvailabilityListBridge = custom();
12412
12423
  var PipelineRunResultBridge = custom();
12413
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12414
- kind: "mutation",
12415
- auth: "admin"
12416
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12424
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12417
12425
  modelId: string(),
12418
12426
  settings: record(string(), unknown()).readonly()
12419
12427
  }))), method(object({ steps: record(string(), object({
@@ -12473,13 +12481,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12473
12481
  * (inputClasses ≠ null) are skipped and served per-track via
12474
12482
  * pipelineRunner.runDetailSubtree (two-plane design).
12475
12483
  */
12476
- plane: _enum(["full", "frame"]).optional()
12484
+ plane: _enum(["full", "frame"]).optional(),
12485
+ /**
12486
+ * Inference-device selector (Phase 2 multi-device). Format
12487
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12488
+ * Omitted ⇒ the runner's default device (current single-engine
12489
+ * behaviour). Selects WHICH device pool of the node runs the call.
12490
+ */
12491
+ deviceKey: string().optional()
12477
12492
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12478
12493
  engine: PipelineEngineChoiceSchema.optional(),
12479
12494
  steps: array(PipelineStepInputSchema).min(1),
12480
12495
  frames: array(FrameInputSchema).min(1).max(255),
12481
12496
  deviceId: number().optional(),
12482
- sessionId: string().optional()
12497
+ sessionId: string().optional(),
12498
+ /**
12499
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12500
+ * the batch to the Python pool's bench preprocess cache
12501
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12502
+ * preprocessed ONCE and every later inference is a pure-inference cache
12503
+ * hit — the sustained-throughput run measures inference, not
12504
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12505
+ * full preprocess every call, correct). Fresh per sustained run;
12506
+ * released via `uncacheFrame`.
12507
+ */
12508
+ frameId: number().int().nonnegative().optional(),
12509
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12510
+ deviceKey: string().optional()
12483
12511
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12484
12512
  data: _instanceof(Uint8Array),
12485
12513
  width: number().int().positive(),
@@ -12511,8 +12539,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12511
12539
  * - `runtime` — main camera-serving engine (no idle TTL).
12512
12540
  * - `warm-override` — benchmark/test override held in the warm
12513
12541
  * cache; auto-disposed after the idle TTL.
12542
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12543
+ * multi-device, keyed by `deviceKey`) resolved
12544
+ * via `resolveDeviceFactory`. Runs alongside the
12545
+ * `runtime` engine on a DIFFERENT accelerator
12546
+ * (NPU / iGPU / Coral) — this is how the
12547
+ * Engines tab shows all pools running at once.
12514
12548
  */
12515
- kind: _enum(["runtime", "warm-override"]),
12549
+ kind: _enum([
12550
+ "runtime",
12551
+ "warm-override",
12552
+ "device-pool"
12553
+ ]),
12516
12554
  /** Native pid of the underlying Python pool (null when no pool). */
12517
12555
  poolPid: number().nullable(),
12518
12556
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12887,7 +12925,14 @@ var RunnerCameraConfigSchema = object({
12887
12925
  * camera's detect node differs from its source-owner (P2d, gated by the
12888
12926
  * `remoteSourcingNodes` rollout setting).
12889
12927
  */
12890
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12928
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12929
+ /**
12930
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12931
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12932
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12933
+ * this only selects WHICH device pool of that node runs the session.
12934
+ */
12935
+ deviceKey: string().optional()
12891
12936
  });
12892
12937
  motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
12893
12938
  /**
@@ -12908,6 +12953,19 @@ var RunnerLocalLoadSchema = object({
12908
12953
  avgInferenceTimeMs: number(),
12909
12954
  /** Total queue depth across motion + detection queues. */
12910
12955
  queueDepthTotal: number(),
12956
+ /**
12957
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12958
+ * this runner currently has attached cameras on, so the orchestrator's second
12959
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12960
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12961
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12962
+ */
12963
+ devices: array(object({
12964
+ deviceKey: string(),
12965
+ backend: string(),
12966
+ attachedCameras: number(),
12967
+ queueDepthTotal: number()
12968
+ })).default([]),
12911
12969
  /** Hardware capability flags reported by this node. */
12912
12970
  hardware: object({
12913
12971
  hasGpu: boolean(),
@@ -16056,6 +16114,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16056
16114
  return toDeviceSummary(device, this.addonId);
16057
16115
  }
16058
16116
  };
16117
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
16118
+ new Set(Object.values(DeviceType));
16059
16119
  /**
16060
16120
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16061
16121
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19742,13 +19802,11 @@ var PipelineTemplateSchema = object({
19742
19802
  createdAt: string(),
19743
19803
  updatedAt: string()
19744
19804
  });
19745
- var AgentAddonConfigSchema = object({
19746
- enabled: boolean(),
19805
+ var DeviceStepConfigSchema = object({
19747
19806
  modelId: string().optional(),
19748
- settings: record(string(), unknown()).readonly()
19807
+ settings: record(string(), unknown()).optional()
19749
19808
  });
19750
19809
  var AgentPipelineSettingsSchema = object({
19751
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19752
19810
  maxCameras: number().int().nonnegative().nullable().default(null),
19753
19811
  /** Per-node detection weight (relative share for the quota balancer). */
19754
19812
  detectWeight: number().positive().optional(),
@@ -19772,7 +19830,22 @@ var AgentPipelineSettingsSchema = object({
19772
19830
  * it already uses to reach the hub). Set this only when the auto-detected
19773
19831
  * address is wrong (multi-homed host, NAT, custom interface).
19774
19832
  */
19775
- reachableHost: string().optional()
19833
+ reachableHost: string().optional(),
19834
+ /**
19835
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19836
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19837
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19838
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19839
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19840
+ * the default model/settings for every camera landing on that accelerator;
19841
+ * a stepId absent ⇒ the step uses that device's format default.
19842
+ */
19843
+ inferenceDevices: record(string(), object({
19844
+ enabled: boolean(),
19845
+ weight: number().positive().optional(),
19846
+ maxSessions: number().int().positive().optional(),
19847
+ steps: record(string(), DeviceStepConfigSchema).optional()
19848
+ })).optional()
19776
19849
  });
19777
19850
  var CameraPipelineForAgentSchema = object({
19778
19851
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19782,14 +19855,13 @@ var CameraPipelineForAgentSchema = object({
19782
19855
  }).nullable()
19783
19856
  });
19784
19857
  var CameraStepOverridePatchSchema = object({
19785
- enabled: boolean().optional(),
19786
19858
  modelId: string().optional(),
19787
19859
  settings: record(string(), unknown()).readonly().optional()
19788
19860
  });
19789
19861
  var CameraPipelineSettingsSchema = object({
19790
19862
  pinnedAgentNodeId: string().optional(),
19791
19863
  stepToggles: record(string(), boolean()).optional(),
19792
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19864
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19793
19865
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19794
19866
  });
19795
19867
  /**
@@ -20003,6 +20075,44 @@ var CameraStatusSchema = object({
20003
20075
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20004
20076
  fetchedAt: number()
20005
20077
  });
20078
+ var NodeInferenceDeviceSchema = object({
20079
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20080
+ key: string(),
20081
+ backend: string(),
20082
+ device: string(),
20083
+ format: _enum(MODEL_FORMATS),
20084
+ /** Whether the node's live probe reports the device as usable right now. */
20085
+ available: boolean(),
20086
+ /**
20087
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20088
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20089
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20090
+ * not a balanced target). An explicit stored value always wins; a stored-only
20091
+ * (unavailable) key keeps its stored value.
20092
+ */
20093
+ enabled: boolean(),
20094
+ /** Relative balancer weight for the enabled device (default 1). */
20095
+ weight: number(),
20096
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20097
+ maxSessions: number().nullable(),
20098
+ /** Object-detection model the executor defaults to for this deviceKey. */
20099
+ defaultModelId: string(),
20100
+ /**
20101
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20102
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20103
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20104
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20105
+ * available per format; this is the stored selection that becomes the
20106
+ * default for EVERY camera landing on this accelerator.
20107
+ */
20108
+ steps: record(string(), DeviceStepConfigSchema).optional()
20109
+ });
20110
+ var NodeInferenceDevicesSchema = object({
20111
+ nodeId: string(),
20112
+ /** False when the node's platform-probe was unreachable (no live device set). */
20113
+ reachable: boolean(),
20114
+ devices: array(NodeInferenceDeviceSchema).readonly()
20115
+ });
20006
20116
  method(object({
20007
20117
  deviceId: number(),
20008
20118
  agentNodeId: string()
@@ -20012,7 +20122,13 @@ method(object({
20012
20122
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20013
20123
  kind: "mutation",
20014
20124
  auth: "admin"
20015
- }), method(_void(), object({ migrated: number() }), {
20125
+ }), method(object({
20126
+ deviceId: number(),
20127
+ deviceKey: string()
20128
+ }), object({ success: literal(true) }), {
20129
+ kind: "mutation",
20130
+ auth: "admin"
20131
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20016
20132
  kind: "mutation",
20017
20133
  auth: "admin"
20018
20134
  }), 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({
@@ -20046,13 +20162,7 @@ method(object({
20046
20162
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20047
20163
  nodeId: string(),
20048
20164
  settings: AgentPipelineSettingsSchema
20049
- })).readonly()), method(object({
20050
- agentNodeId: string(),
20051
- defaults: record(string(), AgentAddonConfigSchema)
20052
- }), object({ success: literal(true) }), {
20053
- kind: "mutation",
20054
- auth: "admin"
20055
- }), method(object({ agentNodeId: string() }), object({
20165
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20056
20166
  success: boolean(),
20057
20167
  removed: boolean()
20058
20168
  }), {
@@ -20084,7 +20194,18 @@ method(object({
20084
20194
  }), object({ success: literal(true) }), {
20085
20195
  kind: "mutation",
20086
20196
  auth: "admin"
20087
- }), method(object({ agentNodeId: string() }), object({
20197
+ }), method(object({
20198
+ agentNodeId: string(),
20199
+ inferenceDevices: record(string(), object({
20200
+ enabled: boolean(),
20201
+ weight: number().positive().optional(),
20202
+ maxSessions: number().int().positive().optional(),
20203
+ steps: record(string(), DeviceStepConfigSchema).optional()
20204
+ }))
20205
+ }), object({ success: literal(true) }), {
20206
+ kind: "mutation",
20207
+ auth: "admin"
20208
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20088
20209
  success: literal(true),
20089
20210
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20090
20211
  effectiveModelId: string().nullable(),
@@ -20100,9 +20221,10 @@ method(object({
20100
20221
  }), object({ success: literal(true) }), {
20101
20222
  kind: "mutation",
20102
20223
  auth: "admin"
20103
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20224
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20104
20225
  deviceId: number(),
20105
20226
  agentNodeId: string(),
20227
+ deviceKey: string(),
20106
20228
  addonId: string(),
20107
20229
  patch: CameraStepOverridePatchSchema.nullable()
20108
20230
  }), object({ success: literal(true) }), {
@@ -20139,14 +20261,13 @@ method(object({
20139
20261
  });
20140
20262
  /**
20141
20263
  * server-management — per-NODE singleton capability for a node's ROOT
20142
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20143
- * agents).
20264
+ * package lifecycle (runtime-updatable node packages).
20144
20265
  *
20145
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20146
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20147
- * version describes the node. Updates install into
20148
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20149
- * starter (probation boot + auto-rollback to N-1).
20266
+ * Every node role runs the SAME root package (`@camstack/server`), which
20267
+ * carries the whole software stack in its npm dep tree, so ONE version
20268
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20269
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20270
+ * no auto-rollback).
20150
20271
  *
20151
20272
  * Providers:
20152
20273
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20254,7 +20375,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20254
20375
  /** Explicit target version; omitted = latest from the registry. */
20255
20376
  version: string().optional() }), ServerUpdateActionResultSchema, {
20256
20377
  kind: "mutation",
20257
- auth: "admin"
20378
+ auth: "admin",
20379
+ timeoutMs: 16 * 6e4
20258
20380
  }), method(_void(), ServerUpdateActionResultSchema, {
20259
20381
  kind: "mutation",
20260
20382
  auth: "admin"
@@ -21298,22 +21420,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21298
21420
  var RestartAddonResultSchema = unknown();
21299
21421
  var InstallPackageResultSchema = unknown();
21300
21422
  var ReloadPackagesResultSchema = unknown();
21301
- /**
21302
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21303
- * server restarts so the admin UI can react to the `restartingAt`
21304
- * timestamp (shows reconnect overlay). The transition from
21305
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21306
- * `system.restart-completed` event after the new process boots.
21307
- *
21308
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21309
- */
21310
- var UpdateFrameworkPackageResultSchema = object({
21311
- packageName: string(),
21312
- fromVersion: string(),
21313
- toVersion: string(),
21314
- /** Ms-epoch the server scheduled its self-restart. */
21315
- restartingAt: number()
21316
- });
21317
21423
  var BulkUpdateItemStatusSchema = _enum([
21318
21424
  "queued",
21319
21425
  "updating",
@@ -21441,13 +21547,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21441
21547
  }), object({ success: literal(true) }), {
21442
21548
  kind: "mutation",
21443
21549
  auth: "admin"
21444
- }), method(object({
21445
- packageName: string().min(1),
21446
- version: string().optional(),
21447
- deferRestart: boolean().optional()
21448
- }), UpdateFrameworkPackageResultSchema, {
21449
- kind: "mutation",
21450
- auth: "admin"
21451
21550
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21452
21551
  kind: "mutation",
21453
21552
  auth: "admin"
@@ -22313,10 +22412,10 @@ var TopologyCategorySchema = object({
22313
22412
  addons: array(TopologyCategoryAddonSchema).readonly()
22314
22413
  });
22315
22414
  /**
22316
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22317
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22318
- * version visibility for the Server management surface. Nullable: offline
22319
- * rows and pre-phase-2 nodes report none.
22415
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22416
+ * root package for every node role) as reported by its `registerNode`
22417
+ * manifest — version visibility for the Server management surface. Nullable:
22418
+ * offline rows and nodes that never reported one.
22320
22419
  */
22321
22420
  var TopologyRootPackageSchema = object({
22322
22421
  name: string(),
@@ -22704,17 +22803,28 @@ var PlatformScoreSchema = object({
22704
22803
  format: _enum([
22705
22804
  "onnx",
22706
22805
  "coreml",
22707
- "openvino"
22806
+ "openvino",
22807
+ "tflite"
22708
22808
  ]),
22709
22809
  score: number(),
22710
22810
  reason: string(),
22711
22811
  available: boolean()
22712
22812
  });
22813
+ var InferenceDeviceDescriptorSchema = object({
22814
+ key: string(),
22815
+ backend: string(),
22816
+ device: string(),
22817
+ format: ModelFormatSchema,
22818
+ runtime: literal("python"),
22819
+ score: number(),
22820
+ available: boolean()
22821
+ });
22713
22822
  var PlatformCapabilitiesSchema = object({
22714
22823
  hardware: HardwareInfoSchema,
22715
22824
  scores: array(PlatformScoreSchema).readonly(),
22716
22825
  bestScore: PlatformScoreSchema,
22717
- pythonPath: string().nullable()
22826
+ pythonPath: string().nullable(),
22827
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22718
22828
  });
22719
22829
  var ModelRequirementSchema = object({
22720
22830
  modelId: string(),
@@ -23593,12 +23703,6 @@ Object.freeze({
23593
23703
  addonId: null,
23594
23704
  access: "delete"
23595
23705
  },
23596
- "addons.updateFrameworkPackage": {
23597
- capName: "addons",
23598
- capScope: "system",
23599
- addonId: null,
23600
- access: "create"
23601
- },
23602
23706
  "addons.updatePackage": {
23603
23707
  capName: "addons",
23604
23708
  capScope: "system",
@@ -26371,12 +26475,6 @@ Object.freeze({
26371
26475
  addonId: null,
26372
26476
  access: "view"
26373
26477
  },
26374
- "pipelineExecutor.reprobeEngine": {
26375
- capName: "pipeline-executor",
26376
- capScope: "system",
26377
- addonId: null,
26378
- access: "create"
26379
- },
26380
26478
  "pipelineExecutor.runAudioTest": {
26381
26479
  capName: "pipeline-executor",
26382
26480
  capScope: "system",
@@ -26527,6 +26625,12 @@ Object.freeze({
26527
26625
  addonId: null,
26528
26626
  access: "view"
26529
26627
  },
26628
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26629
+ capName: "pipeline-orchestrator",
26630
+ capScope: "system",
26631
+ addonId: null,
26632
+ access: "view"
26633
+ },
26530
26634
  "pipelineOrchestrator.getPipelineAssignment": {
26531
26635
  capName: "pipeline-orchestrator",
26532
26636
  capScope: "system",
@@ -26539,6 +26643,12 @@ Object.freeze({
26539
26643
  addonId: null,
26540
26644
  access: "view"
26541
26645
  },
26646
+ "pipelineOrchestrator.getPipelineDevicePin": {
26647
+ capName: "pipeline-orchestrator",
26648
+ capScope: "system",
26649
+ addonId: null,
26650
+ access: "view"
26651
+ },
26542
26652
  "pipelineOrchestrator.listAgentSettings": {
26543
26653
  capName: "pipeline-orchestrator",
26544
26654
  capScope: "system",
@@ -26581,19 +26691,19 @@ Object.freeze({
26581
26691
  addonId: null,
26582
26692
  access: "create"
26583
26693
  },
26584
- "pipelineOrchestrator.setAgentAddonDefaults": {
26694
+ "pipelineOrchestrator.setAgentCapabilities": {
26585
26695
  capName: "pipeline-orchestrator",
26586
26696
  capScope: "system",
26587
26697
  addonId: null,
26588
26698
  access: "create"
26589
26699
  },
26590
- "pipelineOrchestrator.setAgentCapabilities": {
26700
+ "pipelineOrchestrator.setAgentDetectWeight": {
26591
26701
  capName: "pipeline-orchestrator",
26592
26702
  capScope: "system",
26593
26703
  addonId: null,
26594
26704
  access: "create"
26595
26705
  },
26596
- "pipelineOrchestrator.setAgentDetectWeight": {
26706
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26597
26707
  capName: "pipeline-orchestrator",
26598
26708
  capScope: "system",
26599
26709
  addonId: null,
@@ -26635,6 +26745,12 @@ Object.freeze({
26635
26745
  addonId: null,
26636
26746
  access: "create"
26637
26747
  },
26748
+ "pipelineOrchestrator.setPipelineDevicePin": {
26749
+ capName: "pipeline-orchestrator",
26750
+ capScope: "system",
26751
+ addonId: null,
26752
+ access: "create"
26753
+ },
26638
26754
  "pipelineOrchestrator.unassignAudio": {
26639
26755
  capName: "pipeline-orchestrator",
26640
26756
  capScope: "system",
@@ -28187,32 +28303,6 @@ Object.freeze({
28187
28303
  "network-access": "ingress",
28188
28304
  "smtp-provider": "email"
28189
28305
  });
28190
- var frameworkSwapPackageSchema = object({
28191
- name: string(),
28192
- stagedPath: string(),
28193
- backupPath: string(),
28194
- toVersion: string(),
28195
- fromVersion: string().nullable()
28196
- });
28197
- object({
28198
- jobId: string(),
28199
- taskId: string(),
28200
- packages: array(frameworkSwapPackageSchema),
28201
- requestedAtMs: number(),
28202
- schemaVersion: literal(1)
28203
- });
28204
- object({
28205
- jobId: string(),
28206
- taskId: string(),
28207
- backups: array(object({
28208
- name: string(),
28209
- backupPath: string(),
28210
- livePath: string()
28211
- })),
28212
- appliedAtMs: number(),
28213
- bootAttempts: number(),
28214
- schemaVersion: literal(1)
28215
- });
28216
28306
  //#endregion
28217
28307
  //#region src/config.ts
28218
28308
  /**
@@ -31888,7 +31978,7 @@ var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
31888
31978
  require_websocket_server();
31889
31979
  var wrapper_default = import_websocket.default;
31890
31980
  //#endregion
31891
- //#region node_modules/@apocaliss92/nodedreo/dist/index.js
31981
+ //#region ../../node_modules/@apocaliss92/nodedreo/dist/index.js
31892
31982
  var DreoError = class extends Error {
31893
31983
  constructor(message) {
31894
31984
  super(message);
package/dist/addon.mjs CHANGED
@@ -7121,6 +7121,17 @@ var ModelCatalogEntrySchema = object({
7121
7121
  "imagenet",
7122
7122
  "none"
7123
7123
  ]).optional(),
7124
+ /**
7125
+ * The model already applies softmax IN-GRAPH — its raw output is a
7126
+ * probability distribution, not logits. When set, the `softmax`
7127
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7128
+ * probability vector collapses it toward uniform (top-1 score craters far
7129
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7130
+ * the output is raw logits and the postprocessor applies softmax (the normal
7131
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7132
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7133
+ */
7134
+ outputProbabilities: boolean().optional(),
7124
7135
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7125
7136
  /**
7126
7137
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12411,10 +12422,7 @@ var ConfigUISchemaNullableBridge = custom();
12411
12422
  var InferenceCapabilitiesBridge = custom();
12412
12423
  var ModelAvailabilityListBridge = custom();
12413
12424
  var PipelineRunResultBridge = custom();
12414
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12415
- kind: "mutation",
12416
- auth: "admin"
12417
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12425
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12418
12426
  modelId: string(),
12419
12427
  settings: record(string(), unknown()).readonly()
12420
12428
  }))), method(object({ steps: record(string(), object({
@@ -12474,13 +12482,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12474
12482
  * (inputClasses ≠ null) are skipped and served per-track via
12475
12483
  * pipelineRunner.runDetailSubtree (two-plane design).
12476
12484
  */
12477
- plane: _enum(["full", "frame"]).optional()
12485
+ plane: _enum(["full", "frame"]).optional(),
12486
+ /**
12487
+ * Inference-device selector (Phase 2 multi-device). Format
12488
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12489
+ * Omitted ⇒ the runner's default device (current single-engine
12490
+ * behaviour). Selects WHICH device pool of the node runs the call.
12491
+ */
12492
+ deviceKey: string().optional()
12478
12493
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12479
12494
  engine: PipelineEngineChoiceSchema.optional(),
12480
12495
  steps: array(PipelineStepInputSchema).min(1),
12481
12496
  frames: array(FrameInputSchema).min(1).max(255),
12482
12497
  deviceId: number().optional(),
12483
- sessionId: string().optional()
12498
+ sessionId: string().optional(),
12499
+ /**
12500
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12501
+ * the batch to the Python pool's bench preprocess cache
12502
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12503
+ * preprocessed ONCE and every later inference is a pure-inference cache
12504
+ * hit — the sustained-throughput run measures inference, not
12505
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12506
+ * full preprocess every call, correct). Fresh per sustained run;
12507
+ * released via `uncacheFrame`.
12508
+ */
12509
+ frameId: number().int().nonnegative().optional(),
12510
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12511
+ deviceKey: string().optional()
12484
12512
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12485
12513
  data: _instanceof(Uint8Array),
12486
12514
  width: number().int().positive(),
@@ -12512,8 +12540,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12512
12540
  * - `runtime` — main camera-serving engine (no idle TTL).
12513
12541
  * - `warm-override` — benchmark/test override held in the warm
12514
12542
  * cache; auto-disposed after the idle TTL.
12543
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12544
+ * multi-device, keyed by `deviceKey`) resolved
12545
+ * via `resolveDeviceFactory`. Runs alongside the
12546
+ * `runtime` engine on a DIFFERENT accelerator
12547
+ * (NPU / iGPU / Coral) — this is how the
12548
+ * Engines tab shows all pools running at once.
12515
12549
  */
12516
- kind: _enum(["runtime", "warm-override"]),
12550
+ kind: _enum([
12551
+ "runtime",
12552
+ "warm-override",
12553
+ "device-pool"
12554
+ ]),
12517
12555
  /** Native pid of the underlying Python pool (null when no pool). */
12518
12556
  poolPid: number().nullable(),
12519
12557
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12888,7 +12926,14 @@ var RunnerCameraConfigSchema = object({
12888
12926
  * camera's detect node differs from its source-owner (P2d, gated by the
12889
12927
  * `remoteSourcingNodes` rollout setting).
12890
12928
  */
12891
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12929
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12930
+ /**
12931
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12932
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12933
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12934
+ * this only selects WHICH device pool of that node runs the session.
12935
+ */
12936
+ deviceKey: string().optional()
12892
12937
  });
12893
12938
  motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
12894
12939
  /**
@@ -12909,6 +12954,19 @@ var RunnerLocalLoadSchema = object({
12909
12954
  avgInferenceTimeMs: number(),
12910
12955
  /** Total queue depth across motion + detection queues. */
12911
12956
  queueDepthTotal: number(),
12957
+ /**
12958
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12959
+ * this runner currently has attached cameras on, so the orchestrator's second
12960
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12961
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12962
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12963
+ */
12964
+ devices: array(object({
12965
+ deviceKey: string(),
12966
+ backend: string(),
12967
+ attachedCameras: number(),
12968
+ queueDepthTotal: number()
12969
+ })).default([]),
12912
12970
  /** Hardware capability flags reported by this node. */
12913
12971
  hardware: object({
12914
12972
  hasGpu: boolean(),
@@ -16057,6 +16115,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16057
16115
  return toDeviceSummary(device, this.addonId);
16058
16116
  }
16059
16117
  };
16118
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
16119
+ new Set(Object.values(DeviceType));
16060
16120
  /**
16061
16121
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16062
16122
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19743,13 +19803,11 @@ var PipelineTemplateSchema = object({
19743
19803
  createdAt: string(),
19744
19804
  updatedAt: string()
19745
19805
  });
19746
- var AgentAddonConfigSchema = object({
19747
- enabled: boolean(),
19806
+ var DeviceStepConfigSchema = object({
19748
19807
  modelId: string().optional(),
19749
- settings: record(string(), unknown()).readonly()
19808
+ settings: record(string(), unknown()).optional()
19750
19809
  });
19751
19810
  var AgentPipelineSettingsSchema = object({
19752
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19753
19811
  maxCameras: number().int().nonnegative().nullable().default(null),
19754
19812
  /** Per-node detection weight (relative share for the quota balancer). */
19755
19813
  detectWeight: number().positive().optional(),
@@ -19773,7 +19831,22 @@ var AgentPipelineSettingsSchema = object({
19773
19831
  * it already uses to reach the hub). Set this only when the auto-detected
19774
19832
  * address is wrong (multi-homed host, NAT, custom interface).
19775
19833
  */
19776
- reachableHost: string().optional()
19834
+ reachableHost: string().optional(),
19835
+ /**
19836
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19837
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19838
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19839
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19840
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19841
+ * the default model/settings for every camera landing on that accelerator;
19842
+ * a stepId absent ⇒ the step uses that device's format default.
19843
+ */
19844
+ inferenceDevices: record(string(), object({
19845
+ enabled: boolean(),
19846
+ weight: number().positive().optional(),
19847
+ maxSessions: number().int().positive().optional(),
19848
+ steps: record(string(), DeviceStepConfigSchema).optional()
19849
+ })).optional()
19777
19850
  });
19778
19851
  var CameraPipelineForAgentSchema = object({
19779
19852
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19783,14 +19856,13 @@ var CameraPipelineForAgentSchema = object({
19783
19856
  }).nullable()
19784
19857
  });
19785
19858
  var CameraStepOverridePatchSchema = object({
19786
- enabled: boolean().optional(),
19787
19859
  modelId: string().optional(),
19788
19860
  settings: record(string(), unknown()).readonly().optional()
19789
19861
  });
19790
19862
  var CameraPipelineSettingsSchema = object({
19791
19863
  pinnedAgentNodeId: string().optional(),
19792
19864
  stepToggles: record(string(), boolean()).optional(),
19793
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19865
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19794
19866
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19795
19867
  });
19796
19868
  /**
@@ -20004,6 +20076,44 @@ var CameraStatusSchema = object({
20004
20076
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20005
20077
  fetchedAt: number()
20006
20078
  });
20079
+ var NodeInferenceDeviceSchema = object({
20080
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20081
+ key: string(),
20082
+ backend: string(),
20083
+ device: string(),
20084
+ format: _enum(MODEL_FORMATS),
20085
+ /** Whether the node's live probe reports the device as usable right now. */
20086
+ available: boolean(),
20087
+ /**
20088
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20089
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20090
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20091
+ * not a balanced target). An explicit stored value always wins; a stored-only
20092
+ * (unavailable) key keeps its stored value.
20093
+ */
20094
+ enabled: boolean(),
20095
+ /** Relative balancer weight for the enabled device (default 1). */
20096
+ weight: number(),
20097
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20098
+ maxSessions: number().nullable(),
20099
+ /** Object-detection model the executor defaults to for this deviceKey. */
20100
+ defaultModelId: string(),
20101
+ /**
20102
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20103
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20104
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20105
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20106
+ * available per format; this is the stored selection that becomes the
20107
+ * default for EVERY camera landing on this accelerator.
20108
+ */
20109
+ steps: record(string(), DeviceStepConfigSchema).optional()
20110
+ });
20111
+ var NodeInferenceDevicesSchema = object({
20112
+ nodeId: string(),
20113
+ /** False when the node's platform-probe was unreachable (no live device set). */
20114
+ reachable: boolean(),
20115
+ devices: array(NodeInferenceDeviceSchema).readonly()
20116
+ });
20007
20117
  method(object({
20008
20118
  deviceId: number(),
20009
20119
  agentNodeId: string()
@@ -20013,7 +20123,13 @@ method(object({
20013
20123
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20014
20124
  kind: "mutation",
20015
20125
  auth: "admin"
20016
- }), method(_void(), object({ migrated: number() }), {
20126
+ }), method(object({
20127
+ deviceId: number(),
20128
+ deviceKey: string()
20129
+ }), object({ success: literal(true) }), {
20130
+ kind: "mutation",
20131
+ auth: "admin"
20132
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20017
20133
  kind: "mutation",
20018
20134
  auth: "admin"
20019
20135
  }), 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({
@@ -20047,13 +20163,7 @@ method(object({
20047
20163
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20048
20164
  nodeId: string(),
20049
20165
  settings: AgentPipelineSettingsSchema
20050
- })).readonly()), method(object({
20051
- agentNodeId: string(),
20052
- defaults: record(string(), AgentAddonConfigSchema)
20053
- }), object({ success: literal(true) }), {
20054
- kind: "mutation",
20055
- auth: "admin"
20056
- }), method(object({ agentNodeId: string() }), object({
20166
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20057
20167
  success: boolean(),
20058
20168
  removed: boolean()
20059
20169
  }), {
@@ -20085,7 +20195,18 @@ method(object({
20085
20195
  }), object({ success: literal(true) }), {
20086
20196
  kind: "mutation",
20087
20197
  auth: "admin"
20088
- }), method(object({ agentNodeId: string() }), object({
20198
+ }), method(object({
20199
+ agentNodeId: string(),
20200
+ inferenceDevices: record(string(), object({
20201
+ enabled: boolean(),
20202
+ weight: number().positive().optional(),
20203
+ maxSessions: number().int().positive().optional(),
20204
+ steps: record(string(), DeviceStepConfigSchema).optional()
20205
+ }))
20206
+ }), object({ success: literal(true) }), {
20207
+ kind: "mutation",
20208
+ auth: "admin"
20209
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20089
20210
  success: literal(true),
20090
20211
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20091
20212
  effectiveModelId: string().nullable(),
@@ -20101,9 +20222,10 @@ method(object({
20101
20222
  }), object({ success: literal(true) }), {
20102
20223
  kind: "mutation",
20103
20224
  auth: "admin"
20104
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20225
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20105
20226
  deviceId: number(),
20106
20227
  agentNodeId: string(),
20228
+ deviceKey: string(),
20107
20229
  addonId: string(),
20108
20230
  patch: CameraStepOverridePatchSchema.nullable()
20109
20231
  }), object({ success: literal(true) }), {
@@ -20140,14 +20262,13 @@ method(object({
20140
20262
  });
20141
20263
  /**
20142
20264
  * server-management — per-NODE singleton capability for a node's ROOT
20143
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20144
- * agents).
20265
+ * package lifecycle (runtime-updatable node packages).
20145
20266
  *
20146
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20147
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20148
- * version describes the node. Updates install into
20149
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20150
- * starter (probation boot + auto-rollback to N-1).
20267
+ * Every node role runs the SAME root package (`@camstack/server`), which
20268
+ * carries the whole software stack in its npm dep tree, so ONE version
20269
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20270
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20271
+ * no auto-rollback).
20151
20272
  *
20152
20273
  * Providers:
20153
20274
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20255,7 +20376,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20255
20376
  /** Explicit target version; omitted = latest from the registry. */
20256
20377
  version: string().optional() }), ServerUpdateActionResultSchema, {
20257
20378
  kind: "mutation",
20258
- auth: "admin"
20379
+ auth: "admin",
20380
+ timeoutMs: 16 * 6e4
20259
20381
  }), method(_void(), ServerUpdateActionResultSchema, {
20260
20382
  kind: "mutation",
20261
20383
  auth: "admin"
@@ -21299,22 +21421,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21299
21421
  var RestartAddonResultSchema = unknown();
21300
21422
  var InstallPackageResultSchema = unknown();
21301
21423
  var ReloadPackagesResultSchema = unknown();
21302
- /**
21303
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21304
- * server restarts so the admin UI can react to the `restartingAt`
21305
- * timestamp (shows reconnect overlay). The transition from
21306
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21307
- * `system.restart-completed` event after the new process boots.
21308
- *
21309
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21310
- */
21311
- var UpdateFrameworkPackageResultSchema = object({
21312
- packageName: string(),
21313
- fromVersion: string(),
21314
- toVersion: string(),
21315
- /** Ms-epoch the server scheduled its self-restart. */
21316
- restartingAt: number()
21317
- });
21318
21424
  var BulkUpdateItemStatusSchema = _enum([
21319
21425
  "queued",
21320
21426
  "updating",
@@ -21442,13 +21548,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21442
21548
  }), object({ success: literal(true) }), {
21443
21549
  kind: "mutation",
21444
21550
  auth: "admin"
21445
- }), method(object({
21446
- packageName: string().min(1),
21447
- version: string().optional(),
21448
- deferRestart: boolean().optional()
21449
- }), UpdateFrameworkPackageResultSchema, {
21450
- kind: "mutation",
21451
- auth: "admin"
21452
21551
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21453
21552
  kind: "mutation",
21454
21553
  auth: "admin"
@@ -22314,10 +22413,10 @@ var TopologyCategorySchema = object({
22314
22413
  addons: array(TopologyCategoryAddonSchema).readonly()
22315
22414
  });
22316
22415
  /**
22317
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22318
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22319
- * version visibility for the Server management surface. Nullable: offline
22320
- * rows and pre-phase-2 nodes report none.
22416
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22417
+ * root package for every node role) as reported by its `registerNode`
22418
+ * manifest — version visibility for the Server management surface. Nullable:
22419
+ * offline rows and nodes that never reported one.
22321
22420
  */
22322
22421
  var TopologyRootPackageSchema = object({
22323
22422
  name: string(),
@@ -22705,17 +22804,28 @@ var PlatformScoreSchema = object({
22705
22804
  format: _enum([
22706
22805
  "onnx",
22707
22806
  "coreml",
22708
- "openvino"
22807
+ "openvino",
22808
+ "tflite"
22709
22809
  ]),
22710
22810
  score: number(),
22711
22811
  reason: string(),
22712
22812
  available: boolean()
22713
22813
  });
22814
+ var InferenceDeviceDescriptorSchema = object({
22815
+ key: string(),
22816
+ backend: string(),
22817
+ device: string(),
22818
+ format: ModelFormatSchema,
22819
+ runtime: literal("python"),
22820
+ score: number(),
22821
+ available: boolean()
22822
+ });
22714
22823
  var PlatformCapabilitiesSchema = object({
22715
22824
  hardware: HardwareInfoSchema,
22716
22825
  scores: array(PlatformScoreSchema).readonly(),
22717
22826
  bestScore: PlatformScoreSchema,
22718
- pythonPath: string().nullable()
22827
+ pythonPath: string().nullable(),
22828
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22719
22829
  });
22720
22830
  var ModelRequirementSchema = object({
22721
22831
  modelId: string(),
@@ -23594,12 +23704,6 @@ Object.freeze({
23594
23704
  addonId: null,
23595
23705
  access: "delete"
23596
23706
  },
23597
- "addons.updateFrameworkPackage": {
23598
- capName: "addons",
23599
- capScope: "system",
23600
- addonId: null,
23601
- access: "create"
23602
- },
23603
23707
  "addons.updatePackage": {
23604
23708
  capName: "addons",
23605
23709
  capScope: "system",
@@ -26372,12 +26476,6 @@ Object.freeze({
26372
26476
  addonId: null,
26373
26477
  access: "view"
26374
26478
  },
26375
- "pipelineExecutor.reprobeEngine": {
26376
- capName: "pipeline-executor",
26377
- capScope: "system",
26378
- addonId: null,
26379
- access: "create"
26380
- },
26381
26479
  "pipelineExecutor.runAudioTest": {
26382
26480
  capName: "pipeline-executor",
26383
26481
  capScope: "system",
@@ -26528,6 +26626,12 @@ Object.freeze({
26528
26626
  addonId: null,
26529
26627
  access: "view"
26530
26628
  },
26629
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26630
+ capName: "pipeline-orchestrator",
26631
+ capScope: "system",
26632
+ addonId: null,
26633
+ access: "view"
26634
+ },
26531
26635
  "pipelineOrchestrator.getPipelineAssignment": {
26532
26636
  capName: "pipeline-orchestrator",
26533
26637
  capScope: "system",
@@ -26540,6 +26644,12 @@ Object.freeze({
26540
26644
  addonId: null,
26541
26645
  access: "view"
26542
26646
  },
26647
+ "pipelineOrchestrator.getPipelineDevicePin": {
26648
+ capName: "pipeline-orchestrator",
26649
+ capScope: "system",
26650
+ addonId: null,
26651
+ access: "view"
26652
+ },
26543
26653
  "pipelineOrchestrator.listAgentSettings": {
26544
26654
  capName: "pipeline-orchestrator",
26545
26655
  capScope: "system",
@@ -26582,19 +26692,19 @@ Object.freeze({
26582
26692
  addonId: null,
26583
26693
  access: "create"
26584
26694
  },
26585
- "pipelineOrchestrator.setAgentAddonDefaults": {
26695
+ "pipelineOrchestrator.setAgentCapabilities": {
26586
26696
  capName: "pipeline-orchestrator",
26587
26697
  capScope: "system",
26588
26698
  addonId: null,
26589
26699
  access: "create"
26590
26700
  },
26591
- "pipelineOrchestrator.setAgentCapabilities": {
26701
+ "pipelineOrchestrator.setAgentDetectWeight": {
26592
26702
  capName: "pipeline-orchestrator",
26593
26703
  capScope: "system",
26594
26704
  addonId: null,
26595
26705
  access: "create"
26596
26706
  },
26597
- "pipelineOrchestrator.setAgentDetectWeight": {
26707
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26598
26708
  capName: "pipeline-orchestrator",
26599
26709
  capScope: "system",
26600
26710
  addonId: null,
@@ -26636,6 +26746,12 @@ Object.freeze({
26636
26746
  addonId: null,
26637
26747
  access: "create"
26638
26748
  },
26749
+ "pipelineOrchestrator.setPipelineDevicePin": {
26750
+ capName: "pipeline-orchestrator",
26751
+ capScope: "system",
26752
+ addonId: null,
26753
+ access: "create"
26754
+ },
26639
26755
  "pipelineOrchestrator.unassignAudio": {
26640
26756
  capName: "pipeline-orchestrator",
26641
26757
  capScope: "system",
@@ -28188,32 +28304,6 @@ Object.freeze({
28188
28304
  "network-access": "ingress",
28189
28305
  "smtp-provider": "email"
28190
28306
  });
28191
- var frameworkSwapPackageSchema = object({
28192
- name: string(),
28193
- stagedPath: string(),
28194
- backupPath: string(),
28195
- toVersion: string(),
28196
- fromVersion: string().nullable()
28197
- });
28198
- object({
28199
- jobId: string(),
28200
- taskId: string(),
28201
- packages: array(frameworkSwapPackageSchema),
28202
- requestedAtMs: number(),
28203
- schemaVersion: literal(1)
28204
- });
28205
- object({
28206
- jobId: string(),
28207
- taskId: string(),
28208
- backups: array(object({
28209
- name: string(),
28210
- backupPath: string(),
28211
- livePath: string()
28212
- })),
28213
- appliedAtMs: number(),
28214
- bootAttempts: number(),
28215
- schemaVersion: literal(1)
28216
- });
28217
28307
  //#endregion
28218
28308
  //#region src/config.ts
28219
28309
  /**
@@ -31889,7 +31979,7 @@ var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
31889
31979
  require_websocket_server();
31890
31980
  var wrapper_default = import_websocket.default;
31891
31981
  //#endregion
31892
- //#region node_modules/@apocaliss92/nodedreo/dist/index.js
31982
+ //#region ../../node_modules/@apocaliss92/nodedreo/dist/index.js
31893
31983
  var DreoError = class extends Error {
31894
31984
  constructor(message) {
31895
31985
  super(message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreo",
3
- "version": "0.1.23",
3
+ "version": "0.2.0",
4
4
  "description": "Dreo smart-device (fan / air-circulator / purifier / heater / humidifier) device-provider addon for CamStack — wraps the @apocaliss92/nodedreo Dreo cloud client (REST + WebSocket)",
5
5
  "keywords": [
6
6
  "camstack",