@camstack/addon-provider-vesync 0.1.14 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +213 -106
  2. package/dist/addon.mjs +213 -106
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -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). */
@@ -12625,7 +12663,21 @@ var NativeCropResultSchema = object({
12625
12663
  /** Packed rgb (24-bit) pixels of the crop. */
12626
12664
  bytes: _instanceof(Uint8Array),
12627
12665
  width: number().int().positive(),
12628
- height: number().int().positive()
12666
+ height: number().int().positive(),
12667
+ /**
12668
+ * Which source served this crop, so a quality-sensitive consumer (the native
12669
+ * `keyFrame`) can reject a degraded fallback:
12670
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12671
+ * quality path).
12672
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12673
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12674
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12675
+ *
12676
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12677
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12678
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12679
+ */
12680
+ tier: _enum(["native", "ram-fullframe"]).optional()
12629
12681
  });
12630
12682
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12631
12683
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12865,7 +12917,14 @@ var RunnerCameraConfigSchema = object({
12865
12917
  * camera's detect node differs from its source-owner (P2d, gated by the
12866
12918
  * `remoteSourcingNodes` rollout setting).
12867
12919
  */
12868
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12920
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12921
+ /**
12922
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12923
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12924
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12925
+ * this only selects WHICH device pool of that node runs the session.
12926
+ */
12927
+ deviceKey: string().optional()
12869
12928
  });
12870
12929
  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
12930
  /**
@@ -12886,6 +12945,19 @@ var RunnerLocalLoadSchema = object({
12886
12945
  avgInferenceTimeMs: number(),
12887
12946
  /** Total queue depth across motion + detection queues. */
12888
12947
  queueDepthTotal: number(),
12948
+ /**
12949
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12950
+ * this runner currently has attached cameras on, so the orchestrator's second
12951
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12952
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12953
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12954
+ */
12955
+ devices: array(object({
12956
+ deviceKey: string(),
12957
+ backend: string(),
12958
+ attachedCameras: number(),
12959
+ queueDepthTotal: number()
12960
+ })).default([]),
12889
12961
  /** Hardware capability flags reported by this node. */
12890
12962
  hardware: object({
12891
12963
  hasGpu: boolean(),
@@ -16034,6 +16106,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16034
16106
  return toDeviceSummary(device, this.addonId);
16035
16107
  }
16036
16108
  };
16109
+ 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;
16110
+ new Set(Object.values(DeviceType));
16037
16111
  /**
16038
16112
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16039
16113
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17607,7 +17681,8 @@ var LinkedDeviceSchema = object({
17607
17681
  deviceId: number(),
17608
17682
  name: string(),
17609
17683
  location: string().nullable(),
17610
- features: array(string())
17684
+ features: array(string()),
17685
+ producesTrackedEvents: boolean().optional()
17611
17686
  });
17612
17687
  var SavedDeviceRowSchema = object({
17613
17688
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19237,6 +19312,7 @@ var TrackSchema = object({
19237
19312
  deviceId: number(),
19238
19313
  className: string(),
19239
19314
  label: string().optional(),
19315
+ producingDeviceName: string().optional(),
19240
19316
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19241
19317
  source: TrackSourceSchema.optional(),
19242
19318
  firstSeen: number(),
@@ -19374,7 +19450,8 @@ var MediaFileKindEnum = _enum([
19374
19450
  "fullFrameBoxed",
19375
19451
  "faceCrop",
19376
19452
  "plateCrop",
19377
- "keyFrame"
19453
+ "keyFrame",
19454
+ "keyFrameSmall"
19378
19455
  ]);
19379
19456
  var MediaFileSchema = object({
19380
19457
  key: string(),
@@ -19703,13 +19780,11 @@ var PipelineTemplateSchema = object({
19703
19780
  createdAt: string(),
19704
19781
  updatedAt: string()
19705
19782
  });
19706
- var AgentAddonConfigSchema = object({
19707
- enabled: boolean(),
19783
+ var DeviceStepConfigSchema = object({
19708
19784
  modelId: string().optional(),
19709
- settings: record(string(), unknown()).readonly()
19785
+ settings: record(string(), unknown()).optional()
19710
19786
  });
19711
19787
  var AgentPipelineSettingsSchema = object({
19712
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19713
19788
  maxCameras: number().int().nonnegative().nullable().default(null),
19714
19789
  /** Per-node detection weight (relative share for the quota balancer). */
19715
19790
  detectWeight: number().positive().optional(),
