@camstack/addon-provider-tuya 0.1.14 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +193 -103
  2. package/dist/addon.mjs +193 -103
  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). */
@@ -12869,7 +12907,14 @@ var RunnerCameraConfigSchema = object({
12869
12907
  * camera's detect node differs from its source-owner (P2d, gated by the
12870
12908
  * `remoteSourcingNodes` rollout setting).
12871
12909
  */
12872
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12910
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12911
+ /**
12912
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12913
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12914
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12915
+ * this only selects WHICH device pool of that node runs the session.
12916
+ */
12917
+ deviceKey: string().optional()
12873
12918
  });
12874
12919
  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
12920
  /**
@@ -12890,6 +12935,19 @@ var RunnerLocalLoadSchema = object({
12890
12935
  avgInferenceTimeMs: number(),
12891
12936
  /** Total queue depth across motion + detection queues. */
12892
12937
  queueDepthTotal: number(),
12938
+ /**
12939
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12940
+ * this runner currently has attached cameras on, so the orchestrator's second
12941
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12942
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12943
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12944
+ */
12945
+ devices: array(object({
12946
+ deviceKey: string(),
12947
+ backend: string(),
12948
+ attachedCameras: number(),
12949
+ queueDepthTotal: number()
12950
+ })).default([]),
12893
12951
  /** Hardware capability flags reported by this node. */
12894
12952
  hardware: object({
12895
12953
  hasGpu: boolean(),
@@ -16038,6 +16096,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16038
16096
  return toDeviceSummary(device, this.addonId);
16039
16097
  }
16040
16098
  };
16099
+ 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;
16100
+ new Set(Object.values(DeviceType));
16041
16101
  /**
16042
16102
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16043
16103
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19724,13 +19784,11 @@ var PipelineTemplateSchema = object({
19724
19784
  createdAt: string(),
19725
19785
  updatedAt: string()
19726
19786
  });
19727
- var AgentAddonConfigSchema = object({
19728
- enabled: boolean(),
19787
+ var DeviceStepConfigSchema = object({
19729
19788
  modelId: string().optional(),
19730
- settings: record(string(), unknown()).readonly()
19789
+ settings: record(string(), unknown()).optional()
19731
19790
  });
19732
19791
  var AgentPipelineSettingsSchema = object({
19733
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19734
19792
  maxCameras: number().int().nonnegative().nullable().default(null),
19735
19793
  /** Per-node detection weight (relative share for the quota balancer). */
19736
19794
  detectWeight: number().positive().optional(),
@@ -19754,7 +19812,22 @@ var AgentPipelineSettingsSchema = object({
19754
19812
  * it already uses to reach the hub). Set this only when the auto-detected
19755
19813
  * address is wrong (multi-homed host, NAT, custom interface).
19756
19814
  */
19757
- reachableHost: string().optional()
19815
+ reachableHost: string().optional(),
19816
+ /**
19817
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19818
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19819
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19820
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19821
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19822
+ * the default model/settings for every camera landing on that accelerator;
19823
+ * a stepId absent ⇒ the step uses that device's format default.
19824
+ */
19825
+ inferenceDevices: record(string(), object({
19826
+ enabled: boolean(),
19827
+ weight: number().positive().optional(),
19828
+ maxSessions: number().int().positive().optional(),
19829
+ steps: record(string(), DeviceStepConfigSchema).optional()
19830
+ })).optional()
19758
19831
  });
19759
19832
  var CameraPipelineForAgentSchema = object({
19760
19833
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19764,14 +19837,13 @@ var CameraPipelineForAgentSchema = object({
19764
19837
  }).nullable()
19765
19838
  });
19766
19839
  var CameraStepOverridePatchSchema = object({
19767
- enabled: boolean().optional(),
19768
19840
  modelId: string().optional(),
19769
19841
  settings: record(string(), unknown()).readonly().optional()
19770
19842
  });
19771
19843
  var CameraPipelineSettingsSchema = object({
19772
19844
  pinnedAgentNodeId: string().optional(),
19773
19845
  stepToggles: record(string(), boolean()).optional(),
19774
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19846
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19775
19847
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19776
19848
  });
