@camstack/addon-provider-tuya 0.1.14 → 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 +213 -106
  2. package/dist/addon.mjs +213 -106
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7102,6 +7102,17 @@ var ModelCatalogEntrySchema = object({
7102
7102
  "imagenet",
7103
7103
  "none"
7104
7104
  ]).optional(),
7105
+ /**
7106
+ * The model already applies softmax IN-GRAPH — its raw output is a
7107
+ * probability distribution, not logits. When set, the `softmax`
7108
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7109
+ * probability vector collapses it toward uniform (top-1 score craters far
7110
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7111
+ * the output is raw logits and the postprocessor applies softmax (the normal
7112
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7113
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7114
+ */
7115
+ outputProbabilities: boolean().optional(),
7105
7116
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7106
7117
  /**
7107
7118
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12392,10 +12403,7 @@ var ConfigUISchemaNullableBridge = custom();
12392
12403
  var InferenceCapabilitiesBridge = custom();
12393
12404
  var ModelAvailabilityListBridge = custom();
12394
12405
  var PipelineRunResultBridge = custom();
12395
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12396
- kind: "mutation",
12397
- auth: "admin"
12398
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12406
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12399
12407
  modelId: string(),
12400
12408
  settings: record(string(), unknown()).readonly()
12401
12409
  }))), method(object({ steps: record(string(), object({
@@ -12455,13 +12463,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12455
12463
  * (inputClasses ≠ null) are skipped and served per-track via
12456
12464
  * pipelineRunner.runDetailSubtree (two-plane design).
12457
12465
  */
12458
- plane: _enum(["full", "frame"]).optional()
12466
+ plane: _enum(["full", "frame"]).optional(),
12467
+ /**
12468
+ * Inference-device selector (Phase 2 multi-device). Format
12469
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12470
+ * Omitted ⇒ the runner's default device (current single-engine
12471
+ * behaviour). Selects WHICH device pool of the node runs the call.
12472
+ */
12473
+ deviceKey: string().optional()
12459
12474
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12460
12475
  engine: PipelineEngineChoiceSchema.optional(),
12461
12476
  steps: array(PipelineStepInputSchema).min(1),
12462
12477
  frames: array(FrameInputSchema).min(1).max(255),
12463
12478
  deviceId: number().optional(),
12464
- sessionId: string().optional()
12479
+ sessionId: string().optional(),
12480
+ /**
12481
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12482
+ * the batch to the Python pool's bench preprocess cache
12483
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12484
+ * preprocessed ONCE and every later inference is a pure-inference cache
12485
+ * hit — the sustained-throughput run measures inference, not
12486
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12487
+ * full preprocess every call, correct). Fresh per sustained run;
12488
+ * released via `uncacheFrame`.
12489
+ */
12490
+ frameId: number().int().nonnegative().optional(),
12491
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12492
+ deviceKey: string().optional()
12465
12493
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12466
12494
  data: _instanceof(Uint8Array),
12467
12495
  width: number().int().positive(),
@@ -12493,8 +12521,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12493
12521
  * - `runtime` — main camera-serving engine (no idle TTL).
12494
12522
  * - `warm-override` — benchmark/test override held in the warm
12495
12523
  * cache; auto-disposed after the idle TTL.
12524
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12525
+ * multi-device, keyed by `deviceKey`) resolved
12526
+ * via `resolveDeviceFactory`. Runs alongside the
12527
+ * `runtime` engine on a DIFFERENT accelerator
12528
+ * (NPU / iGPU / Coral) — this is how the
12529
+ * Engines tab shows all pools running at once.
12496
12530
  */
12497
- kind: _enum(["runtime", "warm-override"]),
12531
+ kind: _enum([
12532
+ "runtime",
12533
+ "warm-override",
12534
+ "device-pool"
12535
+ ]),
12498
12536
  /** Native pid of the underlying Python pool (null when no pool). */
12499
12537
  poolPid: number().nullable(),
12500
12538
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12629,7 +12667,21 @@ var NativeCropResultSchema = object({
12629
12667
  /** Packed rgb (24-bit) pixels of the crop. */
12630
12668
  bytes: _instanceof(Uint8Array),
12631
12669
  width: number().int().positive(),
12632
- height: number().int().positive()
12670
+ height: number().int().positive(),
12671
+ /**
12672
+ * Which source served this crop, so a quality-sensitive consumer (the native
12673
+ * `keyFrame`) can reject a degraded fallback:
12674
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12675
+ * quality path).
12676
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12677
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12678
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12679
+ *
12680
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12681
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12682
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12683
+ */
12684
+ tier: _enum(["native", "ram-fullframe"]).optional()
12633
12685
  });
12634
12686
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12635
12687
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12869,7 +12921,14 @@ var RunnerCameraConfigSchema = object({
12869
12921
  * camera's detect node differs from its source-owner (P2d, gated by the
12870
12922
  * `remoteSourcingNodes` rollout setting).
12871
12923
  */
12872
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12924
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12925
+ /**
12926
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12927
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12928
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12929
+ * this only selects WHICH device pool of that node runs the session.
12930
+ */
12931
+ deviceKey: string().optional()
12873
12932
  });
12874
12933
  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;
12875
12934
  /**
@@ -12890,6 +12949,19 @@ var RunnerLocalLoadSchema = object({
12890
12949
  avgInferenceTimeMs: number(),
12891
12950
  /** Total queue depth across motion + detection queues. */
12892
12951
  queueDepthTotal: number(),
12952
+ /**
12953
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12954
+ * this runner currently has attached cameras on, so the orchestrator's second
12955
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12956
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12957
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12958
+ */
12959
+ devices: array(object({
12960
+ deviceKey: string(),
12961
+ backend: string(),
12962
+ attachedCameras: number(),
12963
+ queueDepthTotal: number()
12964
+ })).default([]),
12893
12965
  /** Hardware capability flags reported by this node. */
