@camstack/addon-provider-unifi 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
@@ -7098,6 +7098,17 @@ var ModelCatalogEntrySchema = object({
7098
7098
  "imagenet",
7099
7099
  "none"
7100
7100
  ]).optional(),
7101
+ /**
7102
+ * The model already applies softmax IN-GRAPH — its raw output is a
7103
+ * probability distribution, not logits. When set, the `softmax`
7104
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7105
+ * probability vector collapses it toward uniform (top-1 score craters far
7106
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7107
+ * the output is raw logits and the postprocessor applies softmax (the normal
7108
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7109
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7110
+ */
7111
+ outputProbabilities: boolean().optional(),
7101
7112
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7102
7113
  /**
7103
7114
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12388,10 +12399,7 @@ var ConfigUISchemaNullableBridge = custom();
12388
12399
  var InferenceCapabilitiesBridge = custom();
12389
12400
  var ModelAvailabilityListBridge = custom();
12390
12401
  var PipelineRunResultBridge = custom();
12391
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12392
- kind: "mutation",
12393
- auth: "admin"
12394
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12402
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12395
12403
  modelId: string(),
12396
12404
  settings: record(string(), unknown()).readonly()
12397
12405
  }))), method(object({ steps: record(string(), object({
@@ -12451,13 +12459,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12451
12459
  * (inputClasses ≠ null) are skipped and served per-track via
12452
12460
  * pipelineRunner.runDetailSubtree (two-plane design).
12453
12461
  */
12454
- plane: _enum(["full", "frame"]).optional()
12462
+ plane: _enum(["full", "frame"]).optional(),
12463
+ /**
12464
+ * Inference-device selector (Phase 2 multi-device). Format
12465
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12466
+ * Omitted ⇒ the runner's default device (current single-engine
12467
+ * behaviour). Selects WHICH device pool of the node runs the call.
12468
+ */
12469
+ deviceKey: string().optional()
12455
12470
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12456
12471
  engine: PipelineEngineChoiceSchema.optional(),
12457
12472
  steps: array(PipelineStepInputSchema).min(1),
12458
12473
  frames: array(FrameInputSchema).min(1).max(255),
12459
12474
  deviceId: number().optional(),
12460
- sessionId: string().optional()
12475
+ sessionId: string().optional(),
12476
+ /**
12477
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12478
+ * the batch to the Python pool's bench preprocess cache
12479
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12480
+ * preprocessed ONCE and every later inference is a pure-inference cache
12481
+ * hit — the sustained-throughput run measures inference, not
12482
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12483
+ * full preprocess every call, correct). Fresh per sustained run;
12484
+ * released via `uncacheFrame`.
12485
+ */
12486
+ frameId: number().int().nonnegative().optional(),
12487
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12488
+ deviceKey: string().optional()
12461
12489
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12462
12490
  data: _instanceof(Uint8Array),
12463
12491
  width: number().int().positive(),
@@ -12489,8 +12517,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12489
12517
  * - `runtime` — main camera-serving engine (no idle TTL).
12490
12518
  * - `warm-override` — benchmark/test override held in the warm
12491
12519
  * cache; auto-disposed after the idle TTL.
12520
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12521
+ * multi-device, keyed by `deviceKey`) resolved
12522
+ * via `resolveDeviceFactory`. Runs alongside the
12523
+ * `runtime` engine on a DIFFERENT accelerator
12524
+ * (NPU / iGPU / Coral) — this is how the
12525
+ * Engines tab shows all pools running at once.
12492
12526
  */
12493
- kind: _enum(["runtime", "warm-override"]),
12527
+ kind: _enum([
12528
+ "runtime",
12529
+ "warm-override",
12530
+ "device-pool"
12531
+ ]),
12494
12532
  /** Native pid of the underlying Python pool (null when no pool). */
12495
12533
  poolPid: number().nullable(),
12496
12534
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12865,7 +12903,14 @@ var RunnerCameraConfigSchema = object({
12865
12903
  * camera's detect node differs from its source-owner (P2d, gated by the
12866
12904
  * `remoteSourcingNodes` rollout setting).
12867
12905
  */
12868
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12906
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12907
+ /**
12908
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12909
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12910
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12911
+ * this only selects WHICH device pool of that node runs the session.
12912
+ */
12913
+ deviceKey: string().optional()
12869
12914
  });
12870
12915
  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;
12871
12916
  /**
@@ -12886,6 +12931,19 @@ var RunnerLocalLoadSchema = object({
12886
12931
  avgInferenceTimeMs: number(),
12887
12932
  /** Total queue depth across motion + detection queues. */
12888
12933
  queueDepthTotal: number(),
12934
+ /**
12935
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12936
+ * this runner currently has attached cameras on, so the orchestrator's second
12937
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12938
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12939
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12940
+ */
12941
+ devices: array(object({
12942
+ deviceKey: string(),
12943
+ backend: string(),
12944
+ attachedCameras: number(),
12945
+ queueDepthTotal: number()
12946
+ })).default([]),
12889
12947
  /** Hardware capability flags reported by this node. */
12890
12948
  hardware: object({
12891
12949
  hasGpu: boolean(),
@@ -16034,6 +16092,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16034
16092
  return toDeviceSummary(device, this.addonId);
16035
16093
  }
16036
16094
  };