19777
19849
  /**
@@ -19985,6 +20057,44 @@ var CameraStatusSchema = object({
19985
20057
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19986
20058
  fetchedAt: number()
19987
20059
  });
20060
+ var NodeInferenceDeviceSchema = object({
20061
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20062
+ key: string(),
20063
+ backend: string(),
20064
+ device: string(),
20065
+ format: _enum(MODEL_FORMATS),
20066
+ /** Whether the node's live probe reports the device as usable right now. */
20067
+ available: boolean(),
20068
+ /**
20069
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20070
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20071
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20072
+ * not a balanced target). An explicit stored value always wins; a stored-only
20073
+ * (unavailable) key keeps its stored value.
20074
+ */
20075
+ enabled: boolean(),
20076
+ /** Relative balancer weight for the enabled device (default 1). */
20077
+ weight: number(),
20078
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20079
+ maxSessions: number().nullable(),
20080
+ /** Object-detection model the executor defaults to for this deviceKey. */
20081
+ defaultModelId: string(),
20082
+ /**
20083
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20084
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20085
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20086
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20087
+ * available per format; this is the stored selection that becomes the
20088
+ * default for EVERY camera landing on this accelerator.
20089
+ */
20090
+ steps: record(string(), DeviceStepConfigSchema).optional()
20091
+ });
20092
+ var NodeInferenceDevicesSchema = object({
20093
+ nodeId: string(),
20094
+ /** False when the node's platform-probe was unreachable (no live device set). */
20095
+ reachable: boolean(),
20096
+ devices: array(NodeInferenceDeviceSchema).readonly()
20097
+ });
19988
20098
  method(object({
19989
20099
  deviceId: number(),
19990
20100
  agentNodeId: string()
@@ -19994,7 +20104,13 @@ method(object({
19994
20104
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19995
20105
  kind: "mutation",
19996
20106
  auth: "admin"
19997
- }), method(_void(), object({ migrated: number() }), {
20107
+ }), method(object({
20108
+ deviceId: number(),
20109
+ deviceKey: string()
20110
+ }), object({ success: literal(true) }), {
20111
+ kind: "mutation",
20112
+ auth: "admin"
20113
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19998
20114
  kind: "mutation",
19999
20115
  auth: "admin"
20000
20116
  }), 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 +20144,7 @@ method(object({
20028
20144
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20029
20145
  nodeId: string(),
20030
20146
  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({
20147
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20038
20148
  success: boolean(),
20039
20149
  removed: boolean()
20040
20150
  }), {
@@ -20066,7 +20176,18 @@ method(object({
20066
20176
  }), object({ success: literal(true) }), {
20067
20177
  kind: "mutation",
20068
20178
  auth: "admin"
20069
- }), method(object({ agentNodeId: string() }), object({
20179
+ }), method(object({
20180
+ agentNodeId: string(),
20181
+ inferenceDevices: record(string(), object({
20182
+ enabled: boolean(),
20183
+ weight: number().positive().optional(),
20184
+ maxSessions: number().int().positive().optional(),
20185
+ steps: record(string(), DeviceStepConfigSchema).optional()
20186
+ }))
20187
+ }), object({ success: literal(true) }), {
20188
+ kind: "mutation",
20189
+ auth: "admin"
20190
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20070
20191
  success: literal(true),
20071
20192
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20072
20193
  effectiveModelId: string().nullable(),
@@ -20082,9 +20203,10 @@ method(object({
20082
20203
  }), object({ success: literal(true) }), {
20083
20204
  kind: "mutation",
20084
20205
  auth: "admin"
20085
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20206
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20086
20207
  deviceId: number(),
20087
20208
  agentNodeId: string(),
20209
+ deviceKey: string(),
20088
20210
  addonId: string(),
20089
20211
  patch: CameraStepOverridePatchSchema.nullable()
20090
20212
  }), object({ success: literal(true) }), {
@@ -20121,14 +20243,13 @@ method(object({
20121
20243
  });
20122
20244
  /**
20123
20245
  * server-management — per-NODE singleton capability for a node's ROOT
20124
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20125
- * agents).
20246
+ * package lifecycle (runtime-updatable node packages).
20126
20247
  *
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).
20248
+ * Every node role runs the SAME root package (`@camstack/server`), which
20249
+ * carries the whole software stack in its npm dep tree, so ONE version
20250
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20251
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20252
+ * no auto-rollback).
20132
20253
  *
20133
20254
  * Providers:
20134
20255
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20236,7 +20357,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20236
20357
  /** Explicit target version; omitted = latest from the registry. */
20237
20358
  version: string().optional() }), ServerUpdateActionResultSchema, {
20238
20359
  kind: "mutation",
20239
- auth: "admin"
20360
+ auth: "admin",
20361
+ timeoutMs: 16 * 6e4
20240
20362
  }), method(_void(), ServerUpdateActionResultSchema, {
20241
20363
  kind: "mutation",
20242
20364
  auth: "admin"
@@ -21280,22 +21402,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21280
21402
  var RestartAddonResultSchema = unknown();
21281
21403
  var InstallPackageResultSchema = unknown();
21282
21404
  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
21405
  var BulkUpdateItemStatusSchema = _enum([
21300
21406
  "queued",
21301
21407
  "updating",
@@ -21423,13 +21529,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21423
21529
  }), object({ success: literal(true) }), {
21424
21530
  kind: "mutation",
21425
21531
  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
21532
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21434
21533
  kind: "mutation",
21435
21534
  auth: "admin"
@@ -22295,10 +22394,10 @@ var TopologyCategorySchema = object({
22295
22394
  addons: array(TopologyCategoryAddonSchema).readonly()
22296
22395
  });
22297
22396
  /**
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.
22397
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22398
+ * root package for every node role) as reported by its `registerNode`
22399
+ * manifest — version visibility for the Server management surface. Nullable:
22400
+ * offline rows and nodes that never reported one.
22302
22401
  */
22303
22402
  var TopologyRootPackageSchema = object({
22304
22403
  name: string(),
@@ -22686,17 +22785,28 @@ var PlatformScoreSchema = object({
22686
22785
  format: _enum([
22687
22786
  "onnx",
22688
22787
  "coreml",
22689
- "openvino"
22788
+ "openvino",
22789
+ "tflite"
22690
22790
  ]),
22691
22791
  score: number(),
22692
22792
  reason: string(),
22693
22793
  available: boolean()
22694
22794
  });
22795
+ var InferenceDeviceDescriptorSchema = object({
22796
+ key: string(),
22797
+ backend: string(),
22798
+ device: string(),
22799
+ format: ModelFormatSchema,
22800
+ runtime: literal("python"),
22801
+ score: number(),
22802
+ available: boolean()
22803
+ });
22695
22804
  var PlatformCapabilitiesSchema = object({
22696
22805
  hardware: HardwareInfoSchema,
22697
22806
  scores: array(PlatformScoreSchema).readonly(),
22698
22807
  bestScore: PlatformScoreSchema,
22699
- pythonPath: string().nullable()
22808
+ pythonPath: string().nullable(),
22809
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22700
22810
  });
22701
22811
  var ModelRequirementSchema = object({
22702
22812
  modelId: string(),
@@ -23575,12 +23685,6 @@ Object.freeze({
23575
23685
  addonId: null,
23576
23686
  access: "delete"
23577
23687
  },
23578
- "addons.updateFrameworkPackage": {
23579
- capName: "addons",
23580
- capScope: "system",
23581
- addonId: null,
23582
- access: "create"
23583
- },
23584
23688
  "addons.updatePackage": {
23585
23689
  capName: "addons",
23586
23690
  capScope: "system",
@@ -26353,12 +26457,6 @@ Object.freeze({
26353
26457
  addonId: null,
26354
26458
  access: "view"
26355
26459
  },
26356
- "pipelineExecutor.reprobeEngine": {
26357
- capName: "pipeline-executor",
26358
- capScope: "system",
26359
- addonId: null,
26360
- access: "create"
26361
- },
26362
26460
  "pipelineExecutor.runAudioTest": {
26363
26461
  capName: "pipeline-executor",
26364
26462
  capScope: "system",
@@ -26509,6 +26607,12 @@ Object.freeze({
26509
26607
  addonId: null,
26510
26608
  access: "view"
26511
26609
  },
26610
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26611
+ capName: "pipeline-orchestrator",
26612
+ capScope: "system",
26613
+ addonId: null,
26614
+ access: "view"
26615
+ },
26512
26616
  "pipelineOrchestrator.getPipelineAssignment": {
26513
26617
  capName: "pipeline-orchestrator",
26514
26618
  capScope: "system",
@@ -26521,6 +26625,12 @@ Object.freeze({
26521
26625
  addonId: null,
26522
26626
  access: "view"
26523
26627
  },
26628
+ "pipelineOrchestrator.getPipelineDevicePin": {
26629
+ capName: "pipeline-orchestrator",
26630
+ capScope: "system",
26631
+ addonId: null,
26632
+ access: "view"
26633
+ },
26524
26634
  "pipelineOrchestrator.listAgentSettings": {
26525
26635
  capName: "pipeline-orchestrator",
26526
26636
  capScope: "system",
@@ -26563,19 +26673,19 @@ Object.freeze({
26563
26673
  addonId: null,
26564
26674
  access: "create"
26565
26675
  },
26566
- "pipelineOrchestrator.setAgentAddonDefaults": {
26676
+ "pipelineOrchestrator.setAgentCapabilities": {
26567
26677
  capName: "pipeline-orchestrator",
26568
26678
  capScope: "system",
26569
26679
  addonId: null,
26570
26680
  access: "create"
26571
26681
  },
26572
- "pipelineOrchestrator.setAgentCapabilities": {
26682
+ "pipelineOrchestrator.setAgentDetectWeight": {
26573
26683
  capName: "pipeline-orchestrator",
26574
26684
  capScope: "system",
26575
26685
  addonId: null,
26576
26686
  access: "create"
26577
26687
  },
26578
- "pipelineOrchestrator.setAgentDetectWeight": {
26688
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26579
26689
  capName: "pipeline-orchestrator",
26580
26690
  capScope: "system",
26581
26691
  addonId: null,
@@ -26617,6 +26727,12 @@ Object.freeze({
26617
26727
  addonId: null,
26618
26728
  access: "create"
26619
26729
  },
26730
+ "pipelineOrchestrator.setPipelineDevicePin": {
26731
+ capName: "pipeline-orchestrator",
26732
+ capScope: "system",
26733
+ addonId: null,
26734
+ access: "create"
26735
+ },
26620
26736
  "pipelineOrchestrator.unassignAudio": {
26621
26737
  capName: "pipeline-orchestrator",
26622
26738
  capScope: "system",
@@ -28169,32 +28285,6 @@ Object.freeze({
28169
28285
  "network-access": "ingress",
28170
28286
  "smtp-provider": "email"
28171
28287
  });
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
28288
  //#endregion
28199
28289
  //#region src/config.ts
28200
28290
  /**
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). */
@@ -12868,7 +12906,14 @@ var RunnerCameraConfigSchema = object({
12868
12906
  * camera's detect node differs from its source-owner (P2d, gated by the
12869
12907
  * `remoteSourcingNodes` rollout setting).
12870
12908
  */
12871
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12909
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12910
+ /**
12911
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12912
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12913
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12914
+ * this only selects WHICH device pool of that node runs the session.
12915
+ */
12916
+ deviceKey: string().optional()
12872
12917
  });
12873
12918
  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
12919
  /**
@@ -12889,6 +12934,19 @@ var RunnerLocalLoadSchema = object({
12889
12934
  avgInferenceTimeMs: number(),
12890
12935
  /** Total queue depth across motion + detection queues. */
12891
12936
  queueDepthTotal: number(),
12937
+ /**
12938
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12939
+ * this runner currently has attached cameras on, so the orchestrator's second
12940
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12941
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12942
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12943
+ */
12944
+ devices: array(object({
12945
+ deviceKey: string(),
12946
+ backend: string(),
12947
+ attachedCameras: number(),
12948
+ queueDepthTotal: number()
12949
+ })).default([]),
12892
12950
  /** Hardware capability flags reported by this node. */
12893
12951
  hardware: object({
12894
12952
  hasGpu: boolean(),
@@ -16037,6 +16095,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16037
16095
  return toDeviceSummary(device, this.addonId);
16038
16096
  }
16039
16097
  };
16098
+ 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;
16099
+ new Set(Object.values(DeviceType));
16040
16100
  /**
16041
16101
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16042
16102
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19723,13 +19783,11 @@ var PipelineTemplateSchema = object({
19723
19783
  createdAt: string(),
19724
19784
  updatedAt: string()
19725
19785
  });
19726
- var AgentAddonConfigSchema = object({
19727
- enabled: boolean(),
19786
+ var DeviceStepConfigSchema = object({
19728
19787
  modelId: string().optional(),
19729
- settings: record(string(), unknown()).readonly()
19788
+ settings: record(string(), unknown()).optional()
19730
19789
  });
19731
19790
  var AgentPipelineSettingsSchema = object({
19732
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19733
19791
  maxCameras: number().int().nonnegative().nullable().default(null),
19734
19792
  /** Per-node detection weight (relative share for the quota balancer). */
19735
19793
  detectWeight: number().positive().optional(),
@@ -19753,7 +19811,22 @@ var AgentPipelineSettingsSchema = object({
19753
19811
  * it already uses to reach the hub). Set this only when the auto-detected
19754
19812
  * address is wrong (multi-homed host, NAT, custom interface).
19755
19813
  */
19756
- reachableHost: string().optional()
19814
+ reachableHost: string().optional(),
19815
+ /**
19816
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19817
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19818
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19819
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19820
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19821
+ * the default model/settings for every camera landing on that accelerator;
19822
+ * a stepId absent ⇒ the step uses that device's format default.
19823
+ */
19824
+ inferenceDevices: record(string(), object({
19825
+ enabled: boolean(),
19826
+ weight: number().positive().optional(),
19827
+ maxSessions: number().int().positive().optional(),
19828
+ steps: record(string(), DeviceStepConfigSchema).optional()
19829
+ })).optional()
19757
19830
  });
19758
19831
  var CameraPipelineForAgentSchema = object({
19759
19832
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19763,14 +19836,13 @@ var CameraPipelineForAgentSchema = object({
19763
19836
  }).nullable()
19764
19837
  });
19765
19838
  var CameraStepOverridePatchSchema = object({
19766
- enabled: boolean().optional(),
19767
19839
  modelId: string().optional(),
19768
19840
  settings: record(string(), unknown()).readonly().optional()
19769
19841
  });
19770
19842
  var CameraPipelineSettingsSchema = object({
19771
19843
  pinnedAgentNodeId: string().optional(),
19772
19844
  stepToggles: record(string(), boolean()).optional(),
19773
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19845
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19774
19846
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19775
19847
  });
19776
19848
  /**
@@ -19984,6 +20056,44 @@ var CameraStatusSchema = object({
19984
20056
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19985
20057
  fetchedAt: number()
19986
20058
  });
20059
+ var NodeInferenceDeviceSchema = object({
20060
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20061
+ key: string(),
20062
+ backend: string(),
20063
+ device: string(),
20064
+ format: _enum(MODEL_FORMATS),
20065
+ /** Whether the node's live probe reports the device as usable right now. */
20066
+ available: boolean(),
20067
+ /**
20068
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20069
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20070
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20071
+ * not a balanced target). An explicit stored value always wins; a stored-only
20072
+ * (unavailable) key keeps its stored value.
20073
+ */
20074
+ enabled: boolean(),
20075
+ /** Relative balancer weight for the enabled device (default 1). */
20076
+ weight: number(),
20077
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20078
+ maxSessions: number().nullable(),
20079
+ /** Object-detection model the executor defaults to for this deviceKey. */
20080
+ defaultModelId: string(),
20081
+ /**
20082
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20083
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20084
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20085
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20086
+ * available per format; this is the stored selection that becomes the
20087
+ * default for EVERY camera landing on this accelerator.
20088
+ */
20089
+ steps: record(string(), DeviceStepConfigSchema).optional()
20090
+ });
20091
+ var NodeInferenceDevicesSchema = object({
20092
+ nodeId: string(),
20093
+ /** False when the node's platform-probe was unreachable (no live device set). */
20094
+ reachable: boolean(),
20095
+ devices: array(NodeInferenceDeviceSchema).readonly()
20096
+ });
19987
20097
  method(object({
19988
20098
  deviceId: number(),
19989
20099
  agentNodeId: string()
@@ -19993,7 +20103,13 @@ method(object({
19993
20103
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19994
20104
  kind: "mutation",
19995
20105
  auth: "admin"
19996
- }), method(_void(), object({ migrated: number() }), {
20106
+ }), method(object({
20107
+ deviceId: number(),
20108
+ deviceKey: string()
20109
+ }), object({ success: literal(true) }), {
20110
+ kind: "mutation",
20111
+ auth: "admin"
20112
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19997
20113
  kind: "mutation",
19998
20114
  auth: "admin"
19999
20115
  }), 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 +20143,7 @@ method(object({
20027
20143
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20028
20144
  nodeId: string(),
20029
20145
  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({
20146
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20037
20147
  success: boolean(),
20038
20148
  removed: boolean()
20039
20149
  }), {
@@ -20065,7 +20175,18 @@ method(object({
20065
20175
  }), object({ success: literal(true) }), {
20066
20176
  kind: "mutation",
20067
20177
  auth: "admin"
20068
- }), method(object({ agentNodeId: string() }), object({
20178
+ }), method(object({
20179
+ agentNodeId: string(),
20180
+ inferenceDevices: record(string(), object({
20181
+ enabled: boolean(),
20182
+ weight: number().positive().optional(),
20183
+ maxSessions: number().int().positive().optional(),
20184
+ steps: record(string(), DeviceStepConfigSchema).optional()
20185
+ }))
20186
+ }), object({ success: literal(true) }), {
20187
+ kind: "mutation",
20188
+ auth: "admin"
20189
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20069
20190
  success: literal(true),
20070
20191
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20071
20192
  effectiveModelId: string().nullable(),
@@ -20081,9 +20202,10 @@ method(object({
20081
20202
  }), object({ success: literal(true) }), {
20082
20203
  kind: "mutation",
20083
20204
  auth: "admin"
20084
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20205
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20085
20206
  deviceId: number(),
20086
20207
  agentNodeId: string(),
20208
+ deviceKey: string(),
20087
20209
  addonId: string(),
20088
20210
  patch: CameraStepOverridePatchSchema.nullable()
20089
20211
  }), object({ success: literal(true) }), {
@@ -20120,14 +20242,13 @@ method(object({
20120
20242
  });
20121
20243
  /**
20122
20244
  * server-management — per-NODE singleton capability for a node's ROOT
20123
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20124
- * agents).
20245
+ * package lifecycle (runtime-updatable node packages).
20125
20246
  *
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).
20247
+ * Every node role runs the SAME root package (`@camstack/server`), which
20248
+ * carries the whole software stack in its npm dep tree, so ONE version
20249
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20250
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20251
+ * no auto-rollback).
20131
20252
  *
20132
20253
  * Providers:
20133
20254
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20235,7 +20356,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20235
20356
  /** Explicit target version; omitted = latest from the registry. */
20236
20357
  version: string().optional() }), ServerUpdateActionResultSchema, {
20237
20358
  kind: "mutation",
20238
- auth: "admin"
20359
+ auth: "admin",
20360
+ timeoutMs: 16 * 6e4
20239
20361
  }), method(_void(), ServerUpdateActionResultSchema, {
20240
20362
  kind: "mutation",
20241
20363
  auth: "admin"
@@ -21279,22 +21401,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21279
21401
  var RestartAddonResultSchema = unknown();
21280
21402
  var InstallPackageResultSchema = unknown();
21281
21403
  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
21404
  var BulkUpdateItemStatusSchema = _enum([
21299
21405
  "queued",
21300
21406
  "updating",
@@ -21422,13 +21528,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21422
21528
  }), object({ success: literal(true) }), {
21423
21529
  kind: "mutation",
21424
21530
  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
21531
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21433
21532
  kind: "mutation",
21434
21533
  auth: "admin"
@@ -22294,10 +22393,10 @@ var TopologyCategorySchema = object({
22294
22393
  addons: array(TopologyCategoryAddonSchema).readonly()
22295
22394
  });
22296
22395
  /**
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.
22396
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22397
+ * root package for every node role) as reported by its `registerNode`
22398
+ * manifest — version visibility for the Server management surface. Nullable:
22399
+ * offline rows and nodes that never reported one.
22301
22400
  */
22302
22401
  var TopologyRootPackageSchema = object({
22303
22402
  name: string(),
@@ -22685,17 +22784,28 @@ var PlatformScoreSchema = object({
22685
22784
  format: _enum([
22686
22785
  "onnx",
22687
22786
  "coreml",
22688
- "openvino"
22787
+ "openvino",
22788
+ "tflite"
22689
22789
  ]),
22690
22790
  score: number(),
22691
22791
  reason: string(),
22692
22792
  available: boolean()
22693
22793
  });
22794
+ var InferenceDeviceDescriptorSchema = object({
22795
+ key: string(),
22796
+ backend: string(),
22797
+ device: string(),
22798
+ format: ModelFormatSchema,
22799
+ runtime: literal("python"),
22800
+ score: number(),
22801
+ available: boolean()
22802
+ });
22694
22803
  var PlatformCapabilitiesSchema = object({
22695
22804
  hardware: HardwareInfoSchema,
22696
22805
  scores: array(PlatformScoreSchema).readonly(),
22697
22806
  bestScore: PlatformScoreSchema,
22698
- pythonPath: string().nullable()
22807
+ pythonPath: string().nullable(),
22808
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22699
22809
  });
22700
22810
  var ModelRequirementSchema = object({
22701
22811
  modelId: string(),
@@ -23574,12 +23684,6 @@ Object.freeze({
23574
23684
  addonId: null,
23575
23685
  access: "delete"
23576
23686
  },
23577
- "addons.updateFrameworkPackage": {
23578
- capName: "addons",
23579
- capScope: "system",
23580
- addonId: null,
23581
- access: "create"
23582
- },
23583
23687
  "addons.updatePackage": {
23584
23688
  capName: "addons",
23585
23689
  capScope: "system",
@@ -26352,12 +26456,6 @@ Object.freeze({
26352
26456
  addonId: null,
26353
26457
  access: "view"
26354
26458
  },
26355
- "pipelineExecutor.reprobeEngine": {
26356
- capName: "pipeline-executor",
26357
- capScope: "system",
26358
- addonId: null,
26359
- access: "create"
26360
- },
26361
26459
  "pipelineExecutor.runAudioTest": {
26362
26460
  capName: "pipeline-executor",
26363
26461
  capScope: "system",
@@ -26508,6 +26606,12 @@ Object.freeze({
26508
26606
  addonId: null,
26509
26607
  access: "view"
26510
26608
  },
26609
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26610
+ capName: "pipeline-orchestrator",
26611
+ capScope: "system",
26612
+ addonId: null,
26613
+ access: "view"
26614
+ },
26511
26615
  "pipelineOrchestrator.getPipelineAssignment": {
26512
26616
  capName: "pipeline-orchestrator",
26513
26617
  capScope: "system",
@@ -26520,6 +26624,12 @@ Object.freeze({
26520
26624
  addonId: null,
26521
26625
  access: "view"
26522
26626
  },
26627
+ "pipelineOrchestrator.getPipelineDevicePin": {
26628
+ capName: "pipeline-orchestrator",
26629
+ capScope: "system",
26630
+ addonId: null,
26631
+ access: "view"
26632
+ },
26523
26633
  "pipelineOrchestrator.listAgentSettings": {
26524
26634
  capName: "pipeline-orchestrator",
26525
26635
  capScope: "system",
@@ -26562,19 +26672,19 @@ Object.freeze({
26562
26672
  addonId: null,
26563
26673
  access: "create"
26564
26674
  },
26565
- "pipelineOrchestrator.setAgentAddonDefaults": {
26675
+ "pipelineOrchestrator.setAgentCapabilities": {
26566
26676
  capName: "pipeline-orchestrator",
26567
26677
  capScope: "system",
26568
26678
  addonId: null,
26569
26679
  access: "create"
26570
26680
  },
26571
- "pipelineOrchestrator.setAgentCapabilities": {
26681
+ "pipelineOrchestrator.setAgentDetectWeight": {
26572
26682
  capName: "pipeline-orchestrator",
26573
26683
  capScope: "system",
26574
26684
  addonId: null,
26575
26685
  access: "create"
26576
26686
  },
26577
- "pipelineOrchestrator.setAgentDetectWeight": {
26687
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26578
26688
  capName: "pipeline-orchestrator",
26579
26689
  capScope: "system",
26580
26690
  addonId: null,
@@ -26616,6 +26726,12 @@ Object.freeze({
26616
26726
  addonId: null,
26617
26727
  access: "create"
26618
26728
  },
26729
+ "pipelineOrchestrator.setPipelineDevicePin": {
26730
+ capName: "pipeline-orchestrator",
26731
+ capScope: "system",
26732
+ addonId: null,
26733
+ access: "create"
26734
+ },
26619
26735
  "pipelineOrchestrator.unassignAudio": {
26620
26736
  capName: "pipeline-orchestrator",
26621
26737
  capScope: "system",
@@ -28168,32 +28284,6 @@ Object.freeze({
28168
28284
  "network-access": "ingress",
28169
28285
  "smtp-provider": "email"
28170
28286
  });
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
28287
  //#endregion
28198
28288
  //#region src/config.ts
28199
28289
  /**
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.0",
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",