12894
12966
  hardware: object({
12895
12967
  hasGpu: boolean(),
@@ -16038,6 +16110,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16038
16110
  return toDeviceSummary(device, this.addonId);
16039
16111
  }
16040
16112
  };
16113
+ 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;
16114
+ new Set(Object.values(DeviceType));
16041
16115
  /**
16042
16116
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16043
16117
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17628,7 +17702,8 @@ var LinkedDeviceSchema = object({
17628
17702
  deviceId: number(),
17629
17703
  name: string(),
17630
17704
  location: string().nullable(),
17631
- features: array(string())
17705
+ features: array(string()),
17706
+ producesTrackedEvents: boolean().optional()
17632
17707
  });
17633
17708
  var SavedDeviceRowSchema = object({
17634
17709
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19258,6 +19333,7 @@ var TrackSchema = object({
19258
19333
  deviceId: number(),
19259
19334
  className: string(),
19260
19335
  label: string().optional(),
19336
+ producingDeviceName: string().optional(),
19261
19337
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19262
19338
  source: TrackSourceSchema.optional(),
19263
19339
  firstSeen: number(),
@@ -19395,7 +19471,8 @@ var MediaFileKindEnum = _enum([
19395
19471
  "fullFrameBoxed",
19396
19472
  "faceCrop",
19397
19473
  "plateCrop",
19398
- "keyFrame"
19474
+ "keyFrame",
19475
+ "keyFrameSmall"
19399
19476
  ]);
19400
19477
  var MediaFileSchema = object({
19401
19478
  key: string(),
@@ -19724,13 +19801,11 @@ var PipelineTemplateSchema = object({
19724
19801
  createdAt: string(),
19725
19802
  updatedAt: string()
19726
19803
  });
19727
- var AgentAddonConfigSchema = object({
19728
- enabled: boolean(),
19804
+ var DeviceStepConfigSchema = object({
19729
19805
  modelId: string().optional(),
19730
- settings: record(string(), unknown()).readonly()
19806
+ settings: record(string(), unknown()).optional()
19731
19807
  });
19732
19808
  var AgentPipelineSettingsSchema = object({
19733
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19734
19809
  maxCameras: number().int().nonnegative().nullable().default(null),
19735
19810
  /** Per-node detection weight (relative share for the quota balancer). */
19736
19811
  detectWeight: number().positive().optional(),
@@ -19754,7 +19829,22 @@ var AgentPipelineSettingsSchema = object({
19754
19829
  * it already uses to reach the hub). Set this only when the auto-detected
19755
19830
  * address is wrong (multi-homed host, NAT, custom interface).
19756
19831
  */
19757
- reachableHost: string().optional()
19832
+ reachableHost: string().optional(),
19833
+ /**
19834
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19835
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19836
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19837
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19838
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19839
+ * the default model/settings for every camera landing on that accelerator;
19840
+ * a stepId absent ⇒ the step uses that device's format default.
19841
+ */
19842
+ inferenceDevices: record(string(), object({
19843
+ enabled: boolean(),
19844
+ weight: number().positive().optional(),
19845
+ maxSessions: number().int().positive().optional(),
19846
+ steps: record(string(), DeviceStepConfigSchema).optional()
19847
+ })).optional()
19758
19848
  });
19759
19849
  var CameraPipelineForAgentSchema = object({
19760
19850
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19764,14 +19854,13 @@ var CameraPipelineForAgentSchema = object({
19764
19854
  }).nullable()
19765
19855
  });
19766
19856
  var CameraStepOverridePatchSchema = object({
19767
- enabled: boolean().optional(),
19768
19857
  modelId: string().optional(),
19769
19858
  settings: record(string(), unknown()).readonly().optional()
19770
19859
  });
19771
19860
  var CameraPipelineSettingsSchema = object({
19772
19861
  pinnedAgentNodeId: string().optional(),
19773
19862
  stepToggles: record(string(), boolean()).optional(),
19774
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19863
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19775
19864
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19776
19865
  });