@@ -19733,7 +19808,22 @@ var AgentPipelineSettingsSchema = object({
19733
19808
  * it already uses to reach the hub). Set this only when the auto-detected
19734
19809
  * address is wrong (multi-homed host, NAT, custom interface).
19735
19810
  */
19736
- reachableHost: string().optional()
19811
+ reachableHost: string().optional(),
19812
+ /**
19813
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19814
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19815
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19816
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19817
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19818
+ * the default model/settings for every camera landing on that accelerator;
19819
+ * a stepId absent ⇒ the step uses that device's format default.
19820
+ */
19821
+ inferenceDevices: record(string(), object({
19822
+ enabled: boolean(),
19823
+ weight: number().positive().optional(),
19824
+ maxSessions: number().int().positive().optional(),
19825
+ steps: record(string(), DeviceStepConfigSchema).optional()
19826
+ })).optional()
19737
19827
  });
19738
19828
  var CameraPipelineForAgentSchema = object({
19739
19829
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19743,14 +19833,13 @@ var CameraPipelineForAgentSchema = object({
19743
19833
  }).nullable()
19744
19834
  });
19745
19835
  var CameraStepOverridePatchSchema = object({
19746
- enabled: boolean().optional(),
19747
19836
  modelId: string().optional(),
19748
19837
  settings: record(string(), unknown()).readonly().optional()
19749
19838
  });
19750
19839
  var CameraPipelineSettingsSchema = object({
19751
19840
  pinnedAgentNodeId: string().optional(),
19752
19841
  stepToggles: record(string(), boolean()).optional(),
19753
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19842
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19754
19843
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19755
19844
  });
