@camstack/addon-provider-dreo 0.1.23 → 0.2.1

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 +214 -107
  2. package/dist/addon.mjs +214 -107
  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). */
@@ -12647,7 +12685,21 @@ var NativeCropResultSchema = object({
12647
12685
  /** Packed rgb (24-bit) pixels of the crop. */
12648
12686
  bytes: _instanceof(Uint8Array),
12649
12687
  width: number().int().positive(),
12650
- height: number().int().positive()
12688
+ height: number().int().positive(),
12689
+ /**
12690
+ * Which source served this crop, so a quality-sensitive consumer (the native
12691
+ * `keyFrame`) can reject a degraded fallback:
12692
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12693
+ * quality path).
12694
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12695
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12696
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12697
+ *
12698
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12699
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12700
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12701
+ */
12702
+ tier: _enum(["native", "ram-fullframe"]).optional()
12651
12703
  });
12652
12704
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12653
12705
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12887,7 +12939,14 @@ var RunnerCameraConfigSchema = object({
12887
12939
  * camera's detect node differs from its source-owner (P2d, gated by the
12888
12940
  * `remoteSourcingNodes` rollout setting).
12889
12941
  */
12890
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12942
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12943
+ /**
12944
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12945
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12946
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12947
+ * this only selects WHICH device pool of that node runs the session.
12948
+ */
12949
+ deviceKey: string().optional()
12891
12950
  });
12892
12951
  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
12952
  /**
@@ -12908,6 +12967,19 @@ var RunnerLocalLoadSchema = object({
12908
12967
  avgInferenceTimeMs: number(),
12909
12968
  /** Total queue depth across motion + detection queues. */
12910
12969
  queueDepthTotal: number(),
12970
+ /**
12971
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12972
+ * this runner currently has attached cameras on, so the orchestrator's second
12973
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12974
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12975
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12976
+ */
12977
+ devices: array(object({
12978
+ deviceKey: string(),
12979
+ backend: string(),
12980
+ attachedCameras: number(),
12981
+ queueDepthTotal: number()
12982
+ })).default([]),
12911
12983
  /** Hardware capability flags reported by this node. */
12912
12984
  hardware: object({
12913
12985
  hasGpu: boolean(),
@@ -16056,6 +16128,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16056
16128
  return toDeviceSummary(device, this.addonId);
16057
16129
  }
16058
16130
  };
16131
+ 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;
16132
+ new Set(Object.values(DeviceType));
16059
16133
  /**
16060
16134
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16061
16135
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17646,7 +17720,8 @@ var LinkedDeviceSchema = object({
17646
17720
  deviceId: number(),
17647
17721
  name: string(),
17648
17722
  location: string().nullable(),
17649
- features: array(string())
17723
+ features: array(string()),
17724
+ producesTrackedEvents: boolean().optional()
17650
17725
  });
17651
17726
  var SavedDeviceRowSchema = object({
17652
17727
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19276,6 +19351,7 @@ var TrackSchema = object({
19276
19351
  deviceId: number(),
19277
19352
  className: string(),
19278
19353
  label: string().optional(),
19354
+ producingDeviceName: string().optional(),
19279
19355
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19280
19356
  source: TrackSourceSchema.optional(),
19281
19357
  firstSeen: number(),
@@ -19413,7 +19489,8 @@ var MediaFileKindEnum = _enum([
19413
19489
  "fullFrameBoxed",
19414
19490
  "faceCrop",
19415
19491
  "plateCrop",
19416
- "keyFrame"
19492
+ "keyFrame",
19493
+ "keyFrameSmall"
19417
19494
  ]);
19418
19495
  var MediaFileSchema = object({
19419
19496
  key: string(),
@@ -19742,13 +19819,11 @@ var PipelineTemplateSchema = object({
19742
19819
  createdAt: string(),
19743
19820
  updatedAt: string()
19744
19821
  });
19745
- var AgentAddonConfigSchema = object({
19746
- enabled: boolean(),
19822
+ var DeviceStepConfigSchema = object({
19747
19823
  modelId: string().optional(),
19748
- settings: record(string(), unknown()).readonly()
19824
+ settings: record(string(), unknown()).optional()
19749
19825
  });
19750
19826
  var AgentPipelineSettingsSchema = object({
19751
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19752
19827
  maxCameras: number().int().nonnegative().nullable().default(null),
19753
19828
  /** Per-node detection weight (relative share for the quota balancer). */
