@camstack/addon-provider-homematic 1.1.27 → 1.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 +213 -106
  2. package/dist/addon.mjs +213 -106
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7107,6 +7107,17 @@ var ModelCatalogEntrySchema = object({
7107
7107
  "imagenet",
7108
7108
  "none"
7109
7109
  ]).optional(),
7110
+ /**
7111
+ * The model already applies softmax IN-GRAPH — its raw output is a
7112
+ * probability distribution, not logits. When set, the `softmax`
7113
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7114
+ * probability vector collapses it toward uniform (top-1 score craters far
7115
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7116
+ * the output is raw logits and the postprocessor applies softmax (the normal
7117
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7118
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7119
+ */
7120
+ outputProbabilities: boolean().optional(),
7110
7121
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7111
7122
  /**
7112
7123
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12397,10 +12408,7 @@ var ConfigUISchemaNullableBridge = custom();
12397
12408
  var InferenceCapabilitiesBridge = custom();
12398
12409
  var ModelAvailabilityListBridge = custom();
12399
12410
  var PipelineRunResultBridge = custom();
12400
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12401
- kind: "mutation",
12402
- auth: "admin"
12403
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12411
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12404
12412
  modelId: string(),
12405
12413
  settings: record(string(), unknown()).readonly()
12406
12414
  }))), method(object({ steps: record(string(), object({
@@ -12460,13 +12468,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12460
12468
  * (inputClasses ≠ null) are skipped and served per-track via
12461
12469
  * pipelineRunner.runDetailSubtree (two-plane design).
12462
12470
  */
12463
- plane: _enum(["full", "frame"]).optional()
12471
+ plane: _enum(["full", "frame"]).optional(),
12472
+ /**
12473
+ * Inference-device selector (Phase 2 multi-device). Format
12474
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12475
+ * Omitted ⇒ the runner's default device (current single-engine
12476
+ * behaviour). Selects WHICH device pool of the node runs the call.
12477
+ */
12478
+ deviceKey: string().optional()
12464
12479
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12465
12480
  engine: PipelineEngineChoiceSchema.optional(),
12466
12481
  steps: array(PipelineStepInputSchema).min(1),
12467
12482
  frames: array(FrameInputSchema).min(1).max(255),
12468
12483
  deviceId: number().optional(),
12469
- sessionId: string().optional()
12484
+ sessionId: string().optional(),
12485
+ /**
12486
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12487
+ * the batch to the Python pool's bench preprocess cache
12488
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12489
+ * preprocessed ONCE and every later inference is a pure-inference cache
12490
+ * hit — the sustained-throughput run measures inference, not
12491
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12492
+ * full preprocess every call, correct). Fresh per sustained run;
12493
+ * released via `uncacheFrame`.
12494
+ */
12495
+ frameId: number().int().nonnegative().optional(),
12496
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12497
+ deviceKey: string().optional()
12470
12498
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12471
12499
  data: _instanceof(Uint8Array),
12472
12500
  width: number().int().positive(),
@@ -12498,8 +12526,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12498
12526
  * - `runtime` — main camera-serving engine (no idle TTL).
12499
12527
  * - `warm-override` — benchmark/test override held in the warm
12500
12528
  * cache; auto-disposed after the idle TTL.
12529
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12530
+ * multi-device, keyed by `deviceKey`) resolved
12531
+ * via `resolveDeviceFactory`. Runs alongside the
12532
+ * `runtime` engine on a DIFFERENT accelerator
12533
+ * (NPU / iGPU / Coral) — this is how the
12534
+ * Engines tab shows all pools running at once.
12501
12535
  */
12502
- kind: _enum(["runtime", "warm-override"]),
12536
+ kind: _enum([
12537
+ "runtime",
12538
+ "warm-override",
12539
+ "device-pool"
12540
+ ]),
12503
12541
  /** Native pid of the underlying Python pool (null when no pool). */
12504
12542
  poolPid: number().nullable(),
12505
12543
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12634,7 +12672,21 @@ var NativeCropResultSchema = object({
12634
12672
  /** Packed rgb (24-bit) pixels of the crop. */
12635
12673
  bytes: _instanceof(Uint8Array),
12636
12674
  width: number().int().positive(),
12637
- height: number().int().positive()
12675
+ height: number().int().positive(),
12676
+ /**
12677
+ * Which source served this crop, so a quality-sensitive consumer (the native
12678
+ * `keyFrame`) can reject a degraded fallback:
12679
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12680
+ * quality path).
12681
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12682
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12683
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12684
+ *
12685
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12686
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12687
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12688
+ */
12689
+ tier: _enum(["native", "ram-fullframe"]).optional()
12638
12690
  });
12639
12691
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12640
12692
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12874,7 +12926,14 @@ var RunnerCameraConfigSchema = object({
12874
12926
  * camera's detect node differs from its source-owner (P2d, gated by the
12875
12927
  * `remoteSourcingNodes` rollout setting).
12876
12928
  */
12877
- 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()
12878
12937
  });
12879
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;
12880
12939
  /**
@@ -12895,6 +12954,19 @@ var RunnerLocalLoadSchema = object({
12895
12954
  avgInferenceTimeMs: number(),
12896
12955
  /** Total queue depth across motion + detection queues. */
12897
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([]),
12898
12970
  /** Hardware capability flags reported by this node. */