19756
19845
  /**
@@ -19964,6 +20053,44 @@ var CameraStatusSchema = object({
19964
20053
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19965
20054
  fetchedAt: number()
19966
20055
  });
20056
+ var NodeInferenceDeviceSchema = object({
20057
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20058
+ key: string(),
20059
+ backend: string(),
20060
+ device: string(),
20061
+ format: _enum(MODEL_FORMATS),
20062
+ /** Whether the node's live probe reports the device as usable right now. */
20063
+ available: boolean(),
20064
+ /**
20065
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20066
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20067
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20068
+ * not a balanced target). An explicit stored value always wins; a stored-only
20069
+ * (unavailable) key keeps its stored value.
20070
+ */
20071
+ enabled: boolean(),
20072
+ /** Relative balancer weight for the enabled device (default 1). */
20073
+ weight: number(),
20074
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20075
+ maxSessions: number().nullable(),
20076
+ /** Object-detection model the executor defaults to for this deviceKey. */
20077
+ defaultModelId: string(),
20078
+ /**
20079
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20080
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20081
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20082
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20083
+ * available per format; this is the stored selection that becomes the
20084
+ * default for EVERY camera landing on this accelerator.
20085
+ */
20086
+ steps: record(string(), DeviceStepConfigSchema).optional()
20087
+ });
20088
+ var NodeInferenceDevicesSchema = object({
20089
+ nodeId: string(),
20090
+ /** False when the node's platform-probe was unreachable (no live device set). */
20091
+ reachable: boolean(),
20092
+ devices: array(NodeInferenceDeviceSchema).readonly()
20093
+ });
19967
20094
  method(object({
19968
20095
  deviceId: number(),
19969
20096
  agentNodeId: string()
@@ -19973,7 +20100,13 @@ method(object({
19973
20100
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19974
20101
  kind: "mutation",
19975
20102
  auth: "admin"
19976
- }), method(_void(), object({ migrated: number() }), {
20103
+ }), method(object({
20104
+ deviceId: number(),
20105
+ deviceKey: string()
20106
+ }), object({ success: literal(true) }), {
20107
+ kind: "mutation",
20108
+ auth: "admin"
20109
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19977
20110
  kind: "mutation",
19978
20111
  auth: "admin"
19979
20112
  }), 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 +20140,7 @@ method(object({
20007
20140
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20008
20141
  nodeId: string(),
20009
20142
  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({
20143
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20017
20144
  success: boolean(),
20018
20145
  removed: boolean()
20019
20146
  }), {
@@ -20045,7 +20172,18 @@ method(object({
20045
20172
  }), object({ success: literal(true) }), {
20046
20173
  kind: "mutation",
20047
20174
  auth: "admin"
20048
- }), method(object({ agentNodeId: string() }), object({
20175
+ }), method(object({
20176
+ agentNodeId: string(),
20177
+ inferenceDevices: record(string(), object({
20178
+ enabled: boolean(),
20179
+ weight: number().positive().optional(),
20180
+ maxSessions: number().int().positive().optional(),
20181
+ steps: record(string(), DeviceStepConfigSchema).optional()
20182
+ }))
20183
+ }), object({ success: literal(true) }), {
20184
+ kind: "mutation",
20185
+ auth: "admin"
20186
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20049
20187
  success: literal(true),
20050
20188
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20051
20189
  effectiveModelId: string().nullable(),
@@ -20061,9 +20199,10 @@ method(object({
20061
20199
  }), object({ success: literal(true) }), {
20062
20200
  kind: "mutation",
20063
20201
  auth: "admin"
20064
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20202
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20065
20203
  deviceId: number(),
20066
20204
  agentNodeId: string(),
20205
+ deviceKey: string(),
20067
20206
  addonId: string(),
20068
20207
  patch: CameraStepOverridePatchSchema.nullable()
20069
20208
  }), object({ success: literal(true) }), {
@@ -20100,14 +20239,13 @@ method(object({
20100
20239
  });
20101
20240
  /**
20102
20241
  * server-management — per-NODE singleton capability for a node's ROOT
20103
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20104
- * agents).
20242
+ * package lifecycle (runtime-updatable node packages).
20105
20243
  *
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).
20244
+ * Every node role runs the SAME root package (`@camstack/server`), which
20245
+ * carries the whole software stack in its npm dep tree, so ONE version
20246
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20247
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20248
+ * no auto-rollback).
20111
20249
  *
20112
20250
  * Providers:
20113
20251
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20215,7 +20353,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20215
20353
  /** Explicit target version; omitted = latest from the registry. */
20216
20354
  version: string().optional() }), ServerUpdateActionResultSchema, {
20217
20355
  kind: "mutation",
20218
- auth: "admin"
20356
+ auth: "admin",
20357
+ timeoutMs: 16 * 6e4
20219
20358
  }), method(_void(), ServerUpdateActionResultSchema, {
20220
20359
  kind: "mutation",
20221
20360
  auth: "admin"
@@ -21259,22 +21398,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21259
21398
  var RestartAddonResultSchema = unknown();
21260
21399
  var InstallPackageResultSchema = unknown();
21261
21400
  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
21401
  var BulkUpdateItemStatusSchema = _enum([
21279
21402
  "queued",
21280
21403
  "updating",
@@ -21402,13 +21525,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21402
21525
  }), object({ success: literal(true) }), {
21403
21526
  kind: "mutation",
21404
21527
  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
21528
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21413
21529
  kind: "mutation",
21414
21530
  auth: "admin"
@@ -22274,10 +22390,10 @@ var TopologyCategorySchema = object({
22274
22390
  addons: array(TopologyCategoryAddonSchema).readonly()
22275
22391
  });
22276
22392
  /**
22277
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22278
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22279
- * version visibility for the Server management surface. Nullable: offline
22280
- * 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.
22281
22397
  */
22282
22398
  var TopologyRootPackageSchema = object({
22283
22399
  name: string(),
@@ -22665,17 +22781,28 @@ var PlatformScoreSchema = object({
22665
22781
  format: _enum([
22666
22782
  "onnx",
22667
22783
  "coreml",
22668
- "openvino"
22784
+ "openvino",
22785
+ "tflite"
22669
22786
  ]),
22670
22787
  score: number(),
22671
22788
  reason: string(),
22672
22789
  available: boolean()
22673
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
+ });
22674
22800
  var PlatformCapabilitiesSchema = object({
22675
22801
  hardware: HardwareInfoSchema,
22676
22802
  scores: array(PlatformScoreSchema).readonly(),
22677
22803
  bestScore: PlatformScoreSchema,
22678
- pythonPath: string().nullable()
22804
+ pythonPath: string().nullable(),
22805
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22679
22806
  });
22680
22807
  var ModelRequirementSchema = object({
22681
22808
  modelId: string(),
@@ -23554,12 +23681,6 @@ Object.freeze({
23554
23681
  addonId: null,
23555
23682
  access: "delete"
23556
23683
  },
23557
- "addons.updateFrameworkPackage": {
23558
- capName: "addons",
23559
- capScope: "system",
23560
- addonId: null,
23561
- access: "create"
23562
- },
23563
23684
  "addons.updatePackage": {
23564
23685
  capName: "addons",
23565
23686
  capScope: "system",
@@ -26332,12 +26453,6 @@ Object.freeze({
26332
26453
  addonId: null,
26333
26454
  access: "view"
26334
26455
  },
26335
- "pipelineExecutor.reprobeEngine": {
26336
- capName: "pipeline-executor",
26337
- capScope: "system",
26338
- addonId: null,
26339
- access: "create"
26340
- },
26341
26456
  "pipelineExecutor.runAudioTest": {
26342
26457
  capName: "pipeline-executor",
26343
26458
  capScope: "system",
@@ -26488,6 +26603,12 @@ Object.freeze({
26488
26603
  addonId: null,
26489
26604
  access: "view"
26490
26605
  },
26606
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26607
+ capName: "pipeline-orchestrator",
26608
+ capScope: "system",
26609
+ addonId: null,
26610
+ access: "view"
26611
+ },
26491
26612
  "pipelineOrchestrator.getPipelineAssignment": {
26492
26613
  capName: "pipeline-orchestrator",
26493
26614
  capScope: "system",
@@ -26500,6 +26621,12 @@ Object.freeze({
26500
26621
  addonId: null,
26501
26622
  access: "view"
26502
26623
  },
26624
+ "pipelineOrchestrator.getPipelineDevicePin": {
26625
+ capName: "pipeline-orchestrator",
26626
+ capScope: "system",
26627
+ addonId: null,
26628
+ access: "view"
26629
+ },
26503
26630
  "pipelineOrchestrator.listAgentSettings": {
26504
26631
  capName: "pipeline-orchestrator",
26505
26632
  capScope: "system",
@@ -26542,19 +26669,19 @@ Object.freeze({
26542
26669
  addonId: null,
26543
26670
  access: "create"
26544
26671
  },
26545
- "pipelineOrchestrator.setAgentAddonDefaults": {
26672
+ "pipelineOrchestrator.setAgentCapabilities": {
26546
26673
  capName: "pipeline-orchestrator",
26547
26674
  capScope: "system",
26548
26675
  addonId: null,
26549
26676
  access: "create"
26550
26677
  },
26551
- "pipelineOrchestrator.setAgentCapabilities": {
26678
+ "pipelineOrchestrator.setAgentDetectWeight": {
26552
26679
  capName: "pipeline-orchestrator",
26553
26680
  capScope: "system",
26554
26681
  addonId: null,
26555
26682
  access: "create"
26556
26683
  },
26557
- "pipelineOrchestrator.setAgentDetectWeight": {
26684
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26558
26685
  capName: "pipeline-orchestrator",
26559
26686
  capScope: "system",
26560
26687
  addonId: null,
@@ -26596,6 +26723,12 @@ Object.freeze({
26596
26723
  addonId: null,
26597
26724
  access: "create"
26598
26725
  },
26726
+ "pipelineOrchestrator.setPipelineDevicePin": {
26727
+ capName: "pipeline-orchestrator",
26728
+ capScope: "system",
26729
+ addonId: null,
26730
+ access: "create"
26731
+ },
26599
26732
  "pipelineOrchestrator.unassignAudio": {
26600
26733
  capName: "pipeline-orchestrator",
26601
26734
  capScope: "system",
@@ -28148,32 +28281,6 @@ Object.freeze({
28148
28281
  "network-access": "ingress",
28149
28282
  "smtp-provider": "email"
28150
28283
  });
28151
- var frameworkSwapPackageSchema = object({
28152
- name: string(),
28153
- stagedPath: string(),
28154
- backupPath: string(),
28155
- toVersion: string(),
28156
- fromVersion: string().nullable()
28157
- });
28158
- object({
28159
- jobId: string(),
28160
- taskId: string(),
28161
- packages: array(frameworkSwapPackageSchema),
28162
- requestedAtMs: number(),
28163
- schemaVersion: literal(1)
28164
- });
28165
- object({
28166
- jobId: string(),
28167
- taskId: string(),
28168
- backups: array(object({
28169
- name: string(),
28170
- backupPath: string(),
28171
- livePath: string()
28172
- })),
28173
- appliedAtMs: number(),
28174
- bootAttempts: number(),
28175
- schemaVersion: literal(1)
28176
- });
28177
28284
  //#endregion
28178
28285
  //#region src/config.ts
28179
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). */
@@ -12624,7 +12662,21 @@ var NativeCropResultSchema = object({
12624
12662
  /** Packed rgb (24-bit) pixels of the crop. */
12625
12663
  bytes: _instanceof(Uint8Array),
12626
12664
  width: number().int().positive(),
12627
- height: number().int().positive()
12665
+ height: number().int().positive(),
12666
+ /**
12667
+ * Which source served this crop, so a quality-sensitive consumer (the native
12668
+ * `keyFrame`) can reject a degraded fallback:
12669
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12670
+ * quality path).
12671
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12672
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12673
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12674
+ *
12675
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12676
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12677
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12678
+ */
12679
+ tier: _enum(["native", "ram-fullframe"]).optional()
12628
12680
  });
12629
12681
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12630
12682
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12864,7 +12916,14 @@ var RunnerCameraConfigSchema = object({
12864
12916
  * camera's detect node differs from its source-owner (P2d, gated by the
12865
12917
  * `remoteSourcingNodes` rollout setting).
12866
12918
  */
12867
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12919
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12920
+ /**
12921
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12922
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12923
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12924
+ * this only selects WHICH device pool of that node runs the session.
12925
+ */
12926
+ deviceKey: string().optional()
12868
12927
  });
12869
12928
  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
12929
  /**
@@ -12885,6 +12944,19 @@ var RunnerLocalLoadSchema = object({
12885
12944
  avgInferenceTimeMs: number(),
12886
12945
  /** Total queue depth across motion + detection queues. */
12887
12946
  queueDepthTotal: number(),
12947
+ /**
12948
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12949
+ * this runner currently has attached cameras on, so the orchestrator's second
12950
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12951
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12952
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12953
+ */
12954
+ devices: array(object({
12955
+ deviceKey: string(),
12956
+ backend: string(),
12957
+ attachedCameras: number(),
12958
+ queueDepthTotal: number()
12959
+ })).default([]),
12888
12960
  /** Hardware capability flags reported by this node. */
12889
12961
  hardware: object({
12890
12962
  hasGpu: boolean(),
@@ -16033,6 +16105,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16033
16105
  return toDeviceSummary(device, this.addonId);
16034
16106
  }
16035
16107
  };
16108
+ 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;
16109
+ new Set(Object.values(DeviceType));
16036
16110
  /**
16037
16111
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16038
16112
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17606,7 +17680,8 @@ var LinkedDeviceSchema = object({
17606
17680
  deviceId: number(),
17607
17681
  name: string(),
17608
17682
  location: string().nullable(),
17609
- features: array(string())
17683
+ features: array(string()),
17684
+ producesTrackedEvents: boolean().optional()
17610
17685
  });
17611
17686
  var SavedDeviceRowSchema = object({
17612
17687
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19236,6 +19311,7 @@ var TrackSchema = object({
19236
19311
  deviceId: number(),
19237
19312
  className: string(),
19238
19313
  label: string().optional(),
19314
+ producingDeviceName: string().optional(),
19239
19315
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19240
19316
  source: TrackSourceSchema.optional(),
19241
19317
  firstSeen: number(),
@@ -19373,7 +19449,8 @@ var MediaFileKindEnum = _enum([
19373
19449
  "fullFrameBoxed",
19374
19450
  "faceCrop",
19375
19451
  "plateCrop",
19376
- "keyFrame"
19452
+ "keyFrame",
19453
+ "keyFrameSmall"
19377
19454
  ]);
19378
19455
  var MediaFileSchema = object({
19379
19456
  key: string(),
@@ -19702,13 +19779,11 @@ var PipelineTemplateSchema = object({
19702
19779
  createdAt: string(),
19703
19780
  updatedAt: string()
19704
19781
  });
19705
- var AgentAddonConfigSchema = object({
19706
- enabled: boolean(),
19782
+ var DeviceStepConfigSchema = object({
19707
19783
  modelId: string().optional(),
19708
- settings: record(string(), unknown()).readonly()
19784
+ settings: record(string(), unknown()).optional()
19709
19785
  });
19710
19786
  var AgentPipelineSettingsSchema = object({
19711
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19712
19787
  maxCameras: number().int().nonnegative().nullable().default(null),
19713
19788
  /** Per-node detection weight (relative share for the quota balancer). */
19714
19789
  detectWeight: number().positive().optional(),
@@ -19732,7 +19807,22 @@ var AgentPipelineSettingsSchema = object({
19732
19807
  * it already uses to reach the hub). Set this only when the auto-detected
19733
19808
  * address is wrong (multi-homed host, NAT, custom interface).
19734
19809
  */
19735
- reachableHost: string().optional()
19810
+ reachableHost: string().optional(),
19811
+ /**
19812
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19813
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19814
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19815
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19816
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19817
+ * the default model/settings for every camera landing on that accelerator;
19818
+ * a stepId absent ⇒ the step uses that device's format default.
19819
+ */
19820
+ inferenceDevices: record(string(), object({
19821
+ enabled: boolean(),
19822
+ weight: number().positive().optional(),
19823
+ maxSessions: number().int().positive().optional(),
19824
+ steps: record(string(), DeviceStepConfigSchema).optional()
19825
+ })).optional()
19736
19826
  });
19737
19827
  var CameraPipelineForAgentSchema = object({
19738
19828
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19742,14 +19832,13 @@ var CameraPipelineForAgentSchema = object({
19742
19832
  }).nullable()
19743
19833
  });
19744
19834
  var CameraStepOverridePatchSchema = object({
19745
- enabled: boolean().optional(),
19746
19835
  modelId: string().optional(),
19747
19836
  settings: record(string(), unknown()).readonly().optional()
19748
19837
  });
19749
19838
  var CameraPipelineSettingsSchema = object({
19750
19839
  pinnedAgentNodeId: string().optional(),
19751
19840
  stepToggles: record(string(), boolean()).optional(),
19752
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19841
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19753
19842
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19754
19843
  });
19755
19844
  /**
@@ -19963,6 +20052,44 @@ var CameraStatusSchema = object({
19963
20052
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19964
20053
  fetchedAt: number()
19965
20054
  });
20055
+ var NodeInferenceDeviceSchema = object({
20056
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20057
+ key: string(),
20058
+ backend: string(),
20059
+ device: string(),
20060
+ format: _enum(MODEL_FORMATS),
20061
+ /** Whether the node's live probe reports the device as usable right now. */
20062
+ available: boolean(),
20063
+ /**
20064
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20065
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20066
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20067
+ * not a balanced target). An explicit stored value always wins; a stored-only
20068
+ * (unavailable) key keeps its stored value.
20069
+ */
20070
+ enabled: boolean(),
20071
+ /** Relative balancer weight for the enabled device (default 1). */
20072
+ weight: number(),
20073
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20074
+ maxSessions: number().nullable(),
20075
+ /** Object-detection model the executor defaults to for this deviceKey. */
20076
+ defaultModelId: string(),
20077
+ /**
20078
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20079
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20080
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20081
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20082
+ * available per format; this is the stored selection that becomes the
20083
+ * default for EVERY camera landing on this accelerator.
20084
+ */
20085
+ steps: record(string(), DeviceStepConfigSchema).optional()
20086
+ });
20087
+ var NodeInferenceDevicesSchema = object({
20088
+ nodeId: string(),
20089
+ /** False when the node's platform-probe was unreachable (no live device set). */
20090
+ reachable: boolean(),
20091
+ devices: array(NodeInferenceDeviceSchema).readonly()
20092
+ });
19966
20093
  method(object({
19967
20094
  deviceId: number(),
19968
20095
  agentNodeId: string()
@@ -19972,7 +20099,13 @@ method(object({
19972
20099
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19973
20100
  kind: "mutation",
19974
20101
  auth: "admin"
19975
- }), method(_void(), object({ migrated: number() }), {
20102
+ }), method(object({
20103
+ deviceId: number(),
20104
+ deviceKey: string()
20105
+ }), object({ success: literal(true) }), {
20106
+ kind: "mutation",
20107
+ auth: "admin"
20108
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19976
20109
  kind: "mutation",
19977
20110
  auth: "admin"
19978
20111
  }), 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 +20139,7 @@ method(object({
20006
20139
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20007
20140
  nodeId: string(),
20008
20141
  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({
20142
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20016
20143
  success: boolean(),
20017
20144
  removed: boolean()
20018
20145
  }), {
@@ -20044,7 +20171,18 @@ method(object({
20044
20171
  }), object({ success: literal(true) }), {
20045
20172
  kind: "mutation",
20046
20173
  auth: "admin"
20047
- }), method(object({ agentNodeId: string() }), object({
20174
+ }), method(object({
20175
+ agentNodeId: string(),
20176
+ inferenceDevices: record(string(), object({
20177
+ enabled: boolean(),
20178
+ weight: number().positive().optional(),
20179
+ maxSessions: number().int().positive().optional(),
20180
+ steps: record(string(), DeviceStepConfigSchema).optional()
20181
+ }))
20182
+ }), object({ success: literal(true) }), {
20183
+ kind: "mutation",
20184
+ auth: "admin"
20185
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20048
20186
  success: literal(true),
20049
20187
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20050
20188
  effectiveModelId: string().nullable(),
@@ -20060,9 +20198,10 @@ method(object({
20060
20198
  }), object({ success: literal(true) }), {
20061
20199
  kind: "mutation",
20062
20200
  auth: "admin"
20063
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20201
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20064
20202
  deviceId: number(),
20065
20203
  agentNodeId: string(),
20204
+ deviceKey: string(),
20066
20205
  addonId: string(),
20067
20206
  patch: CameraStepOverridePatchSchema.nullable()
20068
20207
  }), object({ success: literal(true) }), {
@@ -20099,14 +20238,13 @@ method(object({
20099
20238
  });
20100
20239
  /**
20101
20240
  * server-management — per-NODE singleton capability for a node's ROOT
20102
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20103
- * agents).
20241
+ * package lifecycle (runtime-updatable node packages).
20104
20242
  *
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).
20243
+ * Every node role runs the SAME root package (`@camstack/server`), which
20244
+ * carries the whole software stack in its npm dep tree, so ONE version
20245
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20246
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20247
+ * no auto-rollback).
20110
20248
  *
20111
20249
  * Providers:
20112
20250
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20214,7 +20352,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20214
20352
  /** Explicit target version; omitted = latest from the registry. */
20215
20353
  version: string().optional() }), ServerUpdateActionResultSchema, {
20216
20354
  kind: "mutation",
20217
- auth: "admin"
20355
+ auth: "admin",
20356
+ timeoutMs: 16 * 6e4
20218
20357
  }), method(_void(), ServerUpdateActionResultSchema, {
20219
20358
  kind: "mutation",
20220
20359
  auth: "admin"
@@ -21258,22 +21397,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21258
21397
  var RestartAddonResultSchema = unknown();
21259
21398
  var InstallPackageResultSchema = unknown();
21260
21399
  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
21400
  var BulkUpdateItemStatusSchema = _enum([
21278
21401
  "queued",
21279
21402
  "updating",
@@ -21401,13 +21524,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21401
21524
  }), object({ success: literal(true) }), {
21402
21525
  kind: "mutation",
21403
21526
  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
21527
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21412
21528
  kind: "mutation",
21413
21529
  auth: "admin"
@@ -22273,10 +22389,10 @@ var TopologyCategorySchema = object({
22273
22389
  addons: array(TopologyCategoryAddonSchema).readonly()
22274
22390
  });
22275
22391
  /**
22276
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22277
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22278
- * version visibility for the Server management surface. Nullable: offline
22279
- * 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.
22280
22396
  */
22281
22397
  var TopologyRootPackageSchema = object({
22282
22398
  name: string(),
@@ -22664,17 +22780,28 @@ var PlatformScoreSchema = object({
22664
22780
  format: _enum([
22665
22781
  "onnx",
22666
22782
  "coreml",
22667
- "openvino"
22783
+ "openvino",
22784
+ "tflite"
22668
22785
  ]),
22669
22786
  score: number(),
22670
22787
  reason: string(),
22671
22788
  available: boolean()
22672
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
+ });
22673
22799
  var PlatformCapabilitiesSchema = object({
22674
22800
  hardware: HardwareInfoSchema,
22675
22801
  scores: array(PlatformScoreSchema).readonly(),
22676
22802
  bestScore: PlatformScoreSchema,
22677
- pythonPath: string().nullable()
22803
+ pythonPath: string().nullable(),
22804
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22678
22805
  });
22679
22806
  var ModelRequirementSchema = object({
22680
22807
  modelId: string(),
@@ -23553,12 +23680,6 @@ Object.freeze({
23553
23680
  addonId: null,
23554
23681
  access: "delete"
23555
23682
  },
23556
- "addons.updateFrameworkPackage": {
23557
- capName: "addons",
23558
- capScope: "system",
23559
- addonId: null,
23560
- access: "create"
23561
- },
23562
23683
  "addons.updatePackage": {
23563
23684
  capName: "addons",
23564
23685
  capScope: "system",
@@ -26331,12 +26452,6 @@ Object.freeze({
26331
26452
  addonId: null,
26332
26453
  access: "view"
26333
26454
  },
26334
- "pipelineExecutor.reprobeEngine": {
26335
- capName: "pipeline-executor",
26336
- capScope: "system",
26337
- addonId: null,
26338
- access: "create"
26339
- },
26340
26455
  "pipelineExecutor.runAudioTest": {
26341
26456
  capName: "pipeline-executor",
26342
26457
  capScope: "system",
@@ -26487,6 +26602,12 @@ Object.freeze({
26487
26602
  addonId: null,
26488
26603
  access: "view"
26489
26604
  },
26605
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26606
+ capName: "pipeline-orchestrator",
26607
+ capScope: "system",
26608
+ addonId: null,
26609
+ access: "view"
26610
+ },
26490
26611
  "pipelineOrchestrator.getPipelineAssignment": {
26491
26612
  capName: "pipeline-orchestrator",
26492
26613
  capScope: "system",
@@ -26499,6 +26620,12 @@ Object.freeze({
26499
26620
  addonId: null,
26500
26621
  access: "view"
26501
26622
  },
26623
+ "pipelineOrchestrator.getPipelineDevicePin": {
26624
+ capName: "pipeline-orchestrator",
26625
+ capScope: "system",
26626
+ addonId: null,
26627
+ access: "view"
26628
+ },
26502
26629
  "pipelineOrchestrator.listAgentSettings": {
26503
26630
  capName: "pipeline-orchestrator",
26504
26631
  capScope: "system",
@@ -26541,19 +26668,19 @@ Object.freeze({
26541
26668
  addonId: null,
26542
26669
  access: "create"
26543
26670
  },
26544
- "pipelineOrchestrator.setAgentAddonDefaults": {
26671
+ "pipelineOrchestrator.setAgentCapabilities": {
26545
26672
  capName: "pipeline-orchestrator",
26546
26673
  capScope: "system",
26547
26674
  addonId: null,
26548
26675
  access: "create"
26549
26676
  },
26550
- "pipelineOrchestrator.setAgentCapabilities": {
26677
+ "pipelineOrchestrator.setAgentDetectWeight": {
26551
26678
  capName: "pipeline-orchestrator",
26552
26679
  capScope: "system",
26553
26680
  addonId: null,
26554
26681
  access: "create"
26555
26682
  },
26556
- "pipelineOrchestrator.setAgentDetectWeight": {
26683
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26557
26684
  capName: "pipeline-orchestrator",
26558
26685
  capScope: "system",
26559
26686
  addonId: null,
@@ -26595,6 +26722,12 @@ Object.freeze({
26595
26722
  addonId: null,
26596
26723
  access: "create"
26597
26724
  },
26725
+ "pipelineOrchestrator.setPipelineDevicePin": {
26726
+ capName: "pipeline-orchestrator",
26727
+ capScope: "system",
26728
+ addonId: null,
26729
+ access: "create"
26730
+ },
26598
26731
  "pipelineOrchestrator.unassignAudio": {
26599
26732
  capName: "pipeline-orchestrator",
26600
26733
  capScope: "system",
@@ -28147,32 +28280,6 @@ Object.freeze({
28147
28280
  "network-access": "ingress",
28148
28281
  "smtp-provider": "email"
28149
28282
  });
28150
- var frameworkSwapPackageSchema = object({
28151
- name: string(),
28152
- stagedPath: string(),
28153
- backupPath: string(),
28154
- toVersion: string(),
28155
- fromVersion: string().nullable()
28156
- });
28157
- object({
28158
- jobId: string(),
28159
- taskId: string(),
28160
- packages: array(frameworkSwapPackageSchema),
28161
- requestedAtMs: number(),
28162
- schemaVersion: literal(1)
28163
- });
28164
- object({
28165
- jobId: string(),
28166
- taskId: string(),
28167
- backups: array(object({
28168
- name: string(),
28169
- backupPath: string(),
28170
- livePath: string()
28171
- })),
28172
- appliedAtMs: number(),
28173
- bootAttempts: number(),
28174
- schemaVersion: literal(1)
28175
- });
28176
28283
  //#endregion
28177
28284
  //#region src/config.ts
28178
28285
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-vesync",
3
- "version": "0.1.14",
3
+ "version": "0.2.1",
4
4
  "description": "VeSync cloud-account device-provider addon for CamStack — Levoit / Cosori air purifiers as Fan devices (power, speed, preset, child-lock, display, air-quality + filter-life sensors)",
5
5
  "keywords": [
6
6
  "camstack",