19754
19829
  detectWeight: number().positive().optional(),
@@ -19772,7 +19847,22 @@ var AgentPipelineSettingsSchema = object({
19772
19847
  * it already uses to reach the hub). Set this only when the auto-detected
19773
19848
  * address is wrong (multi-homed host, NAT, custom interface).
19774
19849
  */
19775
- reachableHost: string().optional()
19850
+ reachableHost: string().optional(),
19851
+ /**
19852
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19853
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19854
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19855
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19856
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19857
+ * the default model/settings for every camera landing on that accelerator;
19858
+ * a stepId absent ⇒ the step uses that device's format default.
19859
+ */
19860
+ inferenceDevices: record(string(), object({
19861
+ enabled: boolean(),
19862
+ weight: number().positive().optional(),
19863
+ maxSessions: number().int().positive().optional(),
19864
+ steps: record(string(), DeviceStepConfigSchema).optional()
19865
+ })).optional()
19776
19866
  });
19777
19867
  var CameraPipelineForAgentSchema = object({
19778
19868
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19782,14 +19872,13 @@ var CameraPipelineForAgentSchema = object({
19782
19872
  }).nullable()
19783
19873
  });
19784
19874
  var CameraStepOverridePatchSchema = object({
19785
- enabled: boolean().optional(),
19786
19875
  modelId: string().optional(),
19787
19876
  settings: record(string(), unknown()).readonly().optional()
19788
19877
  });
19789
19878
  var CameraPipelineSettingsSchema = object({
19790
19879
  pinnedAgentNodeId: string().optional(),
19791
19880
  stepToggles: record(string(), boolean()).optional(),
19792
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19881
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19793
19882
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19794
19883
  });