12899
12971
  hardware: object({
12900
12972
  hasGpu: boolean(),
@@ -16043,6 +16115,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16043
16115
  return toDeviceSummary(device, this.addonId);
16044
16116
  }
16045
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));
16046
16120
  /**
16047
16121
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16048
16122
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17682,7 +17756,8 @@ var LinkedDeviceSchema = object({
17682
17756
  deviceId: number(),
17683
17757
  name: string(),
17684
17758
  location: string().nullable(),
17685
- features: array(string())
17759
+ features: array(string()),
17760
+ producesTrackedEvents: boolean().optional()
17686
17761
  });
17687
17762
  var SavedDeviceRowSchema = object({
17688
17763
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19312,6 +19387,7 @@ var TrackSchema = object({
19312
19387
  deviceId: number(),
19313
19388
  className: string(),
19314
19389
  label: string().optional(),
19390
+ producingDeviceName: string().optional(),
19315
19391
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19316
19392
  source: TrackSourceSchema.optional(),
19317
19393
  firstSeen: number(),
@@ -19449,7 +19525,8 @@ var MediaFileKindEnum = _enum([
19449
19525
  "fullFrameBoxed",
19450
19526
  "faceCrop",
19451
19527
  "plateCrop",
19452
- "keyFrame"
19528
+ "keyFrame",
19529
+ "keyFrameSmall"
19453
19530
  ]);
19454
19531
  var MediaFileSchema = object({
19455
19532
  key: string(),
@@ -19778,13 +19855,11 @@ var PipelineTemplateSchema = object({
19778
19855
  createdAt: string(),
19779
19856
  updatedAt: string()
19780
19857
  });
19781
- var AgentAddonConfigSchema = object({
19782
- enabled: boolean(),
19858
+ var DeviceStepConfigSchema = object({
19783
19859
  modelId: string().optional(),
19784
- settings: record(string(), unknown()).readonly()
19860
+ settings: record(string(), unknown()).optional()
19785
19861
  });
19786
19862
  var AgentPipelineSettingsSchema = object({
19787
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19788
19863
  maxCameras: number().int().nonnegative().nullable().default(null),
19789
19864
  /** Per-node detection weight (relative share for the quota balancer). */
19790
19865
  detectWeight: number().positive().optional(),
@@ -19808,7 +19883,22 @@ var AgentPipelineSettingsSchema = object({
19808
19883
  * it already uses to reach the hub). Set this only when the auto-detected
19809
19884
  * address is wrong (multi-homed host, NAT, custom interface).
19810
19885
  */
19811
- reachableHost: string().optional()
19886
+ reachableHost: string().optional(),
19887
+ /**
19888
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19889
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19890
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19891
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19892
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19893
+ * the default model/settings for every camera landing on that accelerator;
19894
+ * a stepId absent ⇒ the step uses that device's format default.
19895
+ */
19896
+ inferenceDevices: record(string(), object({
19897
+ enabled: boolean(),
19898
+ weight: number().positive().optional(),
19899
+ maxSessions: number().int().positive().optional(),
19900
+ steps: record(string(), DeviceStepConfigSchema).optional()
19901
+ })).optional()
19812
19902
  });
19813
19903
  var CameraPipelineForAgentSchema = object({
19814
19904
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19818,14 +19908,13 @@ var CameraPipelineForAgentSchema = object({
19818
19908
  }).nullable()
19819
19909
  });
19820
19910
  var CameraStepOverridePatchSchema = object({
19821
- enabled: boolean().optional(),
19822
19911
  modelId: string().optional(),
19823
19912
  settings: record(string(), unknown()).readonly().optional()
19824
19913
  });
19825
19914
  var CameraPipelineSettingsSchema = object({
19826
19915
  pinnedAgentNodeId: string().optional(),
19827
19916
  stepToggles: record(string(), boolean()).optional(),
19828
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19917
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19829
19918
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19830
19919
  });