19777
19866
  /**
@@ -19985,6 +20074,44 @@ var CameraStatusSchema = object({
19985
20074
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19986
20075
  fetchedAt: number()
19987
20076
  });
20077
+ var NodeInferenceDeviceSchema = object({
20078
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20079
+ key: string(),
20080
+ backend: string(),
20081
+ device: string(),
20082
+ format: _enum(MODEL_FORMATS),
20083
+ /** Whether the node's live probe reports the device as usable right now. */
20084
+ available: boolean(),
20085
+ /**
20086
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20087
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20088
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20089
+ * not a balanced target). An explicit stored value always wins; a stored-only
20090
+ * (unavailable) key keeps its stored value.
20091
+ */
20092
+ enabled: boolean(),
20093
+ /** Relative balancer weight for the enabled device (default 1). */
20094
+ weight: number(),
20095
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20096
+ maxSessions: number().nullable(),
20097
+ /** Object-detection model the executor defaults to for this deviceKey. */
20098
+ defaultModelId: string(),
20099
+ /**
20100
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20101
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20102
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20103
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20104
+ * available per format; this is the stored selection that becomes the
20105
+ * default for EVERY camera landing on this accelerator.
20106
+ */
20107
+ steps: record(string(), DeviceStepConfigSchema).optional()
20108
+ });
20109
+ var NodeInferenceDevicesSchema = object({
20110
+ nodeId: string(),
20111
+ /** False when the node's platform-probe was unreachable (no live device set). */
20112
+ reachable: boolean(),
20113
+ devices: array(NodeInferenceDeviceSchema).readonly()
20114
+ });
19988
20115
  method(object({
19989
20116
  deviceId: number(),
19990
20117
  agentNodeId: string()
@@ -19994,7 +20121,13 @@ method(object({
19994
20121
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19995
20122
  kind: "mutation",
19996
20123
  auth: "admin"
19997
- }), method(_void(), object({ migrated: number() }), {
20124
+ }), method(object({
20125
+ deviceId: number(),
20126
+ deviceKey: string()
20127
+ }), object({ success: literal(true) }), {
20128
+ kind: "mutation",
20129
+ auth: "admin"
20130
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19998
20131
  kind: "mutation",
19999
20132
  auth: "admin"
20000
20133
  }), 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({
@@ -20028,13 +20161,7 @@ method(object({
20028
20161
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20029
20162
  nodeId: string(),
20030
20163
  settings: AgentPipelineSettingsSchema
20031
- })).readonly()), method(object({
20032
- agentNodeId: string(),
20033
- defaults: record(string(), AgentAddonConfigSchema)
20034
- }), object({ success: literal(true) }), {
20035
- kind: "mutation",
20036
- auth: "admin"
20037
- }), method(object({ agentNodeId: string() }), object({
20164
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20038
20165
  success: boolean(),
20039
20166
  removed: boolean()
20040
20167
  }), {
@@ -20066,7 +20193,18 @@ method(object({
20066
20193
  }), object({ success: literal(true) }), {
20067
20194
  kind: "mutation",
20068
20195
  auth: "admin"
20069
- }), method(object({ agentNodeId: string() }), object({
20196
+ }), method(object({
20197
+ agentNodeId: string(),
20198
+ inferenceDevices: record(string(), object({
20199
+ enabled: boolean(),
20200
+ weight: number().positive().optional(),
20201
+ maxSessions: number().int().positive().optional(),
20202
+ steps: record(string(), DeviceStepConfigSchema).optional()
20203
+ }))
20204
+ }), object({ success: literal(true) }), {
20205
+ kind: "mutation",
20206
+ auth: "admin"
20207
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20070
20208
  success: literal(true),
20071
20209
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20072
20210
  effectiveModelId: string().nullable(),
@@ -20082,9 +20220,10 @@ method(object({
20082
20220
  }), object({ success: literal(true) }), {
20083
20221
  kind: "mutation",
20084
20222
  auth: "admin"
20085
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20223
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20086
20224
  deviceId: number(),
20087
20225
  agentNodeId: string(),
20226
+ deviceKey: string(),
20088
20227
  addonId: string(),
20089
20228
  patch: CameraStepOverridePatchSchema.nullable()
20090
20229
  }), object({ success: literal(true) }), {
@@ -20121,14 +20260,13 @@ method(object({
20121
20260
  });
20122
20261
  /**
20123
20262
  * server-management — per-NODE singleton capability for a node's ROOT
20124
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20125
- * agents).
20263
+ * package lifecycle (runtime-updatable node packages).
20126
20264
  *
20127
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20128
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20129
- * version describes the node. Updates install into
20130
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20131
- * starter (probation boot + auto-rollback to N-1).
20265
+ * Every node role runs the SAME root package (`@camstack/server`), which
20266
+ * carries the whole software stack in its npm dep tree, so ONE version
20267
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20268
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20269
+ * no auto-rollback).
20132
20270
  *
20133
20271
  * Providers:
20134
20272
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20236,7 +20374,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20236
20374
  /** Explicit target version; omitted = latest from the registry. */
20237
20375
  version: string().optional() }), ServerUpdateActionResultSchema, {
20238
20376
  kind: "mutation",
20239
- auth: "admin"
20377
+ auth: "admin",
20378
+ timeoutMs: 16 * 6e4
20240
20379
  }), method(_void(), ServerUpdateActionResultSchema, {
20241
20380
  kind: "mutation",
20242
20381
  auth: "admin"
@@ -21280,22 +21419,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21280
21419
  var RestartAddonResultSchema = unknown();
21281
21420
  var InstallPackageResultSchema = unknown();
21282
21421
  var ReloadPackagesResultSchema = unknown();
21283
- /**
21284
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21285
- * server restarts so the admin UI can react to the `restartingAt`
21286
- * timestamp (shows reconnect overlay). The transition from
21287
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21288
- * `system.restart-completed` event after the new process boots.
21289
- *
21290
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21291
- */
21292
- var UpdateFrameworkPackageResultSchema = object({
21293
- packageName: string(),
21294
- fromVersion: string(),
21295
- toVersion: string(),
21296
- /** Ms-epoch the server scheduled its self-restart. */
21297
- restartingAt: number()
21298
- });
21299
21422
  var BulkUpdateItemStatusSchema = _enum([
21300
21423
  "queued",
21301
21424
  "updating",
@@ -21423,13 +21546,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21423
21546
  }), object({ success: literal(true) }), {
21424
21547
  kind: "mutation",
21425
21548
  auth: "admin"
21426
- }), method(object({
21427
- packageName: string().min(1),
21428
- version: string().optional(),
21429
- deferRestart: boolean().optional()
21430
- }), UpdateFrameworkPackageResultSchema, {
21431
- kind: "mutation",
21432
- auth: "admin"
21433
21549
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21434
21550
  kind: "mutation",
21435
21551
  auth: "admin"
@@ -22295,10 +22411,10 @@ var TopologyCategorySchema = object({
22295
22411
  addons: array(TopologyCategoryAddonSchema).readonly()
22296
22412
  });
22297
22413
  /**
22298
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22299
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22300
- * version visibility for the Server management surface. Nullable: offline
22301
- * rows and pre-phase-2 nodes report none.
22414
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22415
+ * root package for every node role) as reported by its `registerNode`
22416
+ * manifest — version visibility for the Server management surface. Nullable:
22417
+ * offline rows and nodes that never reported one.
22302
22418
  */
22303
22419
  var TopologyRootPackageSchema = object({
22304
22420
  name: string(),
@@ -22686,17 +22802,28 @@ var PlatformScoreSchema = object({
22686
22802
  format: _enum([
22687
22803
  "onnx",
22688
22804
  "coreml",
22689
- "openvino"
22805
+ "openvino",
22806
+ "tflite"
22690
22807
  ]),
22691
22808
  score: number(),
22692
22809
  reason: string(),
22693
22810
  available: boolean()
22694
22811
  });
22812
+ var InferenceDeviceDescriptorSchema = object({
22813
+ key: string(),
22814
+ backend: string(),
22815
+ device: string(),
22816
+ format: ModelFormatSchema,
22817
+ runtime: literal("python"),
22818
+ score: number(),
22819
+ available: boolean()
22820
+ });
22695
22821
  var PlatformCapabilitiesSchema = object({
22696
22822
  hardware: HardwareInfoSchema,
22697
22823
  scores: array(PlatformScoreSchema).readonly(),
22698
22824
  bestScore: PlatformScoreSchema,
22699
- pythonPath: string().nullable()
22825
+ pythonPath: string().nullable(),
22826
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22700
22827
  });
22701
22828
  var ModelRequirementSchema = object({
22702
22829
  modelId: string(),
@@ -23575,12 +23702,6 @@ Object.freeze({
23575
23702
  addonId: null,
23576
23703
  access: "delete"
23577
23704
  },
23578
- "addons.updateFrameworkPackage": {
23579
- capName: "addons",
23580
- capScope: "system",
23581
- addonId: null,
23582
- access: "create"
23583
- },
23584
23705
  "addons.updatePackage": {
23585
23706
  capName: "addons",
23586
23707
  capScope: "system",
@@ -26353,12 +26474,6 @@ Object.freeze({
26353
26474
  addonId: null,
26354
26475
  access: "view"
26355
26476
  },
26356
- "pipelineExecutor.reprobeEngine": {
26357
- capName: "pipeline-executor",
26358
- capScope: "system",
26359
- addonId: null,
26360
- access: "create"
26361
- },
26362
26477
  "pipelineExecutor.runAudioTest": {
26363
26478
  capName: "pipeline-executor",
26364
26479
  capScope: "system",
@@ -26509,6 +26624,12 @@ Object.freeze({
26509
26624
  addonId: null,
26510
26625
  access: "view"
26511
26626
  },
26627
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26628
+ capName: "pipeline-orchestrator",
26629
+ capScope: "system",
26630
+ addonId: null,
26631
+ access: "view"
26632
+ },
26512
26633
  "pipelineOrchestrator.getPipelineAssignment": {
26513
26634
  capName: "pipeline-orchestrator",
26514
26635
  capScope: "system",
@@ -26521,6 +26642,12 @@ Object.freeze({
26521
26642
  addonId: null,
26522
26643
  access: "view"
26523
26644
  },
26645
+ "pipelineOrchestrator.getPipelineDevicePin": {
26646
+ capName: "pipeline-orchestrator",
26647
+ capScope: "system",
26648
+ addonId: null,
26649
+ access: "view"
26650
+ },
26524
26651
  "pipelineOrchestrator.listAgentSettings": {
26525
26652
  capName: "pipeline-orchestrator",
26526
26653
  capScope: "system",
@@ -26563,19 +26690,19 @@ Object.freeze({
26563
26690
  addonId: null,
26564
26691
  access: "create"
26565
26692
  },
26566
- "pipelineOrchestrator.setAgentAddonDefaults": {
26693
+ "pipelineOrchestrator.setAgentCapabilities": {
26567
26694
  capName: "pipeline-orchestrator",
26568
26695
  capScope: "system",
26569
26696
  addonId: null,
26570
26697
  access: "create"
26571
26698
  },
26572
- "pipelineOrchestrator.setAgentCapabilities": {
26699
+ "pipelineOrchestrator.setAgentDetectWeight": {
26573
26700
  capName: "pipeline-orchestrator",
26574
26701
  capScope: "system",
26575
26702
  addonId: null,
26576
26703
  access: "create"
26577
26704
  },
26578
- "pipelineOrchestrator.setAgentDetectWeight": {
26705
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26579
26706
  capName: "pipeline-orchestrator",
26580
26707
  capScope: "system",
26581
26708
  addonId: null,
@@ -26617,6 +26744,12 @@ Object.freeze({
26617
26744
  addonId: null,
26618
26745
  access: "create"
26619
26746
  },
26747
+ "pipelineOrchestrator.setPipelineDevicePin": {
26748
+ capName: "pipeline-orchestrator",
26749
+ capScope: "system",
26750
+ addonId: null,
26751
+ access: "create"
26752
+ },
26620
26753
  "pipelineOrchestrator.unassignAudio": {
26621
26754
  capName: "pipeline-orchestrator",
26622
26755
  capScope: "system",
@@ -28169,32 +28302,6 @@ Object.freeze({
28169
28302
  "network-access": "ingress",
28170
28303
  "smtp-provider": "email"
28171
28304
  });
28172
- var frameworkSwapPackageSchema = object({
28173
- name: string(),
28174
- stagedPath: string(),
28175
- backupPath: string(),
28176
- toVersion: string(),
28177
- fromVersion: string().nullable()
28178
- });
28179
- object({
28180
- jobId: string(),
28181
- taskId: string(),
28182
- packages: array(frameworkSwapPackageSchema),
28183
- requestedAtMs: number(),
28184
- schemaVersion: literal(1)
28185
- });
28186
- object({
28187
- jobId: string(),
28188
- taskId: string(),
28189
- backups: array(object({
28190
- name: string(),
28191
- backupPath: string(),
28192
- livePath: string()
28193
- })),
28194
- appliedAtMs: number(),
28195
- bootAttempts: number(),
28196
- schemaVersion: literal(1)
28197
- });
28198
28305
  //#endregion
28199
28306
  //#region src/config.ts
28200
28307
  /**
package/dist/addon.mjs CHANGED
@@ -7101,6 +7101,17 @@ var ModelCatalogEntrySchema = object({
7101
7101
  "imagenet",
7102
7102
  "none"
7103
7103
  ]).optional(),
7104
+ /**
7105
+ * The model already applies softmax IN-GRAPH — its raw output is a
7106
+ * probability distribution, not logits. When set, the `softmax`
7107
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7108
+ * probability vector collapses it toward uniform (top-1 score craters far
7109
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7110
+ * the output is raw logits and the postprocessor applies softmax (the normal
7111
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7112
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7113
+ */
7114
+ outputProbabilities: boolean().optional(),
7104
7115
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7105
7116
  /**
7106
7117
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12391,10 +12402,7 @@ var ConfigUISchemaNullableBridge = custom();
12391
12402
  var InferenceCapabilitiesBridge = custom();
12392
12403
  var ModelAvailabilityListBridge = custom();
12393
12404
  var PipelineRunResultBridge = custom();
12394
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12395
- kind: "mutation",
12396
- auth: "admin"
12397
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12405
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12398
12406
  modelId: string(),
12399
12407
  settings: record(string(), unknown()).readonly()
12400
12408
  }))), method(object({ steps: record(string(), object({
@@ -12454,13 +12462,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12454
12462
  * (inputClasses ≠ null) are skipped and served per-track via
12455
12463
  * pipelineRunner.runDetailSubtree (two-plane design).
12456
12464
  */
12457
- plane: _enum(["full", "frame"]).optional()
12465
+ plane: _enum(["full", "frame"]).optional(),
12466
+ /**
12467
+ * Inference-device selector (Phase 2 multi-device). Format
12468
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12469
+ * Omitted ⇒ the runner's default device (current single-engine
12470
+ * behaviour). Selects WHICH device pool of the node runs the call.
12471
+ */
12472
+ deviceKey: string().optional()
12458
12473
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12459
12474
  engine: PipelineEngineChoiceSchema.optional(),
12460
12475
  steps: array(PipelineStepInputSchema).min(1),
12461
12476
  frames: array(FrameInputSchema).min(1).max(255),
12462
12477
  deviceId: number().optional(),
12463
- sessionId: string().optional()
12478
+ sessionId: string().optional(),
12479
+ /**
12480
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12481
+ * the batch to the Python pool's bench preprocess cache
12482
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12483
+ * preprocessed ONCE and every later inference is a pure-inference cache
12484
+ * hit — the sustained-throughput run measures inference, not
12485
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12486
+ * full preprocess every call, correct). Fresh per sustained run;
12487
+ * released via `uncacheFrame`.
12488
+ */
12489
+ frameId: number().int().nonnegative().optional(),
12490
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12491
+ deviceKey: string().optional()
12464
12492
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12465
12493
  data: _instanceof(Uint8Array),
12466
12494
  width: number().int().positive(),
@@ -12492,8 +12520,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12492
12520
  * - `runtime` — main camera-serving engine (no idle TTL).
12493
12521
  * - `warm-override` — benchmark/test override held in the warm
12494
12522
  * cache; auto-disposed after the idle TTL.
12523
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12524
+ * multi-device, keyed by `deviceKey`) resolved
12525
+ * via `resolveDeviceFactory`. Runs alongside the
12526
+ * `runtime` engine on a DIFFERENT accelerator
12527
+ * (NPU / iGPU / Coral) — this is how the
12528
+ * Engines tab shows all pools running at once.
12495
12529
  */
12496
- kind: _enum(["runtime", "warm-override"]),
12530
+ kind: _enum([
12531
+ "runtime",
12532
+ "warm-override",
12533
+ "device-pool"
12534
+ ]),
12497
12535
  /** Native pid of the underlying Python pool (null when no pool). */
12498
12536
  poolPid: number().nullable(),
12499
12537
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12628,7 +12666,21 @@ var NativeCropResultSchema = object({
12628
12666
  /** Packed rgb (24-bit) pixels of the crop. */
12629
12667
  bytes: _instanceof(Uint8Array),
12630
12668
  width: number().int().positive(),
12631
- height: number().int().positive()
12669
+ height: number().int().positive(),
12670
+ /**
12671
+ * Which source served this crop, so a quality-sensitive consumer (the native
12672
+ * `keyFrame`) can reject a degraded fallback:
12673
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12674
+ * quality path).
12675
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12676
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12677
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12678
+ *
12679
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12680
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12681
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12682
+ */
12683
+ tier: _enum(["native", "ram-fullframe"]).optional()
12632
12684
  });
12633
12685
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12634
12686
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12868,7 +12920,14 @@ var RunnerCameraConfigSchema = object({
12868
12920
  * camera's detect node differs from its source-owner (P2d, gated by the
12869
12921
  * `remoteSourcingNodes` rollout setting).
12870
12922
  */
12871
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12923
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12924
+ /**
12925
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12926
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12927
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12928
+ * this only selects WHICH device pool of that node runs the session.
12929
+ */
12930
+ deviceKey: string().optional()
12872
12931
  });
12873
12932
  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;
12874
12933
  /**
@@ -12889,6 +12948,19 @@ var RunnerLocalLoadSchema = object({
12889
12948
  avgInferenceTimeMs: number(),
12890
12949
  /** Total queue depth across motion + detection queues. */
12891
12950
  queueDepthTotal: number(),
12951
+ /**
12952
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12953
+ * this runner currently has attached cameras on, so the orchestrator's second
12954
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12955
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12956
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12957
+ */
12958
+ devices: array(object({
12959
+ deviceKey: string(),
12960
+ backend: string(),
12961
+ attachedCameras: number(),
12962
+ queueDepthTotal: number()
12963
+ })).default([]),
12892
12964
  /** Hardware capability flags reported by this node. */
12893
12965
  hardware: object({
12894
12966
  hasGpu: boolean(),
@@ -16037,6 +16109,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16037
16109
  return toDeviceSummary(device, this.addonId);
16038
16110
  }
16039
16111
  };
16112
+ 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;
16113
+ new Set(Object.values(DeviceType));
16040
16114
  /**
16041
16115
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16042
16116
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17627,7 +17701,8 @@ var LinkedDeviceSchema = object({
17627
17701
  deviceId: number(),
17628
17702
  name: string(),
17629
17703
  location: string().nullable(),
17630
- features: array(string())
17704
+ features: array(string()),
17705
+ producesTrackedEvents: boolean().optional()
17631
17706
  });
17632
17707
  var SavedDeviceRowSchema = object({
17633
17708
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19257,6 +19332,7 @@ var TrackSchema = object({
19257
19332
  deviceId: number(),
19258
19333
  className: string(),
19259
19334
  label: string().optional(),
19335
+ producingDeviceName: string().optional(),
19260
19336
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19261
19337
  source: TrackSourceSchema.optional(),
19262
19338
  firstSeen: number(),
@@ -19394,7 +19470,8 @@ var MediaFileKindEnum = _enum([
19394
19470
  "fullFrameBoxed",
19395
19471
  "faceCrop",
19396
19472
  "plateCrop",
19397
- "keyFrame"
19473
+ "keyFrame",
19474
+ "keyFrameSmall"
19398
19475
  ]);
19399
19476
  var MediaFileSchema = object({
19400
19477
  key: string(),
@@ -19723,13 +19800,11 @@ var PipelineTemplateSchema = object({
19723
19800
  createdAt: string(),
19724
19801
  updatedAt: string()
19725
19802
  });
19726
- var AgentAddonConfigSchema = object({
19727
- enabled: boolean(),
19803
+ var DeviceStepConfigSchema = object({
19728
19804
  modelId: string().optional(),
19729
- settings: record(string(), unknown()).readonly()
19805
+ settings: record(string(), unknown()).optional()
19730
19806
  });
19731
19807
  var AgentPipelineSettingsSchema = object({
19732
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19733
19808
  maxCameras: number().int().nonnegative().nullable().default(null),
19734
19809
  /** Per-node detection weight (relative share for the quota balancer). */
19735
19810
  detectWeight: number().positive().optional(),
@@ -19753,7 +19828,22 @@ var AgentPipelineSettingsSchema = object({
19753
19828
  * it already uses to reach the hub). Set this only when the auto-detected
19754
19829
  * address is wrong (multi-homed host, NAT, custom interface).
19755
19830
  */
19756
- reachableHost: string().optional()
19831
+ reachableHost: string().optional(),
19832
+ /**
19833
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19834
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19835
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19836
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19837
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19838
+ * the default model/settings for every camera landing on that accelerator;
19839
+ * a stepId absent ⇒ the step uses that device's format default.
19840
+ */
19841
+ inferenceDevices: record(string(), object({
19842
+ enabled: boolean(),
19843
+ weight: number().positive().optional(),
19844
+ maxSessions: number().int().positive().optional(),
19845
+ steps: record(string(), DeviceStepConfigSchema).optional()
19846
+ })).optional()
19757
19847
  });
19758
19848
  var CameraPipelineForAgentSchema = object({
19759
19849
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19763,14 +19853,13 @@ var CameraPipelineForAgentSchema = object({
19763
19853
  }).nullable()
19764
19854
  });
19765
19855
  var CameraStepOverridePatchSchema = object({
19766
- enabled: boolean().optional(),
19767
19856
  modelId: string().optional(),
19768
19857
  settings: record(string(), unknown()).readonly().optional()
19769
19858
  });
19770
19859
  var CameraPipelineSettingsSchema = object({
19771
19860
  pinnedAgentNodeId: string().optional(),
19772
19861
  stepToggles: record(string(), boolean()).optional(),
19773
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19862
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19774
19863
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19775
19864
  });
19776
19865
  /**
@@ -19984,6 +20073,44 @@ var CameraStatusSchema = object({
19984
20073
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19985
20074
  fetchedAt: number()
19986
20075
  });
20076
+ var NodeInferenceDeviceSchema = object({
20077
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20078
+ key: string(),
20079
+ backend: string(),
20080
+ device: string(),
20081
+ format: _enum(MODEL_FORMATS),
20082
+ /** Whether the node's live probe reports the device as usable right now. */
20083
+ available: boolean(),
20084
+ /**
20085
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20086
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20087
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20088
+ * not a balanced target). An explicit stored value always wins; a stored-only
20089
+ * (unavailable) key keeps its stored value.
20090
+ */
20091
+ enabled: boolean(),
20092
+ /** Relative balancer weight for the enabled device (default 1). */
20093
+ weight: number(),
20094
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20095
+ maxSessions: number().nullable(),
20096
+ /** Object-detection model the executor defaults to for this deviceKey. */
20097
+ defaultModelId: string(),
20098
+ /**
20099
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20100
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20101
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20102
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20103
+ * available per format; this is the stored selection that becomes the
20104
+ * default for EVERY camera landing on this accelerator.
20105
+ */
20106
+ steps: record(string(), DeviceStepConfigSchema).optional()
20107
+ });
20108
+ var NodeInferenceDevicesSchema = object({
20109
+ nodeId: string(),
20110
+ /** False when the node's platform-probe was unreachable (no live device set). */
20111
+ reachable: boolean(),
20112
+ devices: array(NodeInferenceDeviceSchema).readonly()
20113
+ });
19987
20114
  method(object({
19988
20115
  deviceId: number(),
19989
20116
  agentNodeId: string()
@@ -19993,7 +20120,13 @@ method(object({
19993
20120
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19994
20121
  kind: "mutation",
19995
20122
  auth: "admin"
19996
- }), method(_void(), object({ migrated: number() }), {
20123
+ }), method(object({
20124
+ deviceId: number(),
20125
+ deviceKey: string()
20126
+ }), object({ success: literal(true) }), {
20127
+ kind: "mutation",
20128
+ auth: "admin"
20129
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19997
20130
  kind: "mutation",
19998
20131
  auth: "admin"
19999
20132
  }), 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({
@@ -20027,13 +20160,7 @@ method(object({
20027
20160
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20028
20161
  nodeId: string(),
20029
20162
  settings: AgentPipelineSettingsSchema
20030
- })).readonly()), method(object({
20031
- agentNodeId: string(),
20032
- defaults: record(string(), AgentAddonConfigSchema)
20033
- }), object({ success: literal(true) }), {
20034
- kind: "mutation",
20035
- auth: "admin"
20036
- }), method(object({ agentNodeId: string() }), object({
20163
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20037
20164
  success: boolean(),
20038
20165
  removed: boolean()
20039
20166
  }), {
@@ -20065,7 +20192,18 @@ method(object({
20065
20192
  }), object({ success: literal(true) }), {
20066
20193
  kind: "mutation",
20067
20194
  auth: "admin"
20068
- }), method(object({ agentNodeId: string() }), object({
20195
+ }), method(object({
20196
+ agentNodeId: string(),
20197
+ inferenceDevices: record(string(), object({
20198
+ enabled: boolean(),
20199
+ weight: number().positive().optional(),
20200
+ maxSessions: number().int().positive().optional(),
20201
+ steps: record(string(), DeviceStepConfigSchema).optional()
20202
+ }))
20203
+ }), object({ success: literal(true) }), {
20204
+ kind: "mutation",
20205
+ auth: "admin"
20206
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20069
20207
  success: literal(true),
20070
20208
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20071
20209
  effectiveModelId: string().nullable(),
@@ -20081,9 +20219,10 @@ method(object({
20081
20219
  }), object({ success: literal(true) }), {
20082
20220
  kind: "mutation",
20083
20221
  auth: "admin"
20084
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20222
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20085
20223
  deviceId: number(),
20086
20224
  agentNodeId: string(),
20225
+ deviceKey: string(),
20087
20226
  addonId: string(),
20088
20227
  patch: CameraStepOverridePatchSchema.nullable()
20089
20228
  }), object({ success: literal(true) }), {
@@ -20120,14 +20259,13 @@ method(object({
20120
20259
  });
20121
20260
  /**
20122
20261
  * server-management — per-NODE singleton capability for a node's ROOT
20123
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20124
- * agents).
20262
+ * package lifecycle (runtime-updatable node packages).
20125
20263
  *
20126
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20127
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20128
- * version describes the node. Updates install into
20129
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20130
- * starter (probation boot + auto-rollback to N-1).
20264
+ * Every node role runs the SAME root package (`@camstack/server`), which
20265
+ * carries the whole software stack in its npm dep tree, so ONE version
20266
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20267
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20268
+ * no auto-rollback).
20131
20269
  *
20132
20270
  * Providers:
20133
20271
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20235,7 +20373,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20235
20373
  /** Explicit target version; omitted = latest from the registry. */
20236
20374
  version: string().optional() }), ServerUpdateActionResultSchema, {
20237
20375
  kind: "mutation",
20238
- auth: "admin"
20376
+ auth: "admin",
20377
+ timeoutMs: 16 * 6e4
20239
20378
  }), method(_void(), ServerUpdateActionResultSchema, {
20240
20379
  kind: "mutation",
20241
20380
  auth: "admin"
@@ -21279,22 +21418,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21279
21418
  var RestartAddonResultSchema = unknown();
21280
21419
  var InstallPackageResultSchema = unknown();
21281
21420
  var ReloadPackagesResultSchema = unknown();
21282
- /**
21283
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21284
- * server restarts so the admin UI can react to the `restartingAt`
21285
- * timestamp (shows reconnect overlay). The transition from
21286
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21287
- * `system.restart-completed` event after the new process boots.
21288
- *
21289
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21290
- */
21291
- var UpdateFrameworkPackageResultSchema = object({
21292
- packageName: string(),
21293
- fromVersion: string(),
21294
- toVersion: string(),
21295
- /** Ms-epoch the server scheduled its self-restart. */
21296
- restartingAt: number()
21297
- });
21298
21421
  var BulkUpdateItemStatusSchema = _enum([
21299
21422
  "queued",
21300
21423
  "updating",
@@ -21422,13 +21545,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21422
21545
  }), object({ success: literal(true) }), {
21423
21546
  kind: "mutation",
21424
21547
  auth: "admin"
21425
- }), method(object({
21426
- packageName: string().min(1),
21427
- version: string().optional(),
21428
- deferRestart: boolean().optional()
21429
- }), UpdateFrameworkPackageResultSchema, {
21430
- kind: "mutation",
21431
- auth: "admin"
21432
21548
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21433
21549
  kind: "mutation",
21434
21550
  auth: "admin"
@@ -22294,10 +22410,10 @@ var TopologyCategorySchema = object({
22294
22410
  addons: array(TopologyCategoryAddonSchema).readonly()
22295
22411
  });
22296
22412
  /**
22297
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22298
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22299
- * version visibility for the Server management surface. Nullable: offline
22300
- * rows and pre-phase-2 nodes report none.
22413
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22414
+ * root package for every node role) as reported by its `registerNode`
22415
+ * manifest — version visibility for the Server management surface. Nullable:
22416
+ * offline rows and nodes that never reported one.
22301
22417
  */
22302
22418
  var TopologyRootPackageSchema = object({
22303
22419
  name: string(),
@@ -22685,17 +22801,28 @@ var PlatformScoreSchema = object({
22685
22801
  format: _enum([
22686
22802
  "onnx",
22687
22803
  "coreml",
22688
- "openvino"
22804
+ "openvino",
22805
+ "tflite"
22689
22806
  ]),
22690
22807
  score: number(),
22691
22808
  reason: string(),
22692
22809
  available: boolean()
22693
22810
  });
22811
+ var InferenceDeviceDescriptorSchema = object({
22812
+ key: string(),
22813
+ backend: string(),
22814
+ device: string(),
22815
+ format: ModelFormatSchema,
22816
+ runtime: literal("python"),
22817
+ score: number(),
22818
+ available: boolean()
22819
+ });
22694
22820
  var PlatformCapabilitiesSchema = object({
22695
22821
  hardware: HardwareInfoSchema,
22696
22822
  scores: array(PlatformScoreSchema).readonly(),
22697
22823
  bestScore: PlatformScoreSchema,
22698
- pythonPath: string().nullable()
22824
+ pythonPath: string().nullable(),
22825
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22699
22826
  });
22700
22827
  var ModelRequirementSchema = object({
22701
22828
  modelId: string(),
@@ -23574,12 +23701,6 @@ Object.freeze({
23574
23701
  addonId: null,
23575
23702
  access: "delete"
23576
23703
  },
23577
- "addons.updateFrameworkPackage": {
23578
- capName: "addons",
23579
- capScope: "system",
23580
- addonId: null,
23581
- access: "create"
23582
- },
23583
23704
  "addons.updatePackage": {
23584
23705
  capName: "addons",
23585
23706
  capScope: "system",
@@ -26352,12 +26473,6 @@ Object.freeze({
26352
26473
  addonId: null,
26353
26474
  access: "view"
26354
26475
  },
26355
- "pipelineExecutor.reprobeEngine": {
26356
- capName: "pipeline-executor",
26357
- capScope: "system",
26358
- addonId: null,
26359
- access: "create"
26360
- },
26361
26476
  "pipelineExecutor.runAudioTest": {
26362
26477
  capName: "pipeline-executor",
26363
26478
  capScope: "system",
@@ -26508,6 +26623,12 @@ Object.freeze({
26508
26623
  addonId: null,
26509
26624
  access: "view"
26510
26625
  },
26626
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26627
+ capName: "pipeline-orchestrator",
26628
+ capScope: "system",
26629
+ addonId: null,
26630
+ access: "view"
26631
+ },
26511
26632
  "pipelineOrchestrator.getPipelineAssignment": {
26512
26633
  capName: "pipeline-orchestrator",
26513
26634
  capScope: "system",
@@ -26520,6 +26641,12 @@ Object.freeze({
26520
26641
  addonId: null,
26521
26642
  access: "view"
26522
26643
  },
26644
+ "pipelineOrchestrator.getPipelineDevicePin": {
26645
+ capName: "pipeline-orchestrator",
26646
+ capScope: "system",
26647
+ addonId: null,
26648
+ access: "view"
26649
+ },
26523
26650
  "pipelineOrchestrator.listAgentSettings": {
26524
26651
  capName: "pipeline-orchestrator",
26525
26652
  capScope: "system",
@@ -26562,19 +26689,19 @@ Object.freeze({
26562
26689
  addonId: null,
26563
26690
  access: "create"
26564
26691
  },
26565
- "pipelineOrchestrator.setAgentAddonDefaults": {
26692
+ "pipelineOrchestrator.setAgentCapabilities": {
26566
26693
  capName: "pipeline-orchestrator",
26567
26694
  capScope: "system",
26568
26695
  addonId: null,
26569
26696
  access: "create"
26570
26697
  },
26571
- "pipelineOrchestrator.setAgentCapabilities": {
26698
+ "pipelineOrchestrator.setAgentDetectWeight": {
26572
26699
  capName: "pipeline-orchestrator",
26573
26700
  capScope: "system",
26574
26701
  addonId: null,
26575
26702
  access: "create"
26576
26703
  },
26577
- "pipelineOrchestrator.setAgentDetectWeight": {
26704
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26578
26705
  capName: "pipeline-orchestrator",
26579
26706
  capScope: "system",
26580
26707
  addonId: null,
@@ -26616,6 +26743,12 @@ Object.freeze({
26616
26743
  addonId: null,
26617
26744
  access: "create"
26618
26745
  },
26746
+ "pipelineOrchestrator.setPipelineDevicePin": {
26747
+ capName: "pipeline-orchestrator",
26748
+ capScope: "system",
26749
+ addonId: null,
26750
+ access: "create"
26751
+ },
26619
26752
  "pipelineOrchestrator.unassignAudio": {
26620
26753
  capName: "pipeline-orchestrator",
26621
26754
  capScope: "system",
@@ -28168,32 +28301,6 @@ Object.freeze({
28168
28301
  "network-access": "ingress",
28169
28302
  "smtp-provider": "email"
28170
28303
  });
28171
- var frameworkSwapPackageSchema = object({
28172
- name: string(),
28173
- stagedPath: string(),
28174
- backupPath: string(),
28175
- toVersion: string(),
28176
- fromVersion: string().nullable()
28177
- });
28178
- object({
28179
- jobId: string(),
28180
- taskId: string(),
28181
- packages: array(frameworkSwapPackageSchema),
28182
- requestedAtMs: number(),
28183
- schemaVersion: literal(1)
28184
- });
28185
- object({
28186
- jobId: string(),
28187
- taskId: string(),
28188
- backups: array(object({
28189
- name: string(),
28190
- backupPath: string(),
28191
- livePath: string()
28192
- })),
28193
- appliedAtMs: number(),
28194
- bootAttempts: number(),
28195
- schemaVersion: literal(1)
28196
- });
28197
28304
  //#endregion
28198
28305
  //#region src/config.ts
28199
28306
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-tuya",
3
- "version": "0.1.14",
3
+ "version": "0.2.1",
4
4
  "description": "Tuya / Smart Life device-provider addon for CamStack — account-onboarded (Tuya IoT cloud fetch of device localKeys) + LOCAL DP control via the @apocaliss92/nodetuya encrypted-LAN client, exposing switch / water-heater-family kettle entities",
5
5
  "keywords": [
6
6
  "camstack",