16095
+ 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;
16096
+ new Set(Object.values(DeviceType));
16037
16097
  /**
16038
16098
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16039
16099
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19703,13 +19763,11 @@ var PipelineTemplateSchema = object({
19703
19763
  createdAt: string(),
19704
19764
  updatedAt: string()
19705
19765
  });
19706
- var AgentAddonConfigSchema = object({
19707
- enabled: boolean(),
19766
+ var DeviceStepConfigSchema = object({
19708
19767
  modelId: string().optional(),
19709
- settings: record(string(), unknown()).readonly()
19768
+ settings: record(string(), unknown()).optional()
19710
19769
  });
19711
19770
  var AgentPipelineSettingsSchema = object({
19712
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19713
19771
  maxCameras: number().int().nonnegative().nullable().default(null),
19714
19772
  /** Per-node detection weight (relative share for the quota balancer). */
19715
19773
  detectWeight: number().positive().optional(),
@@ -19733,7 +19791,22 @@ var AgentPipelineSettingsSchema = object({
19733
19791
  * it already uses to reach the hub). Set this only when the auto-detected
19734
19792
  * address is wrong (multi-homed host, NAT, custom interface).
19735
19793
  */
19736
- reachableHost: string().optional()
19794
+ reachableHost: string().optional(),
19795
+ /**
19796
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19797
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19798
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19799
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19800
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19801
+ * the default model/settings for every camera landing on that accelerator;
19802
+ * a stepId absent ⇒ the step uses that device's format default.
19803
+ */
19804
+ inferenceDevices: record(string(), object({
19805
+ enabled: boolean(),
19806
+ weight: number().positive().optional(),
19807
+ maxSessions: number().int().positive().optional(),
19808
+ steps: record(string(), DeviceStepConfigSchema).optional()
19809
+ })).optional()
19737
19810
  });
19738
19811
  var CameraPipelineForAgentSchema = object({
19739
19812
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19743,14 +19816,13 @@ var CameraPipelineForAgentSchema = object({
19743
19816
  }).nullable()
19744
19817
  });
19745
19818
  var CameraStepOverridePatchSchema = object({
19746
- enabled: boolean().optional(),
19747
19819
  modelId: string().optional(),
19748
19820
  settings: record(string(), unknown()).readonly().optional()
19749
19821
  });
19750
19822
  var CameraPipelineSettingsSchema = object({
19751
19823
  pinnedAgentNodeId: string().optional(),
19752
19824
  stepToggles: record(string(), boolean()).optional(),
19753
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19825
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19754
19826
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19755
19827
  });