19831
19920
  /**
@@ -20039,6 +20128,44 @@ var CameraStatusSchema = object({
20039
20128
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20040
20129
  fetchedAt: number()
20041
20130
  });
20131
+ var NodeInferenceDeviceSchema = object({
20132
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20133
+ key: string(),
20134
+ backend: string(),
20135
+ device: string(),
20136
+ format: _enum(MODEL_FORMATS),
20137
+ /** Whether the node's live probe reports the device as usable right now. */
20138
+ available: boolean(),
20139
+ /**
20140
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20141
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20142
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20143
+ * not a balanced target). An explicit stored value always wins; a stored-only
20144
+ * (unavailable) key keeps its stored value.
20145
+ */
20146
+ enabled: boolean(),
20147
+ /** Relative balancer weight for the enabled device (default 1). */
20148
+ weight: number(),
20149
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20150
+ maxSessions: number().nullable(),
20151
+ /** Object-detection model the executor defaults to for this deviceKey. */
20152
+ defaultModelId: string(),
20153
+ /**
20154
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20155
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20156
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20157
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20158
+ * available per format; this is the stored selection that becomes the
20159
+ * default for EVERY camera landing on this accelerator.
20160
+ */
20161
+ steps: record(string(), DeviceStepConfigSchema).optional()
20162
+ });
20163
+ var NodeInferenceDevicesSchema = object({
20164
+ nodeId: string(),
20165
+ /** False when the node's platform-probe was unreachable (no live device set). */
20166
+ reachable: boolean(),
20167
+ devices: array(NodeInferenceDeviceSchema).readonly()
20168
+ });
20042
20169
  method(object({
20043
20170
  deviceId: number(),
20044
20171
  agentNodeId: string()
@@ -20048,7 +20175,13 @@ method(object({
20048
20175
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20049
20176
  kind: "mutation",
20050
20177
  auth: "admin"
20051
- }), method(_void(), object({ migrated: number() }), {
20178
+ }), method(object({
20179
+ deviceId: number(),
20180
+ deviceKey: string()
20181
+ }), object({ success: literal(true) }), {
20182
+ kind: "mutation",
20183
+ auth: "admin"
20184
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20052
20185
  kind: "mutation",
20053
20186
  auth: "admin"
20054
20187
  }), 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({
@@ -20082,13 +20215,7 @@ method(object({
20082
20215
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20083
20216
  nodeId: string(),
20084
20217
  settings: AgentPipelineSettingsSchema
20085
- })).readonly()), method(object({
20086
- agentNodeId: string(),
20087
- defaults: record(string(), AgentAddonConfigSchema)
20088
- }), object({ success: literal(true) }), {
20089
- kind: "mutation",
20090
- auth: "admin"
20091
- }), method(object({ agentNodeId: string() }), object({
20218
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20092
20219
  success: boolean(),
20093
20220
  removed: boolean()
20094
20221
  }), {
@@ -20120,7 +20247,18 @@ method(object({
20120
20247
  }), object({ success: literal(true) }), {
20121
20248
  kind: "mutation",
20122
20249
  auth: "admin"
20123
- }), method(object({ agentNodeId: string() }), object({
20250
+ }), method(object({
20251
+ agentNodeId: string(),
20252
+ inferenceDevices: record(string(), object({
20253
+ enabled: boolean(),
20254
+ weight: number().positive().optional(),
20255
+ maxSessions: number().int().positive().optional(),
20256
+ steps: record(string(), DeviceStepConfigSchema).optional()
20257
+ }))
20258
+ }), object({ success: literal(true) }), {
20259
+ kind: "mutation",
20260
+ auth: "admin"
20261
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20124
20262
  success: literal(true),
20125
20263
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20126
20264
  effectiveModelId: string().nullable(),
@@ -20136,9 +20274,10 @@ method(object({
20136
20274
  }), object({ success: literal(true) }), {
20137
20275
  kind: "mutation",
20138
20276
  auth: "admin"
20139
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20277
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20140
20278
  deviceId: number(),
20141
20279
  agentNodeId: string(),
20280
+ deviceKey: string(),
20142
20281
  addonId: string(),
20143
20282
  patch: CameraStepOverridePatchSchema.nullable()
20144
20283
  }), object({ success: literal(true) }), {
@@ -20175,14 +20314,13 @@ method(object({
20175
20314
  });
20176
20315
  /**
20177
20316
  * server-management — per-NODE singleton capability for a node's ROOT
20178
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20179
- * agents).
20317
+ * package lifecycle (runtime-updatable node packages).
20180
20318
  *
20181
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20182
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20183
- * version describes the node. Updates install into
20184
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20185
- * starter (probation boot + auto-rollback to N-1).
20319
+ * Every node role runs the SAME root package (`@camstack/server`), which
20320
+ * carries the whole software stack in its npm dep tree, so ONE version
20321
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20322
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20323
+ * no auto-rollback).
20186
20324
  *
20187
20325
  * Providers:
20188
20326
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20290,7 +20428,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20290
20428
  /** Explicit target version; omitted = latest from the registry. */
20291
20429
  version: string().optional() }), ServerUpdateActionResultSchema, {
20292
20430
  kind: "mutation",
20293
- auth: "admin"
20431
+ auth: "admin",
20432
+ timeoutMs: 16 * 6e4
20294
20433
  }), method(_void(), ServerUpdateActionResultSchema, {
20295
20434
  kind: "mutation",
20296
20435
  auth: "admin"
@@ -21334,22 +21473,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21334
21473
  var RestartAddonResultSchema = unknown();
21335
21474
  var InstallPackageResultSchema = unknown();
21336
21475
  var ReloadPackagesResultSchema = unknown();
21337
- /**
21338
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21339
- * server restarts so the admin UI can react to the `restartingAt`
21340
- * timestamp (shows reconnect overlay). The transition from
21341
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21342
- * `system.restart-completed` event after the new process boots.
21343
- *
21344
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21345
- */
21346
- var UpdateFrameworkPackageResultSchema = object({
21347
- packageName: string(),
21348
- fromVersion: string(),
21349
- toVersion: string(),
21350
- /** Ms-epoch the server scheduled its self-restart. */
21351
- restartingAt: number()
21352
- });
21353
21476
  var BulkUpdateItemStatusSchema = _enum([
21354
21477
  "queued",
21355
21478
  "updating",
@@ -21477,13 +21600,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21477
21600
  }), object({ success: literal(true) }), {
21478
21601
  kind: "mutation",
21479
21602
  auth: "admin"
21480
- }), method(object({
21481
- packageName: string().min(1),
21482
- version: string().optional(),
21483
- deferRestart: boolean().optional()
21484
- }), UpdateFrameworkPackageResultSchema, {
21485
- kind: "mutation",
21486
- auth: "admin"
21487
21603
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21488
21604
  kind: "mutation",
21489
21605
  auth: "admin"
@@ -22349,10 +22465,10 @@ var TopologyCategorySchema = object({
22349
22465
  addons: array(TopologyCategoryAddonSchema).readonly()
22350
22466
  });
22351
22467
  /**
22352
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22353
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22354
- * version visibility for the Server management surface. Nullable: offline
22355
- * rows and pre-phase-2 nodes report none.
22468
+ * The node's runtime-updatable ROOT package (`@camstack/server` — the single
22469
+ * root package for every node role) as reported by its `registerNode`
22470
+ * manifest — version visibility for the Server management surface. Nullable:
22471
+ * offline rows and nodes that never reported one.
22356
22472
  */
22357
22473
  var TopologyRootPackageSchema = object({
22358
22474
  name: string(),
@@ -22740,17 +22856,28 @@ var PlatformScoreSchema = object({
22740
22856
  format: _enum([
22741
22857
  "onnx",
22742
22858
  "coreml",
22743
- "openvino"
22859
+ "openvino",
22860
+ "tflite"
22744
22861
  ]),
22745
22862
  score: number(),
22746
22863
  reason: string(),
22747
22864
  available: boolean()
22748
22865
  });
22866
+ var InferenceDeviceDescriptorSchema = object({
22867
+ key: string(),
22868
+ backend: string(),
22869
+ device: string(),
22870
+ format: ModelFormatSchema,
22871
+ runtime: literal("python"),
22872
+ score: number(),
22873
+ available: boolean()
22874
+ });
22749
22875
  var PlatformCapabilitiesSchema = object({
22750
22876
  hardware: HardwareInfoSchema,
22751
22877
  scores: array(PlatformScoreSchema).readonly(),
22752
22878
  bestScore: PlatformScoreSchema,
22753
- pythonPath: string().nullable()
22879
+ pythonPath: string().nullable(),
22880
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22754
22881
  });
22755
22882
  var ModelRequirementSchema = object({
22756
22883
  modelId: string(),
@@ -23629,12 +23756,6 @@ Object.freeze({
23629
23756
  addonId: null,
23630
23757
  access: "delete"
23631
23758
  },
23632
- "addons.updateFrameworkPackage": {
23633
- capName: "addons",
23634
- capScope: "system",
23635
- addonId: null,
23636
- access: "create"
23637
- },
23638
23759
  "addons.updatePackage": {
23639
23760
  capName: "addons",
23640
23761
  capScope: "system",
@@ -26407,12 +26528,6 @@ Object.freeze({
26407
26528
  addonId: null,
26408
26529
  access: "view"
26409
26530
  },
26410
- "pipelineExecutor.reprobeEngine": {
26411
- capName: "pipeline-executor",
26412
- capScope: "system",
26413
- addonId: null,
26414
- access: "create"
26415
- },
26416
26531
  "pipelineExecutor.runAudioTest": {
26417
26532
  capName: "pipeline-executor",
26418
26533
  capScope: "system",
@@ -26563,6 +26678,12 @@ Object.freeze({
26563
26678
  addonId: null,
26564
26679
  access: "view"
26565
26680
  },
26681
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26682
+ capName: "pipeline-orchestrator",
26683
+ capScope: "system",
26684
+ addonId: null,
26685
+ access: "view"
26686
+ },
26566
26687
  "pipelineOrchestrator.getPipelineAssignment": {
26567
26688
  capName: "pipeline-orchestrator",
26568
26689
  capScope: "system",
@@ -26575,6 +26696,12 @@ Object.freeze({
26575
26696
  addonId: null,
26576
26697
  access: "view"
26577
26698
  },
26699
+ "pipelineOrchestrator.getPipelineDevicePin": {
26700
+ capName: "pipeline-orchestrator",
26701
+ capScope: "system",
26702
+ addonId: null,
26703
+ access: "view"
26704
+ },
26578
26705
  "pipelineOrchestrator.listAgentSettings": {
26579
26706
  capName: "pipeline-orchestrator",
26580
26707
  capScope: "system",
@@ -26617,19 +26744,19 @@ Object.freeze({
26617
26744
  addonId: null,
26618
26745
  access: "create"
26619
26746
  },
26620
- "pipelineOrchestrator.setAgentAddonDefaults": {
26747
+ "pipelineOrchestrator.setAgentCapabilities": {
26621
26748
  capName: "pipeline-orchestrator",
26622
26749
  capScope: "system",
26623
26750
  addonId: null,
26624
26751
  access: "create"
26625
26752
  },
26626
- "pipelineOrchestrator.setAgentCapabilities": {
26753
+ "pipelineOrchestrator.setAgentDetectWeight": {
26627
26754
  capName: "pipeline-orchestrator",
26628
26755
  capScope: "system",
26629
26756
  addonId: null,
26630
26757
  access: "create"
26631
26758
  },
26632
- "pipelineOrchestrator.setAgentDetectWeight": {
26759
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26633
26760
  capName: "pipeline-orchestrator",
26634
26761
  capScope: "system",
26635
26762
  addonId: null,
@@ -26671,6 +26798,12 @@ Object.freeze({
26671
26798
  addonId: null,
26672
26799
  access: "create"
26673
26800
  },
26801
+ "pipelineOrchestrator.setPipelineDevicePin": {
26802
+ capName: "pipeline-orchestrator",
26803
+ capScope: "system",
26804
+ addonId: null,
26805
+ access: "create"
26806
+ },
26674
26807
  "pipelineOrchestrator.unassignAudio": {
26675
26808
  capName: "pipeline-orchestrator",
26676
26809
  capScope: "system",
@@ -28223,32 +28356,6 @@ Object.freeze({
28223
28356
  "network-access": "ingress",
28224
28357
  "smtp-provider": "email"
28225
28358
  });
28226
- var frameworkSwapPackageSchema = object({
28227
- name: string(),
28228
- stagedPath: string(),
28229
- backupPath: string(),
28230
- toVersion: string(),
28231
- fromVersion: string().nullable()
28232
- });
28233
- object({
28234
- jobId: string(),
28235
- taskId: string(),
28236
- packages: array(frameworkSwapPackageSchema),
28237
- requestedAtMs: number(),
28238
- schemaVersion: literal(1)
28239
- });
28240
- object({
28241
- jobId: string(),
28242
- taskId: string(),
28243
- backups: array(object({
28244
- name: string(),
28245
- backupPath: string(),
28246
- livePath: string()
28247
- })),
28248
- appliedAtMs: number(),
28249
- bootAttempts: number(),
28250
- schemaVersion: literal(1)
28251
- });
28252
28359
  /**
28253
28360
  * Pure fuzzy matcher for adoption location import. Normalized
28254
28361
  * case-insensitive Levenshtein similarity, used to reuse an existing
package/dist/addon.mjs CHANGED
@@ -7108,6 +7108,17 @@ var ModelCatalogEntrySchema = object({
7108
7108
  "imagenet",
7109
7109
  "none"
7110
7110
  ]).optional(),
7111
+ /**
7112
+ * The model already applies softmax IN-GRAPH — its raw output is a
7113
+ * probability distribution, not logits. When set, the `softmax`
7114
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7115
+ * probability vector collapses it toward uniform (top-1 score craters far
7116
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7117
+ * the output is raw logits and the postprocessor applies softmax (the normal
7118
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7119
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7120
+ */
7121
+ outputProbabilities: boolean().optional(),
7111
7122
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7112
7123
  /**
7113
7124
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12398,10 +12409,7 @@ var ConfigUISchemaNullableBridge = custom();
12398
12409
  var InferenceCapabilitiesBridge = custom();
12399
12410
  var ModelAvailabilityListBridge = custom();
12400
12411
  var PipelineRunResultBridge = custom();
12401
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12402
- kind: "mutation",
12403
- auth: "admin"
12404
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12412
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12405
12413
  modelId: string(),
12406
12414
  settings: record(string(), unknown()).readonly()
12407
12415
  }))), method(object({ steps: record(string(), object({
@@ -12461,13 +12469,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12461
12469
  * (inputClasses ≠ null) are skipped and served per-track via
12462
12470
  * pipelineRunner.runDetailSubtree (two-plane design).
12463
12471
  */
12464
- plane: _enum(["full", "frame"]).optional()
12472
+ plane: _enum(["full", "frame"]).optional(),
12473
+ /**
12474
+ * Inference-device selector (Phase 2 multi-device). Format
12475
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12476
+ * Omitted ⇒ the runner's default device (current single-engine
12477
+ * behaviour). Selects WHICH device pool of the node runs the call.
12478
+ */
12479
+ deviceKey: string().optional()
12465
12480
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12466
12481
  engine: PipelineEngineChoiceSchema.optional(),
12467
12482
  steps: array(PipelineStepInputSchema).min(1),
12468
12483
  frames: array(FrameInputSchema).min(1).max(255),
12469
12484
  deviceId: number().optional(),
12470
- sessionId: string().optional()
12485
+ sessionId: string().optional(),
12486
+ /**
12487
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12488
+ * the batch to the Python pool's bench preprocess cache
12489
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12490
+ * preprocessed ONCE and every later inference is a pure-inference cache
12491
+ * hit — the sustained-throughput run measures inference, not
12492
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12493
+ * full preprocess every call, correct). Fresh per sustained run;
12494
+ * released via `uncacheFrame`.
12495
+ */
12496
+ frameId: number().int().nonnegative().optional(),
12497
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12498
+ deviceKey: string().optional()
12471
12499
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12472
12500
  data: _instanceof(Uint8Array),
12473
12501
  width: number().int().positive(),
@@ -12499,8 +12527,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12499
12527
  * - `runtime` — main camera-serving engine (no idle TTL).
12500
12528
  * - `warm-override` — benchmark/test override held in the warm
12501
12529
  * cache; auto-disposed after the idle TTL.
12530
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12531
+ * multi-device, keyed by `deviceKey`) resolved
12532
+ * via `resolveDeviceFactory`. Runs alongside the
12533
+ * `runtime` engine on a DIFFERENT accelerator
12534
+ * (NPU / iGPU / Coral) — this is how the
12535
+ * Engines tab shows all pools running at once.
12502
12536
  */
12503
- kind: _enum(["runtime", "warm-override"]),
12537
+ kind: _enum([
12538
+ "runtime",
12539
+ "warm-override",
12540
+ "device-pool"
12541
+ ]),
12504
12542
  /** Native pid of the underlying Python pool (null when no pool). */
12505
12543
  poolPid: number().nullable(),
12506
12544
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12635,7 +12673,21 @@ var NativeCropResultSchema = object({
12635
12673
  /** Packed rgb (24-bit) pixels of the crop. */
12636
12674
  bytes: _instanceof(Uint8Array),
12637
12675
  width: number().int().positive(),
12638
- height: number().int().positive()
12676
+ height: number().int().positive(),
12677
+ /**
12678
+ * Which source served this crop, so a quality-sensitive consumer (the native
12679
+ * `keyFrame`) can reject a degraded fallback:
12680
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12681
+ * quality path).
12682
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12683
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12684
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12685
+ *
12686
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12687
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12688
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12689
+ */
12690
+ tier: _enum(["native", "ram-fullframe"]).optional()
12639
12691
  });
12640
12692
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12641
12693
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12875,7 +12927,14 @@ var RunnerCameraConfigSchema = object({
12875
12927
  * camera's detect node differs from its source-owner (P2d, gated by the
12876
12928
  * `remoteSourcingNodes` rollout setting).
12877
12929
  */
12878
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12930
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12931
+ /**
12932
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12933
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12934
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12935
+ * this only selects WHICH device pool of that node runs the session.
12936
+ */
12937
+ deviceKey: string().optional()
12879
12938
  });
12880
12939
  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;
12881
12940
  /**
@@ -12896,6 +12955,19 @@ var RunnerLocalLoadSchema = object({
12896
12955
  avgInferenceTimeMs: number(),
12897
12956
  /** Total queue depth across motion + detection queues. */
12898
12957
  queueDepthTotal: number(),
12958
+ /**
12959
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12960
+ * this runner currently has attached cameras on, so the orchestrator's second
12961
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12962
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12963
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12964
+ */
12965
+ devices: array(object({
12966
+ deviceKey: string(),
12967
+ backend: string(),
12968
+ attachedCameras: number(),
12969
+ queueDepthTotal: number()
12970
+ })).default([]),
12899
12971
  /** Hardware capability flags reported by this node. */
12900
12972
  hardware: object({
12901
12973
  hasGpu: boolean(),
@@ -16044,6 +16116,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16044
16116
  return toDeviceSummary(device, this.addonId);
16045
16117
  }
16046
16118
  };
16119
+ 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;
16120
+ new Set(Object.values(DeviceType));
16047
16121
  /**
16048
16122
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16049
16123
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17683,7 +17757,8 @@ var LinkedDeviceSchema = object({
17683
17757
  deviceId: number(),
17684
17758
  name: string(),
17685
17759
  location: string().nullable(),
17686
- features: array(string())
17760
+ features: array(string()),
17761
+ producesTrackedEvents: boolean().optional()
17687
17762
  });
17688
17763
  var SavedDeviceRowSchema = object({
17689
17764
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19313,6 +19388,7 @@ var TrackSchema = object({
19313
19388
  deviceId: number(),
19314
19389
  className: string(),
19315
19390
  label: string().optional(),
19391
+ producingDeviceName: string().optional(),
19316
19392
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19317
19393
  source: TrackSourceSchema.optional(),
19318
19394
  firstSeen: number(),
@@ -19450,7 +19526,8 @@ var MediaFileKindEnum = _enum([
19450
19526
  "fullFrameBoxed",
19451
19527
  "faceCrop",
19452
19528
  "plateCrop",
19453
- "keyFrame"
19529
+ "keyFrame",
19530
+ "keyFrameSmall"
19454
19531
  ]);
19455
19532
  var MediaFileSchema = object({
19456
19533
  key: string(),
@@ -19779,13 +19856,11 @@ var PipelineTemplateSchema = object({
19779
19856
  createdAt: string(),
19780
19857
  updatedAt: string()
19781
19858
  });
19782
- var AgentAddonConfigSchema = object({
19783
- enabled: boolean(),
19859
+ var DeviceStepConfigSchema = object({
19784
19860
  modelId: string().optional(),
19785
- settings: record(string(), unknown()).readonly()
19861
+ settings: record(string(), unknown()).optional()
19786
19862
  });
19787
19863
  var AgentPipelineSettingsSchema = object({
19788
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19789
19864
  maxCameras: number().int().nonnegative().nullable().default(null),
19790
19865
  /** Per-node detection weight (relative share for the quota balancer). */
19791
19866
  detectWeight: number().positive().optional(),
@@ -19809,7 +19884,22 @@ var AgentPipelineSettingsSchema = object({
19809
19884
  * it already uses to reach the hub). Set this only when the auto-detected
19810
19885
  * address is wrong (multi-homed host, NAT, custom interface).
19811
19886
  */
19812
- reachableHost: string().optional()
19887
+ reachableHost: string().optional(),
19888
+ /**
19889
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19890
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19891
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19892
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19893
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19894
+ * the default model/settings for every camera landing on that accelerator;
19895
+ * a stepId absent ⇒ the step uses that device's format default.
19896
+ */
19897
+ inferenceDevices: record(string(), object({
19898
+ enabled: boolean(),
19899
+ weight: number().positive().optional(),
19900
+ maxSessions: number().int().positive().optional(),
19901
+ steps: record(string(), DeviceStepConfigSchema).optional()
19902
+ })).optional()
19813
19903
  });
19814
19904
  var CameraPipelineForAgentSchema = object({
19815
19905
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19819,14 +19909,13 @@ var CameraPipelineForAgentSchema = object({
19819
19909
  }).nullable()
19820
19910
  });
19821
19911
  var CameraStepOverridePatchSchema = object({
19822
- enabled: boolean().optional(),
19823
19912
  modelId: string().optional(),
19824
19913
  settings: record(string(), unknown()).readonly().optional()
19825
19914
  });
19826
19915
  var CameraPipelineSettingsSchema = object({
19827
19916
  pinnedAgentNodeId: string().optional(),
19828
19917
  stepToggles: record(string(), boolean()).optional(),
19829
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19918
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19830
19919
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19831
19920
  });
19832
19921
  /**
@@ -20040,6 +20129,44 @@ var CameraStatusSchema = object({
20040
20129
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20041
20130
  fetchedAt: number()
20042
20131
  });
20132
+ var NodeInferenceDeviceSchema = object({
20133
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20134
+ key: string(),
20135
+ backend: string(),
20136
+ device: string(),
20137
+ format: _enum(MODEL_FORMATS),
20138
+ /** Whether the node's live probe reports the device as usable right now. */
20139
+ available: boolean(),
20140
+ /**
20141
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20142
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20143
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20144
+ * not a balanced target). An explicit stored value always wins; a stored-only
20145
+ * (unavailable) key keeps its stored value.
20146
+ */
20147
+ enabled: boolean(),
20148
+ /** Relative balancer weight for the enabled device (default 1). */
20149
+ weight: number(),
20150
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20151
+ maxSessions: number().nullable(),
20152
+ /** Object-detection model the executor defaults to for this deviceKey. */
20153
+ defaultModelId: string(),
20154
+ /**
20155
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20156
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20157
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20158
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20159
+ * available per format; this is the stored selection that becomes the
20160
+ * default for EVERY camera landing on this accelerator.
20161
+ */
20162
+ steps: record(string(), DeviceStepConfigSchema).optional()
20163
+ });
20164
+ var NodeInferenceDevicesSchema = object({
20165
+ nodeId: string(),
20166
+ /** False when the node's platform-probe was unreachable (no live device set). */
20167
+ reachable: boolean(),
20168
+ devices: array(NodeInferenceDeviceSchema).readonly()
20169
+ });
20043
20170
  method(object({
20044
20171
  deviceId: number(),
20045
20172
  agentNodeId: string()
@@ -20049,7 +20176,13 @@ method(object({
20049
20176
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20050
20177
  kind: "mutation",
20051
20178
  auth: "admin"
20052
- }), method(_void(), object({ migrated: number() }), {
20179
+ }), method(object({
20180
+ deviceId: number(),
20181
+ deviceKey: string()
20182
+ }), object({ success: literal(true) }), {
20183
+ kind: "mutation",
20184
+ auth: "admin"
20185
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20053
20186
  kind: "mutation",
20054
20187
  auth: "admin"
20055
20188
  }), 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({
@@ -20083,13 +20216,7 @@ method(object({
20083
20216
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20084
20217
  nodeId: string(),
20085
20218
  settings: AgentPipelineSettingsSchema
20086
- })).readonly()), method(object({
20087
- agentNodeId: string(),
20088
- defaults: record(string(), AgentAddonConfigSchema)
20089
- }), object({ success: literal(true) }), {
20090
- kind: "mutation",
20091
- auth: "admin"
20092
- }), method(object({ agentNodeId: string() }), object({
20219
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20093
20220
  success: boolean(),
20094
20221
  removed: boolean()
20095
20222
  }), {
@@ -20121,7 +20248,18 @@ method(object({
20121
20248
  }), object({ success: literal(true) }), {
20122
20249
  kind: "mutation",
20123
20250
  auth: "admin"
20124
- }), method(object({ agentNodeId: string() }), object({
20251
+ }), method(object({
20252
+ agentNodeId: string(),
20253
+ inferenceDevices: record(string(), object({
20254
+ enabled: boolean(),
20255
+ weight: number().positive().optional(),
20256
+ maxSessions: number().int().positive().optional(),
20257
+ steps: record(string(), DeviceStepConfigSchema).optional()
20258
+ }))
20259
+ }), object({ success: literal(true) }), {
20260
+ kind: "mutation",
20261
+ auth: "admin"
20262
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20125
20263
  success: literal(true),
20126
20264
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20127
20265
  effectiveModelId: string().nullable(),
@@ -20137,9 +20275,10 @@ method(object({
20137
20275
  }), object({ success: literal(true) }), {
20138
20276
  kind: "mutation",
20139
20277
  auth: "admin"
20140
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20278
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20141
20279
  deviceId: number(),
20142
20280
  agentNodeId: string(),
20281
+ deviceKey: string(),
20143
20282
  addonId: string(),
20144
20283
  patch: CameraStepOverridePatchSchema.nullable()
20145
20284
  }), object({ success: literal(true) }), {
@@ -20176,14 +20315,13 @@ method(object({
20176
20315
  });
20177
20316
  /**
20178
20317
  * server-management — per-NODE singleton capability for a node's ROOT
20179
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20180
- * agents).
20318
+ * package lifecycle (runtime-updatable node packages).
20181
20319
  *
20182
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20183
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20184
- * version describes the node. Updates install into
20185
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20186
- * starter (probation boot + auto-rollback to N-1).
20320
+ * Every node role runs the SAME root package (`@camstack/server`), which
20321
+ * carries the whole software stack in its npm dep tree, so ONE version
20322
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20323
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20324
+ * no auto-rollback).
20187
20325
  *
20188
20326
  * Providers:
20189
20327
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20291,7 +20429,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20291
20429
  /** Explicit target version; omitted = latest from the registry. */
20292
20430
  version: string().optional() }), ServerUpdateActionResultSchema, {
20293
20431
  kind: "mutation",
20294
- auth: "admin"
20432
+ auth: "admin",
20433
+ timeoutMs: 16 * 6e4
20295
20434
  }), method(_void(), ServerUpdateActionResultSchema, {
20296
20435
  kind: "mutation",
20297
20436
  auth: "admin"
@@ -21335,22 +21474,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21335
21474
  var RestartAddonResultSchema = unknown();
21336
21475
  var InstallPackageResultSchema = unknown();
21337
21476
  var ReloadPackagesResultSchema = unknown();
21338
- /**
21339
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21340
- * server restarts so the admin UI can react to the `restartingAt`
21341
- * timestamp (shows reconnect overlay). The transition from
21342
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21343
- * `system.restart-completed` event after the new process boots.
21344
- *
21345
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21346
- */
21347
- var UpdateFrameworkPackageResultSchema = object({
21348
- packageName: string(),
21349
- fromVersion: string(),
21350
- toVersion: string(),
21351
- /** Ms-epoch the server scheduled its self-restart. */
21352
- restartingAt: number()
21353
- });
21354
21477
  var BulkUpdateItemStatusSchema = _enum([
21355
21478
  "queued",
21356
21479
  "updating",
@@ -21478,13 +21601,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21478
21601
  }), object({ success: literal(true) }), {
21479
21602
  kind: "mutation",
21480
21603
  auth: "admin"
21481
- }), method(object({
21482
- packageName: string().min(1),
21483
- version: string().optional(),
21484
- deferRestart: boolean().optional()
21485
- }), UpdateFrameworkPackageResultSchema, {
21486
- kind: "mutation",
21487
- auth: "admin"
21488
21604
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21489
21605
  kind: "mutation",
21490
21606
  auth: "admin"
@@ -22350,10 +22466,10 @@ var TopologyCategorySchema = object({
22350
22466
  addons: array(TopologyCategoryAddonSchema).readonly()
22351
22467
  });
22352
22468
  /**
22353
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22354
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22355
- * version visibility for the Server management surface. Nullable: offline
22356
- * rows and pre-phase-2 nodes report none.
22469
+ * The node's runtime-updatable ROOT package (`@camstack/server` — the single
22470
+ * root package for every node role) as reported by its `registerNode`
22471
+ * manifest — version visibility for the Server management surface. Nullable:
22472
+ * offline rows and nodes that never reported one.
22357
22473
  */
22358
22474
  var TopologyRootPackageSchema = object({
22359
22475
  name: string(),
@@ -22741,17 +22857,28 @@ var PlatformScoreSchema = object({
22741
22857
  format: _enum([
22742
22858
  "onnx",
22743
22859
  "coreml",
22744
- "openvino"
22860
+ "openvino",
22861
+ "tflite"
22745
22862
  ]),
22746
22863
  score: number(),
22747
22864
  reason: string(),
22748
22865
  available: boolean()
22749
22866
  });
22867
+ var InferenceDeviceDescriptorSchema = object({
22868
+ key: string(),
22869
+ backend: string(),
22870
+ device: string(),
22871
+ format: ModelFormatSchema,
22872
+ runtime: literal("python"),
22873
+ score: number(),
22874
+ available: boolean()
22875
+ });
22750
22876
  var PlatformCapabilitiesSchema = object({
22751
22877
  hardware: HardwareInfoSchema,
22752
22878
  scores: array(PlatformScoreSchema).readonly(),
22753
22879
  bestScore: PlatformScoreSchema,
22754
- pythonPath: string().nullable()
22880
+ pythonPath: string().nullable(),
22881
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22755
22882
  });
22756
22883
  var ModelRequirementSchema = object({
22757
22884
  modelId: string(),
@@ -23630,12 +23757,6 @@ Object.freeze({
23630
23757
  addonId: null,
23631
23758
  access: "delete"
23632
23759
  },
23633
- "addons.updateFrameworkPackage": {
23634
- capName: "addons",
23635
- capScope: "system",
23636
- addonId: null,
23637
- access: "create"
23638
- },
23639
23760
  "addons.updatePackage": {
23640
23761
  capName: "addons",
23641
23762
  capScope: "system",
@@ -26408,12 +26529,6 @@ Object.freeze({
26408
26529
  addonId: null,
26409
26530
  access: "view"
26410
26531
  },
26411
- "pipelineExecutor.reprobeEngine": {
26412
- capName: "pipeline-executor",
26413
- capScope: "system",
26414
- addonId: null,
26415
- access: "create"
26416
- },
26417
26532
  "pipelineExecutor.runAudioTest": {
26418
26533
  capName: "pipeline-executor",
26419
26534
  capScope: "system",
@@ -26564,6 +26679,12 @@ Object.freeze({
26564
26679
  addonId: null,
26565
26680
  access: "view"
26566
26681
  },
26682
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26683
+ capName: "pipeline-orchestrator",
26684
+ capScope: "system",
26685
+ addonId: null,
26686
+ access: "view"
26687
+ },
26567
26688
  "pipelineOrchestrator.getPipelineAssignment": {
26568
26689
  capName: "pipeline-orchestrator",
26569
26690
  capScope: "system",
@@ -26576,6 +26697,12 @@ Object.freeze({
26576
26697
  addonId: null,
26577
26698
  access: "view"
26578
26699
  },
26700
+ "pipelineOrchestrator.getPipelineDevicePin": {
26701
+ capName: "pipeline-orchestrator",
26702
+ capScope: "system",
26703
+ addonId: null,
26704
+ access: "view"
26705
+ },
26579
26706
  "pipelineOrchestrator.listAgentSettings": {
26580
26707
  capName: "pipeline-orchestrator",
26581
26708
  capScope: "system",
@@ -26618,19 +26745,19 @@ Object.freeze({
26618
26745
  addonId: null,
26619
26746
  access: "create"
26620
26747
  },
26621
- "pipelineOrchestrator.setAgentAddonDefaults": {
26748
+ "pipelineOrchestrator.setAgentCapabilities": {
26622
26749
  capName: "pipeline-orchestrator",
26623
26750
  capScope: "system",
26624
26751
  addonId: null,
26625
26752
  access: "create"
26626
26753
  },
26627
- "pipelineOrchestrator.setAgentCapabilities": {
26754
+ "pipelineOrchestrator.setAgentDetectWeight": {
26628
26755
  capName: "pipeline-orchestrator",
26629
26756
  capScope: "system",
26630
26757
  addonId: null,
26631
26758
  access: "create"
26632
26759
  },
26633
- "pipelineOrchestrator.setAgentDetectWeight": {
26760
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26634
26761
  capName: "pipeline-orchestrator",
26635
26762
  capScope: "system",
26636
26763
  addonId: null,
@@ -26672,6 +26799,12 @@ Object.freeze({
26672
26799
  addonId: null,
26673
26800
  access: "create"
26674
26801
  },
26802
+ "pipelineOrchestrator.setPipelineDevicePin": {
26803
+ capName: "pipeline-orchestrator",
26804
+ capScope: "system",
26805
+ addonId: null,
26806
+ access: "create"
26807
+ },
26675
26808
  "pipelineOrchestrator.unassignAudio": {
26676
26809
  capName: "pipeline-orchestrator",
26677
26810
  capScope: "system",
@@ -28224,32 +28357,6 @@ Object.freeze({
28224
28357
  "network-access": "ingress",
28225
28358
  "smtp-provider": "email"
28226
28359
  });
28227
- var frameworkSwapPackageSchema = object({
28228
- name: string(),
28229
- stagedPath: string(),
28230
- backupPath: string(),
28231
- toVersion: string(),
28232
- fromVersion: string().nullable()
28233
- });
28234
- object({
28235
- jobId: string(),
28236
- taskId: string(),
28237
- packages: array(frameworkSwapPackageSchema),
28238
- requestedAtMs: number(),
28239
- schemaVersion: literal(1)
28240
- });
28241
- object({
28242
- jobId: string(),
28243
- taskId: string(),
28244
- backups: array(object({
28245
- name: string(),
28246
- backupPath: string(),
28247
- livePath: string()
28248
- })),
28249
- appliedAtMs: number(),
28250
- bootAttempts: number(),
28251
- schemaVersion: literal(1)
28252
- });
28253
28360
  /**
28254
28361
  * Pure fuzzy matcher for adoption location import. Normalized
28255
28362
  * case-insensitive Levenshtein similarity, used to reuse an existing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.1.27",
3
+ "version": "1.2.1",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",