19795
19884
  /**
@@ -20003,6 +20092,44 @@ var CameraStatusSchema = object({
20003
20092
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20004
20093
  fetchedAt: number()
20005
20094
  });
20095
+ var NodeInferenceDeviceSchema = object({
20096
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20097
+ key: string(),
20098
+ backend: string(),
20099
+ device: string(),
20100
+ format: _enum(MODEL_FORMATS),
20101
+ /** Whether the node's live probe reports the device as usable right now. */
20102
+ available: boolean(),
20103
+ /**
20104
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20105
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20106
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20107
+ * not a balanced target). An explicit stored value always wins; a stored-only
20108
+ * (unavailable) key keeps its stored value.
20109
+ */
20110
+ enabled: boolean(),
20111
+ /** Relative balancer weight for the enabled device (default 1). */
20112
+ weight: number(),
20113
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20114
+ maxSessions: number().nullable(),
20115
+ /** Object-detection model the executor defaults to for this deviceKey. */
20116
+ defaultModelId: string(),
20117
+ /**
20118
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20119
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20120
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20121
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20122
+ * available per format; this is the stored selection that becomes the
20123
+ * default for EVERY camera landing on this accelerator.
20124
+ */
20125
+ steps: record(string(), DeviceStepConfigSchema).optional()
20126
+ });
20127
+ var NodeInferenceDevicesSchema = object({
20128
+ nodeId: string(),
20129
+ /** False when the node's platform-probe was unreachable (no live device set). */
20130
+ reachable: boolean(),
20131
+ devices: array(NodeInferenceDeviceSchema).readonly()
20132
+ });
20006
20133
  method(object({
20007
20134
  deviceId: number(),
20008
20135
  agentNodeId: string()
@@ -20012,7 +20139,13 @@ method(object({
20012
20139
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20013
20140
  kind: "mutation",
20014
20141
  auth: "admin"
20015
- }), method(_void(), object({ migrated: number() }), {
20142
+ }), method(object({
20143
+ deviceId: number(),
20144
+ deviceKey: string()
20145
+ }), object({ success: literal(true) }), {
20146
+ kind: "mutation",
20147
+ auth: "admin"
20148
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20016
20149
  kind: "mutation",
20017
20150
  auth: "admin"
20018
20151
  }), 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 +20179,7 @@ method(object({
20046
20179
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20047
20180
  nodeId: string(),
20048
20181
  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({
20182
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20056
20183
  success: boolean(),
20057
20184
  removed: boolean()
20058
20185
  }), {
@@ -20084,7 +20211,18 @@ method(object({
20084
20211
  }), object({ success: literal(true) }), {
20085
20212
  kind: "mutation",
20086
20213
  auth: "admin"
20087
- }), method(object({ agentNodeId: string() }), object({
20214
+ }), method(object({
20215
+ agentNodeId: string(),
20216
+ inferenceDevices: record(string(), object({
20217
+ enabled: boolean(),
20218
+ weight: number().positive().optional(),
20219
+ maxSessions: number().int().positive().optional(),
20220
+ steps: record(string(), DeviceStepConfigSchema).optional()
20221
+ }))
20222
+ }), object({ success: literal(true) }), {
20223
+ kind: "mutation",
20224
+ auth: "admin"
20225
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20088
20226
  success: literal(true),
20089
20227
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20090
20228
  effectiveModelId: string().nullable(),
@@ -20100,9 +20238,10 @@ method(object({
20100
20238
  }), object({ success: literal(true) }), {
20101
20239
  kind: "mutation",
20102
20240
  auth: "admin"
20103
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20241
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20104
20242
  deviceId: number(),
20105
20243
  agentNodeId: string(),
20244
+ deviceKey: string(),
20106
20245
  addonId: string(),
20107
20246
  patch: CameraStepOverridePatchSchema.nullable()
20108
20247
  }), object({ success: literal(true) }), {
@@ -20139,14 +20278,13 @@ method(object({
20139
20278
  });
20140
20279
  /**
20141
20280
  * server-management — per-NODE singleton capability for a node's ROOT
20142
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20143
- * agents).
20281
+ * package lifecycle (runtime-updatable node packages).
20144
20282
  *
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).
20283
+ * Every node role runs the SAME root package (`@camstack/server`), which
20284
+ * carries the whole software stack in its npm dep tree, so ONE version
20285
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20286
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20287
+ * no auto-rollback).
20150
20288
  *
20151
20289
  * Providers:
20152
20290
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20254,7 +20392,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20254
20392
  /** Explicit target version; omitted = latest from the registry. */
20255
20393
  version: string().optional() }), ServerUpdateActionResultSchema, {
20256
20394
  kind: "mutation",
20257
- auth: "admin"
20395
+ auth: "admin",
20396
+ timeoutMs: 16 * 6e4
20258
20397
  }), method(_void(), ServerUpdateActionResultSchema, {
20259
20398
  kind: "mutation",
20260
20399
  auth: "admin"
@@ -21298,22 +21437,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21298
21437
  var RestartAddonResultSchema = unknown();
21299
21438
  var InstallPackageResultSchema = unknown();
21300
21439
  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
21440
  var BulkUpdateItemStatusSchema = _enum([
21318
21441
  "queued",
21319
21442
  "updating",
@@ -21441,13 +21564,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21441
21564
  }), object({ success: literal(true) }), {
21442
21565
  kind: "mutation",
21443
21566
  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
21567
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21452
21568
  kind: "mutation",
21453
21569
  auth: "admin"
@@ -22313,10 +22429,10 @@ var TopologyCategorySchema = object({
22313
22429
  addons: array(TopologyCategoryAddonSchema).readonly()
22314
22430
  });
22315
22431
  /**
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.
22432
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22433
+ * root package for every node role) as reported by its `registerNode`
22434
+ * manifest — version visibility for the Server management surface. Nullable:
22435
+ * offline rows and nodes that never reported one.
22320
22436
  */
22321
22437
  var TopologyRootPackageSchema = object({
22322
22438
  name: string(),
@@ -22704,17 +22820,28 @@ var PlatformScoreSchema = object({
22704
22820
  format: _enum([
22705
22821
  "onnx",
22706
22822
  "coreml",
22707
- "openvino"
22823
+ "openvino",
22824
+ "tflite"
22708
22825
  ]),
22709
22826
  score: number(),
22710
22827
  reason: string(),
22711
22828
  available: boolean()
22712
22829
  });
22830
+ var InferenceDeviceDescriptorSchema = object({
22831
+ key: string(),
22832
+ backend: string(),
22833
+ device: string(),
22834
+ format: ModelFormatSchema,
22835
+ runtime: literal("python"),
22836
+ score: number(),
22837
+ available: boolean()
22838
+ });
22713
22839
  var PlatformCapabilitiesSchema = object({
22714
22840
  hardware: HardwareInfoSchema,
22715
22841
  scores: array(PlatformScoreSchema).readonly(),
22716
22842
  bestScore: PlatformScoreSchema,
22717
- pythonPath: string().nullable()
22843
+ pythonPath: string().nullable(),
22844
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22718
22845
  });
22719
22846
  var ModelRequirementSchema = object({
22720
22847
  modelId: string(),
@@ -23593,12 +23720,6 @@ Object.freeze({
23593
23720
  addonId: null,
23594
23721
  access: "delete"
23595
23722
  },
23596
- "addons.updateFrameworkPackage": {
23597
- capName: "addons",
23598
- capScope: "system",
23599
- addonId: null,
23600
- access: "create"
23601
- },
23602
23723
  "addons.updatePackage": {
23603
23724
  capName: "addons",
23604
23725
  capScope: "system",
@@ -26371,12 +26492,6 @@ Object.freeze({
26371
26492
  addonId: null,
26372
26493
  access: "view"
26373
26494
  },
26374
- "pipelineExecutor.reprobeEngine": {
26375
- capName: "pipeline-executor",
26376
- capScope: "system",
26377
- addonId: null,
26378
- access: "create"
26379
- },
26380
26495
  "pipelineExecutor.runAudioTest": {
26381
26496
  capName: "pipeline-executor",
26382
26497
  capScope: "system",
@@ -26527,6 +26642,12 @@ Object.freeze({
26527
26642
  addonId: null,
26528
26643
  access: "view"
26529
26644
  },
26645
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26646
+ capName: "pipeline-orchestrator",
26647
+ capScope: "system",
26648
+ addonId: null,
26649
+ access: "view"
26650
+ },
26530
26651
  "pipelineOrchestrator.getPipelineAssignment": {
26531
26652
  capName: "pipeline-orchestrator",
26532
26653
  capScope: "system",
@@ -26539,6 +26660,12 @@ Object.freeze({
26539
26660
  addonId: null,
26540
26661
  access: "view"
26541
26662
  },
26663
+ "pipelineOrchestrator.getPipelineDevicePin": {
26664
+ capName: "pipeline-orchestrator",
26665
+ capScope: "system",
26666
+ addonId: null,
26667
+ access: "view"
26668
+ },
26542
26669
  "pipelineOrchestrator.listAgentSettings": {
26543
26670
  capName: "pipeline-orchestrator",
26544
26671
  capScope: "system",
@@ -26581,19 +26708,19 @@ Object.freeze({
26581
26708
  addonId: null,
26582
26709
  access: "create"
26583
26710
  },
26584
- "pipelineOrchestrator.setAgentAddonDefaults": {
26711
+ "pipelineOrchestrator.setAgentCapabilities": {
26585
26712
  capName: "pipeline-orchestrator",
26586
26713
  capScope: "system",
26587
26714
  addonId: null,
26588
26715
  access: "create"
26589
26716
  },
26590
- "pipelineOrchestrator.setAgentCapabilities": {
26717
+ "pipelineOrchestrator.setAgentDetectWeight": {
26591
26718
  capName: "pipeline-orchestrator",
26592
26719
  capScope: "system",
26593
26720
  addonId: null,
26594
26721
  access: "create"
26595
26722
  },
26596
- "pipelineOrchestrator.setAgentDetectWeight": {
26723
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26597
26724
  capName: "pipeline-orchestrator",
26598
26725
  capScope: "system",
26599
26726
  addonId: null,
@@ -26635,6 +26762,12 @@ Object.freeze({
26635
26762
  addonId: null,
26636
26763
  access: "create"
26637
26764
  },
26765
+ "pipelineOrchestrator.setPipelineDevicePin": {
26766
+ capName: "pipeline-orchestrator",
26767
+ capScope: "system",
26768
+ addonId: null,
26769
+ access: "create"
26770
+ },
26638
26771
  "pipelineOrchestrator.unassignAudio": {
26639
26772
  capName: "pipeline-orchestrator",
26640
26773
  capScope: "system",
@@ -28187,32 +28320,6 @@ Object.freeze({
28187
28320
  "network-access": "ingress",
28188
28321
  "smtp-provider": "email"
28189
28322
  });
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
28323
  //#endregion
28217
28324
  //#region src/config.ts
28218
28325
  /**
@@ -31888,7 +31995,7 @@ var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
31888
31995
  require_websocket_server();
31889
31996
  var wrapper_default = import_websocket.default;
31890
31997
  //#endregion
31891
- //#region node_modules/@apocaliss92/nodedreo/dist/index.js
31998
+ //#region ../../node_modules/@apocaliss92/nodedreo/dist/index.js
31892
31999
  var DreoError = class extends Error {
31893
32000
  constructor(message) {
31894
32001
  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). */
@@ -12648,7 +12686,21 @@ var NativeCropResultSchema = object({
12648
12686
  /** Packed rgb (24-bit) pixels of the crop. */
12649
12687
  bytes: _instanceof(Uint8Array),
12650
12688
  width: number().int().positive(),
12651
- height: number().int().positive()
12689
+ height: number().int().positive(),
12690
+ /**
12691
+ * Which source served this crop, so a quality-sensitive consumer (the native
12692
+ * `keyFrame`) can reject a degraded fallback:
12693
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12694
+ * quality path).
12695
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12696
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12697
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12698
+ *
12699
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12700
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12701
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12702
+ */
12703
+ tier: _enum(["native", "ram-fullframe"]).optional()
12652
12704
  });
12653
12705
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12654
12706
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12888,7 +12940,14 @@ var RunnerCameraConfigSchema = object({
12888
12940
  * camera's detect node differs from its source-owner (P2d, gated by the
12889
12941
  * `remoteSourcingNodes` rollout setting).
12890
12942
  */
12891
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12943
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12944
+ /**
12945
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12946
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12947
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12948
+ * this only selects WHICH device pool of that node runs the session.
12949
+ */
12950
+ deviceKey: string().optional()
12892
12951
  });
12893
12952
  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
12953
  /**
@@ -12909,6 +12968,19 @@ var RunnerLocalLoadSchema = object({
12909
12968
  avgInferenceTimeMs: number(),
12910
12969
  /** Total queue depth across motion + detection queues. */
12911
12970
  queueDepthTotal: number(),
12971
+ /**
12972
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12973
+ * this runner currently has attached cameras on, so the orchestrator's second
12974
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12975
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12976
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12977
+ */
12978
+ devices: array(object({
12979
+ deviceKey: string(),
12980
+ backend: string(),
12981
+ attachedCameras: number(),
12982
+ queueDepthTotal: number()
12983
+ })).default([]),
12912
12984
  /** Hardware capability flags reported by this node. */
12913
12985
  hardware: object({
12914
12986
  hasGpu: boolean(),
@@ -16057,6 +16129,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16057
16129
  return toDeviceSummary(device, this.addonId);
16058
16130
  }
16059
16131
  };
16132
+ 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;
16133
+ new Set(Object.values(DeviceType));
16060
16134
  /**
16061
16135
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16062
16136
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17647,7 +17721,8 @@ var LinkedDeviceSchema = object({
17647
17721
  deviceId: number(),
17648
17722
  name: string(),
17649
17723
  location: string().nullable(),
17650
- features: array(string())
17724
+ features: array(string()),
17725
+ producesTrackedEvents: boolean().optional()
17651
17726
  });
17652
17727
  var SavedDeviceRowSchema = object({
17653
17728
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19277,6 +19352,7 @@ var TrackSchema = object({
19277
19352
  deviceId: number(),
19278
19353
  className: string(),
19279
19354
  label: string().optional(),
19355
+ producingDeviceName: string().optional(),
19280
19356
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19281
19357
  source: TrackSourceSchema.optional(),
19282
19358
  firstSeen: number(),
@@ -19414,7 +19490,8 @@ var MediaFileKindEnum = _enum([
19414
19490
  "fullFrameBoxed",
19415
19491
  "faceCrop",
19416
19492
  "plateCrop",
19417
- "keyFrame"
19493
+ "keyFrame",
19494
+ "keyFrameSmall"
19418
19495
  ]);
19419
19496
  var MediaFileSchema = object({
19420
19497
  key: string(),
@@ -19743,13 +19820,11 @@ var PipelineTemplateSchema = object({
19743
19820
  createdAt: string(),
19744
19821
  updatedAt: string()
19745
19822
  });
19746
- var AgentAddonConfigSchema = object({
19747
- enabled: boolean(),
19823
+ var DeviceStepConfigSchema = object({
19748
19824
  modelId: string().optional(),
19749
- settings: record(string(), unknown()).readonly()
19825
+ settings: record(string(), unknown()).optional()
19750
19826
  });
19751
19827
  var AgentPipelineSettingsSchema = object({
19752
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19753
19828
  maxCameras: number().int().nonnegative().nullable().default(null),
19754
19829
  /** Per-node detection weight (relative share for the quota balancer). */
19755
19830
  detectWeight: number().positive().optional(),
@@ -19773,7 +19848,22 @@ var AgentPipelineSettingsSchema = object({
19773
19848
  * it already uses to reach the hub). Set this only when the auto-detected
19774
19849
  * address is wrong (multi-homed host, NAT, custom interface).
19775
19850
  */
19776
- reachableHost: string().optional()
19851
+ reachableHost: string().optional(),
19852
+ /**
19853
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19854
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19855
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19856
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19857
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19858
+ * the default model/settings for every camera landing on that accelerator;
19859
+ * a stepId absent ⇒ the step uses that device's format default.
19860
+ */
19861
+ inferenceDevices: record(string(), object({
19862
+ enabled: boolean(),
19863
+ weight: number().positive().optional(),
19864
+ maxSessions: number().int().positive().optional(),
19865
+ steps: record(string(), DeviceStepConfigSchema).optional()
19866
+ })).optional()
19777
19867
  });
19778
19868
  var CameraPipelineForAgentSchema = object({
19779
19869
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19783,14 +19873,13 @@ var CameraPipelineForAgentSchema = object({
19783
19873
  }).nullable()
19784
19874
  });
19785
19875
  var CameraStepOverridePatchSchema = object({
19786
- enabled: boolean().optional(),
19787
19876
  modelId: string().optional(),
19788
19877
  settings: record(string(), unknown()).readonly().optional()
19789
19878
  });
19790
19879
  var CameraPipelineSettingsSchema = object({
19791
19880
  pinnedAgentNodeId: string().optional(),
19792
19881
  stepToggles: record(string(), boolean()).optional(),
19793
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19882
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19794
19883
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19795
19884
  });
19796
19885
  /**
@@ -20004,6 +20093,44 @@ var CameraStatusSchema = object({
20004
20093
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20005
20094
  fetchedAt: number()
20006
20095
  });
20096
+ var NodeInferenceDeviceSchema = object({
20097
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20098
+ key: string(),
20099
+ backend: string(),
20100
+ device: string(),
20101
+ format: _enum(MODEL_FORMATS),
20102
+ /** Whether the node's live probe reports the device as usable right now. */
20103
+ available: boolean(),
20104
+ /**
20105
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20106
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20107
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20108
+ * not a balanced target). An explicit stored value always wins; a stored-only
20109
+ * (unavailable) key keeps its stored value.
20110
+ */
20111
+ enabled: boolean(),
20112
+ /** Relative balancer weight for the enabled device (default 1). */
20113
+ weight: number(),
20114
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20115
+ maxSessions: number().nullable(),
20116
+ /** Object-detection model the executor defaults to for this deviceKey. */
20117
+ defaultModelId: string(),
20118
+ /**
20119
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20120
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20121
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20122
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20123
+ * available per format; this is the stored selection that becomes the
20124
+ * default for EVERY camera landing on this accelerator.
20125
+ */
20126
+ steps: record(string(), DeviceStepConfigSchema).optional()
20127
+ });
20128
+ var NodeInferenceDevicesSchema = object({
20129
+ nodeId: string(),
20130
+ /** False when the node's platform-probe was unreachable (no live device set). */
20131
+ reachable: boolean(),
20132
+ devices: array(NodeInferenceDeviceSchema).readonly()
20133
+ });
20007
20134
  method(object({
20008
20135
  deviceId: number(),
20009
20136
  agentNodeId: string()
@@ -20013,7 +20140,13 @@ method(object({
20013
20140
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20014
20141
  kind: "mutation",
20015
20142
  auth: "admin"
20016
- }), method(_void(), object({ migrated: number() }), {
20143
+ }), method(object({
20144
+ deviceId: number(),
20145
+ deviceKey: string()
20146
+ }), object({ success: literal(true) }), {
20147
+ kind: "mutation",
20148
+ auth: "admin"
20149
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20017
20150
  kind: "mutation",
20018
20151
  auth: "admin"
20019
20152
  }), 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 +20180,7 @@ method(object({
20047
20180
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20048
20181
  nodeId: string(),
20049
20182
  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({
20183
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20057
20184
  success: boolean(),
20058
20185
  removed: boolean()
20059
20186
  }), {
@@ -20085,7 +20212,18 @@ method(object({
20085
20212
  }), object({ success: literal(true) }), {
20086
20213
  kind: "mutation",
20087
20214
  auth: "admin"
20088
- }), method(object({ agentNodeId: string() }), object({
20215
+ }), method(object({
20216
+ agentNodeId: string(),
20217
+ inferenceDevices: record(string(), object({
20218
+ enabled: boolean(),
20219
+ weight: number().positive().optional(),
20220
+ maxSessions: number().int().positive().optional(),
20221
+ steps: record(string(), DeviceStepConfigSchema).optional()
20222
+ }))
20223
+ }), object({ success: literal(true) }), {
20224
+ kind: "mutation",
20225
+ auth: "admin"
20226
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20089
20227
  success: literal(true),
20090
20228
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20091
20229
  effectiveModelId: string().nullable(),
@@ -20101,9 +20239,10 @@ method(object({
20101
20239
  }), object({ success: literal(true) }), {
20102
20240
  kind: "mutation",
20103
20241
  auth: "admin"
20104
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20242
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20105
20243
  deviceId: number(),
20106
20244
  agentNodeId: string(),
20245
+ deviceKey: string(),
20107
20246
  addonId: string(),
20108
20247
  patch: CameraStepOverridePatchSchema.nullable()
20109
20248
  }), object({ success: literal(true) }), {
@@ -20140,14 +20279,13 @@ method(object({
20140
20279
  });
20141
20280
  /**
20142
20281
  * server-management — per-NODE singleton capability for a node's ROOT
20143
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20144
- * agents).
20282
+ * package lifecycle (runtime-updatable node packages).
20145
20283
  *
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).
20284
+ * Every node role runs the SAME root package (`@camstack/server`), which
20285
+ * carries the whole software stack in its npm dep tree, so ONE version
20286
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20287
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20288
+ * no auto-rollback).
20151
20289
  *
20152
20290
  * Providers:
20153
20291
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20255,7 +20393,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20255
20393
  /** Explicit target version; omitted = latest from the registry. */
20256
20394
  version: string().optional() }), ServerUpdateActionResultSchema, {
20257
20395
  kind: "mutation",
20258
- auth: "admin"
20396
+ auth: "admin",
20397
+ timeoutMs: 16 * 6e4
20259
20398
  }), method(_void(), ServerUpdateActionResultSchema, {
20260
20399
  kind: "mutation",
20261
20400
  auth: "admin"
@@ -21299,22 +21438,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21299
21438
  var RestartAddonResultSchema = unknown();
21300
21439
  var InstallPackageResultSchema = unknown();
21301
21440
  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
21441
  var BulkUpdateItemStatusSchema = _enum([
21319
21442
  "queued",
21320
21443
  "updating",
@@ -21442,13 +21565,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21442
21565
  }), object({ success: literal(true) }), {
21443
21566
  kind: "mutation",
21444
21567
  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
21568
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21453
21569
  kind: "mutation",
21454
21570
  auth: "admin"
@@ -22314,10 +22430,10 @@ var TopologyCategorySchema = object({
22314
22430
  addons: array(TopologyCategoryAddonSchema).readonly()
22315
22431
  });
22316
22432
  /**
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.
22433
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22434
+ * root package for every node role) as reported by its `registerNode`
22435
+ * manifest — version visibility for the Server management surface. Nullable:
22436
+ * offline rows and nodes that never reported one.
22321
22437
  */
22322
22438
  var TopologyRootPackageSchema = object({
22323
22439
  name: string(),
@@ -22705,17 +22821,28 @@ var PlatformScoreSchema = object({
22705
22821
  format: _enum([
22706
22822
  "onnx",
22707
22823
  "coreml",
22708
- "openvino"
22824
+ "openvino",
22825
+ "tflite"
22709
22826
  ]),
22710
22827
  score: number(),
22711
22828
  reason: string(),
22712
22829
  available: boolean()
22713
22830
  });
22831
+ var InferenceDeviceDescriptorSchema = object({
22832
+ key: string(),
22833
+ backend: string(),
22834
+ device: string(),
22835
+ format: ModelFormatSchema,
22836
+ runtime: literal("python"),
22837
+ score: number(),
22838
+ available: boolean()
22839
+ });
22714
22840
  var PlatformCapabilitiesSchema = object({
22715
22841
  hardware: HardwareInfoSchema,
22716
22842
  scores: array(PlatformScoreSchema).readonly(),
22717
22843
  bestScore: PlatformScoreSchema,
22718
- pythonPath: string().nullable()
22844
+ pythonPath: string().nullable(),
22845
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22719
22846
  });
22720
22847
  var ModelRequirementSchema = object({
22721
22848
  modelId: string(),
@@ -23594,12 +23721,6 @@ Object.freeze({
23594
23721
  addonId: null,
23595
23722
  access: "delete"
23596
23723
  },
23597
- "addons.updateFrameworkPackage": {
23598
- capName: "addons",
23599
- capScope: "system",
23600
- addonId: null,
23601
- access: "create"
23602
- },
23603
23724
  "addons.updatePackage": {
23604
23725
  capName: "addons",
23605
23726
  capScope: "system",
@@ -26372,12 +26493,6 @@ Object.freeze({
26372
26493
  addonId: null,
26373
26494
  access: "view"
26374
26495
  },
26375
- "pipelineExecutor.reprobeEngine": {
26376
- capName: "pipeline-executor",
26377
- capScope: "system",
26378
- addonId: null,
26379
- access: "create"
26380
- },
26381
26496
  "pipelineExecutor.runAudioTest": {
26382
26497
  capName: "pipeline-executor",
26383
26498
  capScope: "system",
@@ -26528,6 +26643,12 @@ Object.freeze({
26528
26643
  addonId: null,
26529
26644
  access: "view"
26530
26645
  },
26646
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26647
+ capName: "pipeline-orchestrator",
26648
+ capScope: "system",
26649
+ addonId: null,
26650
+ access: "view"
26651
+ },
26531
26652
  "pipelineOrchestrator.getPipelineAssignment": {
26532
26653
  capName: "pipeline-orchestrator",
26533
26654
  capScope: "system",
@@ -26540,6 +26661,12 @@ Object.freeze({
26540
26661
  addonId: null,
26541
26662
  access: "view"
26542
26663
  },
26664
+ "pipelineOrchestrator.getPipelineDevicePin": {
26665
+ capName: "pipeline-orchestrator",
26666
+ capScope: "system",
26667
+ addonId: null,
26668
+ access: "view"
26669
+ },
26543
26670
  "pipelineOrchestrator.listAgentSettings": {
26544
26671
  capName: "pipeline-orchestrator",
26545
26672
  capScope: "system",
@@ -26582,19 +26709,19 @@ Object.freeze({
26582
26709
  addonId: null,
26583
26710
  access: "create"
26584
26711
  },
26585
- "pipelineOrchestrator.setAgentAddonDefaults": {
26712
+ "pipelineOrchestrator.setAgentCapabilities": {
26586
26713
  capName: "pipeline-orchestrator",
26587
26714
  capScope: "system",
26588
26715
  addonId: null,
26589
26716
  access: "create"
26590
26717
  },
26591
- "pipelineOrchestrator.setAgentCapabilities": {
26718
+ "pipelineOrchestrator.setAgentDetectWeight": {
26592
26719
  capName: "pipeline-orchestrator",
26593
26720
  capScope: "system",
26594
26721
  addonId: null,
26595
26722
  access: "create"
26596
26723
  },
26597
- "pipelineOrchestrator.setAgentDetectWeight": {
26724
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26598
26725
  capName: "pipeline-orchestrator",
26599
26726
  capScope: "system",
26600
26727
  addonId: null,
@@ -26636,6 +26763,12 @@ Object.freeze({
26636
26763
  addonId: null,
26637
26764
  access: "create"
26638
26765
  },
26766
+ "pipelineOrchestrator.setPipelineDevicePin": {
26767
+ capName: "pipeline-orchestrator",
26768
+ capScope: "system",
26769
+ addonId: null,
26770
+ access: "create"
26771
+ },
26639
26772
  "pipelineOrchestrator.unassignAudio": {
26640
26773
  capName: "pipeline-orchestrator",
26641
26774
  capScope: "system",
@@ -28188,32 +28321,6 @@ Object.freeze({
28188
28321
  "network-access": "ingress",
28189
28322
  "smtp-provider": "email"
28190
28323
  });
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
28324
  //#endregion
28218
28325
  //#region src/config.ts
28219
28326
  /**
@@ -31889,7 +31996,7 @@ var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
31889
31996
  require_websocket_server();
31890
31997
  var wrapper_default = import_websocket.default;
31891
31998
  //#endregion
31892
- //#region node_modules/@apocaliss92/nodedreo/dist/index.js
31999
+ //#region ../../node_modules/@apocaliss92/nodedreo/dist/index.js
31893
32000
  var DreoError = class extends Error {
31894
32001
  constructor(message) {
31895
32002
  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.1",
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",