19756
19828
  /**
@@ -19964,6 +20036,44 @@ var CameraStatusSchema = object({
19964
20036
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19965
20037
  fetchedAt: number()
19966
20038
  });
20039
+ var NodeInferenceDeviceSchema = object({
20040
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20041
+ key: string(),
20042
+ backend: string(),
20043
+ device: string(),
20044
+ format: _enum(MODEL_FORMATS),
20045
+ /** Whether the node's live probe reports the device as usable right now. */
20046
+ available: boolean(),
20047
+ /**
20048
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20049
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20050
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20051
+ * not a balanced target). An explicit stored value always wins; a stored-only
20052
+ * (unavailable) key keeps its stored value.
20053
+ */
20054
+ enabled: boolean(),
20055
+ /** Relative balancer weight for the enabled device (default 1). */
20056
+ weight: number(),
20057
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20058
+ maxSessions: number().nullable(),
20059
+ /** Object-detection model the executor defaults to for this deviceKey. */
20060
+ defaultModelId: string(),
20061
+ /**
20062
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20063
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20064
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20065
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20066
+ * available per format; this is the stored selection that becomes the
20067
+ * default for EVERY camera landing on this accelerator.
20068
+ */
20069
+ steps: record(string(), DeviceStepConfigSchema).optional()
20070
+ });
20071
+ var NodeInferenceDevicesSchema = object({
20072
+ nodeId: string(),
20073
+ /** False when the node's platform-probe was unreachable (no live device set). */
20074
+ reachable: boolean(),
20075
+ devices: array(NodeInferenceDeviceSchema).readonly()
20076
+ });
19967
20077
  method(object({
19968
20078
  deviceId: number(),
19969
20079
  agentNodeId: string()
@@ -19973,7 +20083,13 @@ method(object({
19973
20083
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19974
20084
  kind: "mutation",
19975
20085
  auth: "admin"
19976
- }), method(_void(), object({ migrated: number() }), {
20086
+ }), method(object({
20087
+ deviceId: number(),
20088
+ deviceKey: string()
20089
+ }), object({ success: literal(true) }), {
20090
+ kind: "mutation",
20091
+ auth: "admin"
20092
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19977
20093
  kind: "mutation",
19978
20094
  auth: "admin"
19979
20095
  }), 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({
@@ -20007,13 +20123,7 @@ method(object({
20007
20123
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20008
20124
  nodeId: string(),
20009
20125
  settings: AgentPipelineSettingsSchema
20010
- })).readonly()), method(object({
20011
- agentNodeId: string(),
20012
- defaults: record(string(), AgentAddonConfigSchema)
20013
- }), object({ success: literal(true) }), {
20014
- kind: "mutation",
20015
- auth: "admin"
20016
- }), method(object({ agentNodeId: string() }), object({
20126
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20017
20127
  success: boolean(),
20018
20128
  removed: boolean()
20019
20129
  }), {
@@ -20045,7 +20155,18 @@ method(object({
20045
20155
  }), object({ success: literal(true) }), {
20046
20156
  kind: "mutation",
20047
20157
  auth: "admin"
20048
- }), method(object({ agentNodeId: string() }), object({
20158
+ }), method(object({
20159
+ agentNodeId: string(),
20160
+ inferenceDevices: record(string(), object({
20161
+ enabled: boolean(),
20162
+ weight: number().positive().optional(),
20163
+ maxSessions: number().int().positive().optional(),
20164
+ steps: record(string(), DeviceStepConfigSchema).optional()
20165
+ }))
20166
+ }), object({ success: literal(true) }), {
20167
+ kind: "mutation",
20168
+ auth: "admin"
20169
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20049
20170
  success: literal(true),
20050
20171
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20051
20172
  effectiveModelId: string().nullable(),
@@ -20061,9 +20182,10 @@ method(object({
20061
20182
  }), object({ success: literal(true) }), {
20062
20183
  kind: "mutation",
20063
20184
  auth: "admin"
20064
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20185
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20065
20186
  deviceId: number(),
20066
20187
  agentNodeId: string(),
20188
+ deviceKey: string(),
20067
20189
  addonId: string(),
20068
20190
  patch: CameraStepOverridePatchSchema.nullable()
20069
20191
  }), object({ success: literal(true) }), {
@@ -20100,14 +20222,13 @@ method(object({
20100
20222
  });
20101
20223
  /**
20102
20224
  * server-management — per-NODE singleton capability for a node's ROOT
20103
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20104
- * agents).
20225
+ * package lifecycle (runtime-updatable node packages).
20105
20226
  *
20106
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20107
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20108
- * version describes the node. Updates install into
20109
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20110
- * starter (probation boot + auto-rollback to N-1).
20227
+ * Every node role runs the SAME root package (`@camstack/server`), which
20228
+ * carries the whole software stack in its npm dep tree, so ONE version
20229
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20230
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20231
+ * no auto-rollback).
20111
20232
  *
20112
20233
  * Providers:
20113
20234
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20215,7 +20336,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20215
20336
  /** Explicit target version; omitted = latest from the registry. */
20216
20337
  version: string().optional() }), ServerUpdateActionResultSchema, {
20217
20338
  kind: "mutation",
20218
- auth: "admin"
20339
+ auth: "admin",
20340
+ timeoutMs: 16 * 6e4
20219
20341
  }), method(_void(), ServerUpdateActionResultSchema, {
20220
20342
  kind: "mutation",
20221
20343
  auth: "admin"
@@ -21259,22 +21381,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21259
21381
  var RestartAddonResultSchema = unknown();
21260
21382
  var InstallPackageResultSchema = unknown();
21261
21383
  var ReloadPackagesResultSchema = unknown();
21262
- /**
21263
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21264
- * server restarts so the admin UI can react to the `restartingAt`
21265
- * timestamp (shows reconnect overlay). The transition from
21266
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21267
- * `system.restart-completed` event after the new process boots.
21268
- *
21269
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21270
- */
21271
- var UpdateFrameworkPackageResultSchema = object({
21272
- packageName: string(),
21273
- fromVersion: string(),
21274
- toVersion: string(),
21275
- /** Ms-epoch the server scheduled its self-restart. */
21276
- restartingAt: number()
21277
- });
21278
21384
  var BulkUpdateItemStatusSchema = _enum([
21279
21385
  "queued",
21280
21386
  "updating",
@@ -21402,13 +21508,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21402
21508
  }), object({ success: literal(true) }), {
21403
21509
  kind: "mutation",
21404
21510
  auth: "admin"
21405
- }), method(object({
21406
- packageName: string().min(1),
21407
- version: string().optional(),
21408
- deferRestart: boolean().optional()
21409
- }), UpdateFrameworkPackageResultSchema, {
21410
- kind: "mutation",
21411
- auth: "admin"
21412
21511
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21413
21512
  kind: "mutation",
21414
21513
  auth: "admin"
@@ -22291,10 +22390,10 @@ var TopologyCategorySchema = object({
22291
22390
  addons: array(TopologyCategoryAddonSchema).readonly()
22292
22391
  });
22293
22392
  /**
22294
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22295
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22296
- * version visibility for the Server management surface. Nullable: offline
22297
- * rows and pre-phase-2 nodes report none.
22393
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22394
+ * root package for every node role) as reported by its `registerNode`
22395
+ * manifest — version visibility for the Server management surface. Nullable:
22396
+ * offline rows and nodes that never reported one.
22298
22397
  */
22299
22398
  var TopologyRootPackageSchema = object({
22300
22399
  name: string(),
@@ -22682,17 +22781,28 @@ var PlatformScoreSchema = object({
22682
22781
  format: _enum([
22683
22782
  "onnx",
22684
22783
  "coreml",
22685
- "openvino"
22784
+ "openvino",
22785
+ "tflite"
22686
22786
  ]),
22687
22787
  score: number(),
22688
22788
  reason: string(),
22689
22789
  available: boolean()
22690
22790
  });
22791
+ var InferenceDeviceDescriptorSchema = object({
22792
+ key: string(),
22793
+ backend: string(),
22794
+ device: string(),
22795
+ format: ModelFormatSchema,
22796
+ runtime: literal("python"),
22797
+ score: number(),
22798
+ available: boolean()
22799
+ });
22691
22800
  var PlatformCapabilitiesSchema = object({
22692
22801
  hardware: HardwareInfoSchema,
22693
22802
  scores: array(PlatformScoreSchema).readonly(),
22694
22803
  bestScore: PlatformScoreSchema,
22695
- pythonPath: string().nullable()
22804
+ pythonPath: string().nullable(),
22805
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22696
22806
  });
22697
22807
  var ModelRequirementSchema = object({
22698
22808
  modelId: string(),
@@ -23571,12 +23681,6 @@ Object.freeze({
23571
23681
  addonId: null,
23572
23682
  access: "delete"
23573
23683
  },
23574
- "addons.updateFrameworkPackage": {
23575
- capName: "addons",
23576
- capScope: "system",
23577
- addonId: null,
23578
- access: "create"
23579
- },
23580
23684
  "addons.updatePackage": {
23581
23685
  capName: "addons",
23582
23686
  capScope: "system",
@@ -26349,12 +26453,6 @@ Object.freeze({
26349
26453
  addonId: null,
26350
26454
  access: "view"
26351
26455
  },
26352
- "pipelineExecutor.reprobeEngine": {
26353
- capName: "pipeline-executor",
26354
- capScope: "system",
26355
- addonId: null,
26356
- access: "create"
26357
- },
26358
26456
  "pipelineExecutor.runAudioTest": {
26359
26457
  capName: "pipeline-executor",
26360
26458
  capScope: "system",
@@ -26505,6 +26603,12 @@ Object.freeze({
26505
26603
  addonId: null,
26506
26604
  access: "view"
26507
26605
  },
26606
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26607
+ capName: "pipeline-orchestrator",
26608
+ capScope: "system",
26609
+ addonId: null,
26610
+ access: "view"
26611
+ },
26508
26612
  "pipelineOrchestrator.getPipelineAssignment": {
26509
26613
  capName: "pipeline-orchestrator",
26510
26614
  capScope: "system",
@@ -26517,6 +26621,12 @@ Object.freeze({
26517
26621
  addonId: null,
26518
26622
  access: "view"
26519
26623
  },
26624
+ "pipelineOrchestrator.getPipelineDevicePin": {
26625
+ capName: "pipeline-orchestrator",
26626
+ capScope: "system",
26627
+ addonId: null,
26628
+ access: "view"
26629
+ },
26520
26630
  "pipelineOrchestrator.listAgentSettings": {
26521
26631
  capName: "pipeline-orchestrator",
26522
26632
  capScope: "system",
@@ -26559,19 +26669,19 @@ Object.freeze({
26559
26669
  addonId: null,
26560
26670
  access: "create"
26561
26671
  },
26562
- "pipelineOrchestrator.setAgentAddonDefaults": {
26672
+ "pipelineOrchestrator.setAgentCapabilities": {
26563
26673
  capName: "pipeline-orchestrator",
26564
26674
  capScope: "system",
26565
26675
  addonId: null,
26566
26676
  access: "create"
26567
26677
  },
26568
- "pipelineOrchestrator.setAgentCapabilities": {
26678
+ "pipelineOrchestrator.setAgentDetectWeight": {
26569
26679
  capName: "pipeline-orchestrator",
26570
26680
  capScope: "system",
26571
26681
  addonId: null,
26572
26682
  access: "create"
26573
26683
  },
26574
- "pipelineOrchestrator.setAgentDetectWeight": {
26684
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26575
26685
  capName: "pipeline-orchestrator",
26576
26686
  capScope: "system",
26577
26687
  addonId: null,
@@ -26613,6 +26723,12 @@ Object.freeze({
26613
26723
  addonId: null,
26614
26724
  access: "create"
26615
26725
  },
26726
+ "pipelineOrchestrator.setPipelineDevicePin": {
26727
+ capName: "pipeline-orchestrator",
26728
+ capScope: "system",
26729
+ addonId: null,
26730
+ access: "create"
26731
+ },
26616
26732
  "pipelineOrchestrator.unassignAudio": {
26617
26733
  capName: "pipeline-orchestrator",
26618
26734
  capScope: "system",
@@ -28165,32 +28281,6 @@ Object.freeze({
28165
28281
  "network-access": "ingress",
28166
28282
  "smtp-provider": "email"
28167
28283
  });
28168
- var frameworkSwapPackageSchema = object({
28169
- name: string(),
28170
- stagedPath: string(),
28171
- backupPath: string(),
28172
- toVersion: string(),
28173
- fromVersion: string().nullable()
28174
- });
28175
- object({
28176
- jobId: string(),
28177
- taskId: string(),
28178
- packages: array(frameworkSwapPackageSchema),
28179
- requestedAtMs: number(),
28180
- schemaVersion: literal(1)
28181
- });
28182
- object({
28183
- jobId: string(),
28184
- taskId: string(),
28185
- backups: array(object({
28186
- name: string(),
28187
- backupPath: string(),
28188
- livePath: string()
28189
- })),
28190
- appliedAtMs: number(),
28191
- bootAttempts: number(),
28192
- schemaVersion: literal(1)
28193
- });
28194
28284
  //#endregion
28195
28285
  //#region src/config.ts
28196
28286
  /**
package/dist/addon.mjs CHANGED
@@ -7097,6 +7097,17 @@ var ModelCatalogEntrySchema = object({
7097
7097
  "imagenet",
7098
7098
  "none"
7099
7099
  ]).optional(),
7100
+ /**
7101
+ * The model already applies softmax IN-GRAPH — its raw output is a
7102
+ * probability distribution, not logits. When set, the `softmax`
7103
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7104
+ * probability vector collapses it toward uniform (top-1 score craters far
7105
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7106
+ * the output is raw logits and the postprocessor applies softmax (the normal
7107
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7108
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7109
+ */
7110
+ outputProbabilities: boolean().optional(),
7100
7111
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7101
7112
  /**
7102
7113
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12387,10 +12398,7 @@ var ConfigUISchemaNullableBridge = custom();
12387
12398
  var InferenceCapabilitiesBridge = custom();
12388
12399
  var ModelAvailabilityListBridge = custom();
12389
12400
  var PipelineRunResultBridge = custom();
12390
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12391
- kind: "mutation",
12392
- auth: "admin"
12393
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12401
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12394
12402
  modelId: string(),
12395
12403
  settings: record(string(), unknown()).readonly()
12396
12404
  }))), method(object({ steps: record(string(), object({
@@ -12450,13 +12458,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12450
12458
  * (inputClasses ≠ null) are skipped and served per-track via
12451
12459
  * pipelineRunner.runDetailSubtree (two-plane design).
12452
12460
  */
12453
- plane: _enum(["full", "frame"]).optional()
12461
+ plane: _enum(["full", "frame"]).optional(),
12462
+ /**
12463
+ * Inference-device selector (Phase 2 multi-device). Format
12464
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12465
+ * Omitted ⇒ the runner's default device (current single-engine
12466
+ * behaviour). Selects WHICH device pool of the node runs the call.
12467
+ */
12468
+ deviceKey: string().optional()
12454
12469
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12455
12470
  engine: PipelineEngineChoiceSchema.optional(),
12456
12471
  steps: array(PipelineStepInputSchema).min(1),
12457
12472
  frames: array(FrameInputSchema).min(1).max(255),
12458
12473
  deviceId: number().optional(),
12459
- sessionId: string().optional()
12474
+ sessionId: string().optional(),
12475
+ /**
12476
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12477
+ * the batch to the Python pool's bench preprocess cache
12478
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12479
+ * preprocessed ONCE and every later inference is a pure-inference cache
12480
+ * hit — the sustained-throughput run measures inference, not
12481
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12482
+ * full preprocess every call, correct). Fresh per sustained run;
12483
+ * released via `uncacheFrame`.
12484
+ */
12485
+ frameId: number().int().nonnegative().optional(),
12486
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12487
+ deviceKey: string().optional()
12460
12488
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12461
12489
  data: _instanceof(Uint8Array),
12462
12490
  width: number().int().positive(),
@@ -12488,8 +12516,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12488
12516
  * - `runtime` — main camera-serving engine (no idle TTL).
12489
12517
  * - `warm-override` — benchmark/test override held in the warm
12490
12518
  * cache; auto-disposed after the idle TTL.
12519
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12520
+ * multi-device, keyed by `deviceKey`) resolved
12521
+ * via `resolveDeviceFactory`. Runs alongside the
12522
+ * `runtime` engine on a DIFFERENT accelerator
12523
+ * (NPU / iGPU / Coral) — this is how the
12524
+ * Engines tab shows all pools running at once.
12491
12525
  */
12492
- kind: _enum(["runtime", "warm-override"]),
12526
+ kind: _enum([
12527
+ "runtime",
12528
+ "warm-override",
12529
+ "device-pool"
12530
+ ]),
12493
12531
  /** Native pid of the underlying Python pool (null when no pool). */
12494
12532
  poolPid: number().nullable(),
12495
12533
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12864,7 +12902,14 @@ var RunnerCameraConfigSchema = object({
12864
12902
  * camera's detect node differs from its source-owner (P2d, gated by the
12865
12903
  * `remoteSourcingNodes` rollout setting).
12866
12904
  */
12867
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12905
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12906
+ /**
12907
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12908
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12909
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12910
+ * this only selects WHICH device pool of that node runs the session.
12911
+ */
12912
+ deviceKey: string().optional()
12868
12913
  });
12869
12914
  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;
12870
12915
  /**
@@ -12885,6 +12930,19 @@ var RunnerLocalLoadSchema = object({
12885
12930
  avgInferenceTimeMs: number(),
12886
12931
  /** Total queue depth across motion + detection queues. */
12887
12932
  queueDepthTotal: number(),
12933
+ /**
12934
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12935
+ * this runner currently has attached cameras on, so the orchestrator's second
12936
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12937
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12938
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12939
+ */
12940
+ devices: array(object({
12941
+ deviceKey: string(),
12942
+ backend: string(),
12943
+ attachedCameras: number(),
12944
+ queueDepthTotal: number()
12945
+ })).default([]),
12888
12946
  /** Hardware capability flags reported by this node. */
12889
12947
  hardware: object({
12890
12948
  hasGpu: boolean(),
@@ -16033,6 +16091,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16033
16091
  return toDeviceSummary(device, this.addonId);
16034
16092
  }
16035
16093
  };
16094
+ 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;
16095
+ new Set(Object.values(DeviceType));
16036
16096
  /**
16037
16097
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16038
16098
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19702,13 +19762,11 @@ var PipelineTemplateSchema = object({
19702
19762
  createdAt: string(),
19703
19763
  updatedAt: string()
19704
19764
  });
19705
- var AgentAddonConfigSchema = object({
19706
- enabled: boolean(),
19765
+ var DeviceStepConfigSchema = object({
19707
19766
  modelId: string().optional(),
19708
- settings: record(string(), unknown()).readonly()
19767
+ settings: record(string(), unknown()).optional()
19709
19768
  });
19710
19769
  var AgentPipelineSettingsSchema = object({
19711
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19712
19770
  maxCameras: number().int().nonnegative().nullable().default(null),
19713
19771
  /** Per-node detection weight (relative share for the quota balancer). */
19714
19772
  detectWeight: number().positive().optional(),
@@ -19732,7 +19790,22 @@ var AgentPipelineSettingsSchema = object({
19732
19790
  * it already uses to reach the hub). Set this only when the auto-detected
19733
19791
  * address is wrong (multi-homed host, NAT, custom interface).
19734
19792
  */
19735
- reachableHost: string().optional()
19793
+ reachableHost: string().optional(),
19794
+ /**
19795
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19796
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19797
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19798
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19799
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19800
+ * the default model/settings for every camera landing on that accelerator;
19801
+ * a stepId absent ⇒ the step uses that device's format default.
19802
+ */
19803
+ inferenceDevices: record(string(), object({
19804
+ enabled: boolean(),
19805
+ weight: number().positive().optional(),
19806
+ maxSessions: number().int().positive().optional(),
19807
+ steps: record(string(), DeviceStepConfigSchema).optional()
19808
+ })).optional()
19736
19809
  });
19737
19810
  var CameraPipelineForAgentSchema = object({
19738
19811
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19742,14 +19815,13 @@ var CameraPipelineForAgentSchema = object({
19742
19815
  }).nullable()
19743
19816
  });
19744
19817
  var CameraStepOverridePatchSchema = object({
19745
- enabled: boolean().optional(),
19746
19818
  modelId: string().optional(),
19747
19819
  settings: record(string(), unknown()).readonly().optional()
19748
19820
  });
19749
19821
  var CameraPipelineSettingsSchema = object({
19750
19822
  pinnedAgentNodeId: string().optional(),
19751
19823
  stepToggles: record(string(), boolean()).optional(),
19752
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19824
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19753
19825
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19754
19826
  });
19755
19827
  /**
@@ -19963,6 +20035,44 @@ var CameraStatusSchema = object({
19963
20035
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19964
20036
  fetchedAt: number()
19965
20037
  });
20038
+ var NodeInferenceDeviceSchema = object({
20039
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20040
+ key: string(),
20041
+ backend: string(),
20042
+ device: string(),
20043
+ format: _enum(MODEL_FORMATS),
20044
+ /** Whether the node's live probe reports the device as usable right now. */
20045
+ available: boolean(),
20046
+ /**
20047
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20048
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20049
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20050
+ * not a balanced target). An explicit stored value always wins; a stored-only
20051
+ * (unavailable) key keeps its stored value.
20052
+ */
20053
+ enabled: boolean(),
20054
+ /** Relative balancer weight for the enabled device (default 1). */
20055
+ weight: number(),
20056
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20057
+ maxSessions: number().nullable(),
20058
+ /** Object-detection model the executor defaults to for this deviceKey. */
20059
+ defaultModelId: string(),
20060
+ /**
20061
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20062
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20063
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20064
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20065
+ * available per format; this is the stored selection that becomes the
20066
+ * default for EVERY camera landing on this accelerator.
20067
+ */
20068
+ steps: record(string(), DeviceStepConfigSchema).optional()
20069
+ });
20070
+ var NodeInferenceDevicesSchema = object({
20071
+ nodeId: string(),
20072
+ /** False when the node's platform-probe was unreachable (no live device set). */
20073
+ reachable: boolean(),
20074
+ devices: array(NodeInferenceDeviceSchema).readonly()
20075
+ });
19966
20076
  method(object({
19967
20077
  deviceId: number(),
19968
20078
  agentNodeId: string()
@@ -19972,7 +20082,13 @@ method(object({
19972
20082
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19973
20083
  kind: "mutation",
19974
20084
  auth: "admin"
19975
- }), method(_void(), object({ migrated: number() }), {
20085
+ }), method(object({
20086
+ deviceId: number(),
20087
+ deviceKey: string()
20088
+ }), object({ success: literal(true) }), {
20089
+ kind: "mutation",
20090
+ auth: "admin"
20091
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19976
20092
  kind: "mutation",
19977
20093
  auth: "admin"
19978
20094
  }), 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({
@@ -20006,13 +20122,7 @@ method(object({
20006
20122
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20007
20123
  nodeId: string(),
20008
20124
  settings: AgentPipelineSettingsSchema
20009
- })).readonly()), method(object({
20010
- agentNodeId: string(),
20011
- defaults: record(string(), AgentAddonConfigSchema)
20012
- }), object({ success: literal(true) }), {
20013
- kind: "mutation",
20014
- auth: "admin"
20015
- }), method(object({ agentNodeId: string() }), object({
20125
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20016
20126
  success: boolean(),
20017
20127
  removed: boolean()
20018
20128
  }), {
@@ -20044,7 +20154,18 @@ method(object({
20044
20154
  }), object({ success: literal(true) }), {
20045
20155
  kind: "mutation",
20046
20156
  auth: "admin"
20047
- }), method(object({ agentNodeId: string() }), object({
20157
+ }), method(object({
20158
+ agentNodeId: string(),
20159
+ inferenceDevices: record(string(), object({
20160
+ enabled: boolean(),
20161
+ weight: number().positive().optional(),
20162
+ maxSessions: number().int().positive().optional(),
20163
+ steps: record(string(), DeviceStepConfigSchema).optional()
20164
+ }))
20165
+ }), object({ success: literal(true) }), {
20166
+ kind: "mutation",
20167
+ auth: "admin"
20168
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20048
20169
  success: literal(true),
20049
20170
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20050
20171
  effectiveModelId: string().nullable(),
@@ -20060,9 +20181,10 @@ method(object({
20060
20181
  }), object({ success: literal(true) }), {
20061
20182
  kind: "mutation",
20062
20183
  auth: "admin"
20063
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20184
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20064
20185
  deviceId: number(),
20065
20186
  agentNodeId: string(),
20187
+ deviceKey: string(),
20066
20188
  addonId: string(),
20067
20189
  patch: CameraStepOverridePatchSchema.nullable()
20068
20190
  }), object({ success: literal(true) }), {
@@ -20099,14 +20221,13 @@ method(object({
20099
20221
  });
20100
20222
  /**
20101
20223
  * server-management — per-NODE singleton capability for a node's ROOT
20102
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20103
- * agents).
20224
+ * package lifecycle (runtime-updatable node packages).
20104
20225
  *
20105
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20106
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20107
- * version describes the node. Updates install into
20108
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20109
- * starter (probation boot + auto-rollback to N-1).
20226
+ * Every node role runs the SAME root package (`@camstack/server`), which
20227
+ * carries the whole software stack in its npm dep tree, so ONE version
20228
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20229
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20230
+ * no auto-rollback).
20110
20231
  *
20111
20232
  * Providers:
20112
20233
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20214,7 +20335,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20214
20335
  /** Explicit target version; omitted = latest from the registry. */
20215
20336
  version: string().optional() }), ServerUpdateActionResultSchema, {
20216
20337
  kind: "mutation",
20217
- auth: "admin"
20338
+ auth: "admin",
20339
+ timeoutMs: 16 * 6e4
20218
20340
  }), method(_void(), ServerUpdateActionResultSchema, {
20219
20341
  kind: "mutation",
20220
20342
  auth: "admin"
@@ -21258,22 +21380,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21258
21380
  var RestartAddonResultSchema = unknown();
21259
21381
  var InstallPackageResultSchema = unknown();
21260
21382
  var ReloadPackagesResultSchema = unknown();
21261
- /**
21262
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21263
- * server restarts so the admin UI can react to the `restartingAt`
21264
- * timestamp (shows reconnect overlay). The transition from
21265
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21266
- * `system.restart-completed` event after the new process boots.
21267
- *
21268
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21269
- */
21270
- var UpdateFrameworkPackageResultSchema = object({
21271
- packageName: string(),
21272
- fromVersion: string(),
21273
- toVersion: string(),
21274
- /** Ms-epoch the server scheduled its self-restart. */
21275
- restartingAt: number()
21276
- });
21277
21383
  var BulkUpdateItemStatusSchema = _enum([
21278
21384
  "queued",
21279
21385
  "updating",
@@ -21401,13 +21507,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21401
21507
  }), object({ success: literal(true) }), {
21402
21508
  kind: "mutation",
21403
21509
  auth: "admin"
21404
- }), method(object({
21405
- packageName: string().min(1),
21406
- version: string().optional(),
21407
- deferRestart: boolean().optional()
21408
- }), UpdateFrameworkPackageResultSchema, {
21409
- kind: "mutation",
21410
- auth: "admin"
21411
21510
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21412
21511
  kind: "mutation",
21413
21512
  auth: "admin"
@@ -22290,10 +22389,10 @@ var TopologyCategorySchema = object({
22290
22389
  addons: array(TopologyCategoryAddonSchema).readonly()
22291
22390
  });
22292
22391
  /**
22293
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22294
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22295
- * version visibility for the Server management surface. Nullable: offline
22296
- * rows and pre-phase-2 nodes report none.
22392
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22393
+ * root package for every node role) as reported by its `registerNode`
22394
+ * manifest — version visibility for the Server management surface. Nullable:
22395
+ * offline rows and nodes that never reported one.
22297
22396
  */
22298
22397
  var TopologyRootPackageSchema = object({
22299
22398
  name: string(),
@@ -22681,17 +22780,28 @@ var PlatformScoreSchema = object({
22681
22780
  format: _enum([
22682
22781
  "onnx",
22683
22782
  "coreml",
22684
- "openvino"
22783
+ "openvino",
22784
+ "tflite"
22685
22785
  ]),
22686
22786
  score: number(),
22687
22787
  reason: string(),
22688
22788
  available: boolean()
22689
22789
  });
22790
+ var InferenceDeviceDescriptorSchema = object({
22791
+ key: string(),
22792
+ backend: string(),
22793
+ device: string(),
22794
+ format: ModelFormatSchema,
22795
+ runtime: literal("python"),
22796
+ score: number(),
22797
+ available: boolean()
22798
+ });
22690
22799
  var PlatformCapabilitiesSchema = object({
22691
22800
  hardware: HardwareInfoSchema,
22692
22801
  scores: array(PlatformScoreSchema).readonly(),
22693
22802
  bestScore: PlatformScoreSchema,
22694
- pythonPath: string().nullable()
22803
+ pythonPath: string().nullable(),
22804
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22695
22805
  });
22696
22806
  var ModelRequirementSchema = object({
22697
22807
  modelId: string(),
@@ -23570,12 +23680,6 @@ Object.freeze({
23570
23680
  addonId: null,
23571
23681
  access: "delete"
23572
23682
  },
23573
- "addons.updateFrameworkPackage": {
23574
- capName: "addons",
23575
- capScope: "system",
23576
- addonId: null,
23577
- access: "create"
23578
- },
23579
23683
  "addons.updatePackage": {
23580
23684
  capName: "addons",
23581
23685
  capScope: "system",
@@ -26348,12 +26452,6 @@ Object.freeze({
26348
26452
  addonId: null,
26349
26453
  access: "view"
26350
26454
  },
26351
- "pipelineExecutor.reprobeEngine": {
26352
- capName: "pipeline-executor",
26353
- capScope: "system",
26354
- addonId: null,
26355
- access: "create"
26356
- },
26357
26455
  "pipelineExecutor.runAudioTest": {
26358
26456
  capName: "pipeline-executor",
26359
26457
  capScope: "system",
@@ -26504,6 +26602,12 @@ Object.freeze({
26504
26602
  addonId: null,
26505
26603
  access: "view"
26506
26604
  },
26605
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26606
+ capName: "pipeline-orchestrator",
26607
+ capScope: "system",
26608
+ addonId: null,
26609
+ access: "view"
26610
+ },
26507
26611
  "pipelineOrchestrator.getPipelineAssignment": {
26508
26612
  capName: "pipeline-orchestrator",
26509
26613
  capScope: "system",
@@ -26516,6 +26620,12 @@ Object.freeze({
26516
26620
  addonId: null,
26517
26621
  access: "view"
26518
26622
  },
26623
+ "pipelineOrchestrator.getPipelineDevicePin": {
26624
+ capName: "pipeline-orchestrator",
26625
+ capScope: "system",
26626
+ addonId: null,
26627
+ access: "view"
26628
+ },
26519
26629
  "pipelineOrchestrator.listAgentSettings": {
26520
26630
  capName: "pipeline-orchestrator",
26521
26631
  capScope: "system",
@@ -26558,19 +26668,19 @@ Object.freeze({
26558
26668
  addonId: null,
26559
26669
  access: "create"
26560
26670
  },
26561
- "pipelineOrchestrator.setAgentAddonDefaults": {
26671
+ "pipelineOrchestrator.setAgentCapabilities": {
26562
26672
  capName: "pipeline-orchestrator",
26563
26673
  capScope: "system",
26564
26674
  addonId: null,
26565
26675
  access: "create"
26566
26676
  },
26567
- "pipelineOrchestrator.setAgentCapabilities": {
26677
+ "pipelineOrchestrator.setAgentDetectWeight": {
26568
26678
  capName: "pipeline-orchestrator",
26569
26679
  capScope: "system",
26570
26680
  addonId: null,
26571
26681
  access: "create"
26572
26682
  },
26573
- "pipelineOrchestrator.setAgentDetectWeight": {
26683
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26574
26684
  capName: "pipeline-orchestrator",
26575
26685
  capScope: "system",
26576
26686
  addonId: null,
@@ -26612,6 +26722,12 @@ Object.freeze({
26612
26722
  addonId: null,
26613
26723
  access: "create"
26614
26724
  },
26725
+ "pipelineOrchestrator.setPipelineDevicePin": {
26726
+ capName: "pipeline-orchestrator",
26727
+ capScope: "system",
26728
+ addonId: null,
26729
+ access: "create"
26730
+ },
26615
26731
  "pipelineOrchestrator.unassignAudio": {
26616
26732
  capName: "pipeline-orchestrator",
26617
26733
  capScope: "system",
@@ -28164,32 +28280,6 @@ Object.freeze({
28164
28280
  "network-access": "ingress",
28165
28281
  "smtp-provider": "email"
28166
28282
  });
28167
- var frameworkSwapPackageSchema = object({
28168
- name: string(),
28169
- stagedPath: string(),
28170
- backupPath: string(),
28171
- toVersion: string(),
28172
- fromVersion: string().nullable()
28173
- });
28174
- object({
28175
- jobId: string(),
28176
- taskId: string(),
28177
- packages: array(frameworkSwapPackageSchema),
28178
- requestedAtMs: number(),
28179
- schemaVersion: literal(1)
28180
- });
28181
- object({
28182
- jobId: string(),
28183
- taskId: string(),
28184
- backups: array(object({
28185
- name: string(),
28186
- backupPath: string(),
28187
- livePath: string()
28188
- })),
28189
- appliedAtMs: number(),
28190
- bootAttempts: number(),
28191
- schemaVersion: literal(1)
28192
- });
28193
28283
  //#endregion
28194
28284
  //#region src/config.ts
28195
28285
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-unifi",
3
- "version": "0.1.14",
3
+ "version": "0.2.0",
4
4
  "description": "UniFi Network controller device-provider addon for CamStack — local-controller infra switches/APs (as containers) + network-client presence. NO cameras/Protect.",
5
5
  "keywords": [
6
6
  "camstack",