@camstack/addon-provider-amcrest 0.1.15 → 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
@@ -7077,6 +7077,17 @@ var ModelCatalogEntrySchema = object({
7077
7077
  "imagenet",
7078
7078
  "none"
7079
7079
  ]).optional(),
7080
+ /**
7081
+ * The model already applies softmax IN-GRAPH — its raw output is a
7082
+ * probability distribution, not logits. When set, the `softmax`
7083
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7084
+ * probability vector collapses it toward uniform (top-1 score craters far
7085
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7086
+ * the output is raw logits and the postprocessor applies softmax (the normal
7087
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7088
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7089
+ */
7090
+ outputProbabilities: boolean().optional(),
7080
7091
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7081
7092
  /**
7082
7093
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12457,10 +12468,7 @@ var ConfigUISchemaNullableBridge = custom();
12457
12468
  var InferenceCapabilitiesBridge = custom();
12458
12469
  var ModelAvailabilityListBridge = custom();
12459
12470
  var PipelineRunResultBridge = custom();
12460
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12461
- kind: "mutation",
12462
- auth: "admin"
12463
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12471
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12464
12472
  modelId: string(),
12465
12473
  settings: record(string(), unknown()).readonly()
12466
12474
  }))), method(object({ steps: record(string(), object({
@@ -12520,13 +12528,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12520
12528
  * (inputClasses ≠ null) are skipped and served per-track via
12521
12529
  * pipelineRunner.runDetailSubtree (two-plane design).
12522
12530
  */
12523
- plane: _enum(["full", "frame"]).optional()
12531
+ plane: _enum(["full", "frame"]).optional(),
12532
+ /**
12533
+ * Inference-device selector (Phase 2 multi-device). Format
12534
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12535
+ * Omitted ⇒ the runner's default device (current single-engine
12536
+ * behaviour). Selects WHICH device pool of the node runs the call.
12537
+ */
12538
+ deviceKey: string().optional()
12524
12539
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12525
12540
  engine: PipelineEngineChoiceSchema.optional(),
12526
12541
  steps: array(PipelineStepInputSchema).min(1),
12527
12542
  frames: array(FrameInputSchema).min(1).max(255),
12528
12543
  deviceId: number().optional(),
12529
- sessionId: string().optional()
12544
+ sessionId: string().optional(),
12545
+ /**
12546
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12547
+ * the batch to the Python pool's bench preprocess cache
12548
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12549
+ * preprocessed ONCE and every later inference is a pure-inference cache
12550
+ * hit — the sustained-throughput run measures inference, not
12551
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12552
+ * full preprocess every call, correct). Fresh per sustained run;
12553
+ * released via `uncacheFrame`.
12554
+ */
12555
+ frameId: number().int().nonnegative().optional(),
12556
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12557
+ deviceKey: string().optional()
12530
12558
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12531
12559
  data: _instanceof(Uint8Array),
12532
12560
  width: number().int().positive(),
@@ -12558,8 +12586,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12558
12586
  * - `runtime` — main camera-serving engine (no idle TTL).
12559
12587
  * - `warm-override` — benchmark/test override held in the warm
12560
12588
  * cache; auto-disposed after the idle TTL.
12589
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12590
+ * multi-device, keyed by `deviceKey`) resolved
12591
+ * via `resolveDeviceFactory`. Runs alongside the
12592
+ * `runtime` engine on a DIFFERENT accelerator
12593
+ * (NPU / iGPU / Coral) — this is how the
12594
+ * Engines tab shows all pools running at once.
12561
12595
  */
12562
- kind: _enum(["runtime", "warm-override"]),
12596
+ kind: _enum([
12597
+ "runtime",
12598
+ "warm-override",
12599
+ "device-pool"
12600
+ ]),
12563
12601
  /** Native pid of the underlying Python pool (null when no pool). */
12564
12602
  poolPid: number().nullable(),
12565
12603
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12934,7 +12972,14 @@ var RunnerCameraConfigSchema = object({
12934
12972
  * camera's detect node differs from its source-owner (P2d, gated by the
12935
12973
  * `remoteSourcingNodes` rollout setting).
12936
12974
  */
12937
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12975
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12976
+ /**
12977
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12978
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12979
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12980
+ * this only selects WHICH device pool of that node runs the session.
12981
+ */
12982
+ deviceKey: string().optional()
12938
12983
  });
12939
12984
  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;
12940
12985
  /**
@@ -12955,6 +13000,19 @@ var RunnerLocalLoadSchema = object({
12955
13000
  avgInferenceTimeMs: number(),
12956
13001
  /** Total queue depth across motion + detection queues. */
12957
13002
  queueDepthTotal: number(),
13003
+ /**
13004
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13005
+ * this runner currently has attached cameras on, so the orchestrator's second
13006
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13007
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13008
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13009
+ */
13010
+ devices: array(object({
13011
+ deviceKey: string(),
13012
+ backend: string(),
13013
+ attachedCameras: number(),
13014
+ queueDepthTotal: number()
13015
+ })).default([]),
12958
13016
  /** Hardware capability flags reported by this node. */
12959
13017
  hardware: object({
12960
13018
  hasGpu: boolean(),
@@ -16103,6 +16161,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16103
16161
  return toDeviceSummary(device, this.addonId);
16104
16162
  }
16105
16163
  };
16164
+ 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;
16165
+ new Set(Object.values(DeviceType));
16106
16166
  /**
16107
16167
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16108
16168
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19772,13 +19832,11 @@ var PipelineTemplateSchema = object({
19772
19832
  createdAt: string(),
19773
19833
  updatedAt: string()
19774
19834
  });
19775
- var AgentAddonConfigSchema = object({
19776
- enabled: boolean(),
19835
+ var DeviceStepConfigSchema = object({
19777
19836
  modelId: string().optional(),
19778
- settings: record(string(), unknown()).readonly()
19837
+ settings: record(string(), unknown()).optional()
19779
19838
  });
19780
19839
  var AgentPipelineSettingsSchema = object({
19781
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19782
19840
  maxCameras: number().int().nonnegative().nullable().default(null),
19783
19841
  /** Per-node detection weight (relative share for the quota balancer). */
19784
19842
  detectWeight: number().positive().optional(),
@@ -19802,7 +19860,22 @@ var AgentPipelineSettingsSchema = object({
19802
19860
  * it already uses to reach the hub). Set this only when the auto-detected
19803
19861
  * address is wrong (multi-homed host, NAT, custom interface).
19804
19862
  */
19805
- reachableHost: string().optional()
19863
+ reachableHost: string().optional(),
19864
+ /**
19865
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19866
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19867
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19868
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19869
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19870
+ * the default model/settings for every camera landing on that accelerator;
19871
+ * a stepId absent ⇒ the step uses that device's format default.
19872
+ */
19873
+ inferenceDevices: record(string(), object({
19874
+ enabled: boolean(),
19875
+ weight: number().positive().optional(),
19876
+ maxSessions: number().int().positive().optional(),
19877
+ steps: record(string(), DeviceStepConfigSchema).optional()
19878
+ })).optional()
19806
19879
  });
19807
19880
  var CameraPipelineForAgentSchema = object({
19808
19881
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19812,14 +19885,13 @@ var CameraPipelineForAgentSchema = object({
19812
19885
  }).nullable()
19813
19886
  });
19814
19887
  var CameraStepOverridePatchSchema = object({
19815
- enabled: boolean().optional(),
19816
19888
  modelId: string().optional(),
19817
19889
  settings: record(string(), unknown()).readonly().optional()
19818
19890
  });
19819
19891
  var CameraPipelineSettingsSchema = object({
19820
19892
  pinnedAgentNodeId: string().optional(),
19821
19893
  stepToggles: record(string(), boolean()).optional(),
19822
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19894
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19823
19895
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19824
19896
  });
19825
19897
  /**
@@ -20033,6 +20105,44 @@ var CameraStatusSchema = object({
20033
20105
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20034
20106
  fetchedAt: number()
20035
20107
  });
20108
+ var NodeInferenceDeviceSchema = object({
20109
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20110
+ key: string(),
20111
+ backend: string(),
20112
+ device: string(),
20113
+ format: _enum(MODEL_FORMATS),
20114
+ /** Whether the node's live probe reports the device as usable right now. */
20115
+ available: boolean(),
20116
+ /**
20117
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20118
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20119
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20120
+ * not a balanced target). An explicit stored value always wins; a stored-only
20121
+ * (unavailable) key keeps its stored value.
20122
+ */
20123
+ enabled: boolean(),
20124
+ /** Relative balancer weight for the enabled device (default 1). */
20125
+ weight: number(),
20126
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20127
+ maxSessions: number().nullable(),
20128
+ /** Object-detection model the executor defaults to for this deviceKey. */
20129
+ defaultModelId: string(),
20130
+ /**
20131
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20132
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20133
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20134
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20135
+ * available per format; this is the stored selection that becomes the
20136
+ * default for EVERY camera landing on this accelerator.
20137
+ */
20138
+ steps: record(string(), DeviceStepConfigSchema).optional()
20139
+ });
20140
+ var NodeInferenceDevicesSchema = object({
20141
+ nodeId: string(),
20142
+ /** False when the node's platform-probe was unreachable (no live device set). */
20143
+ reachable: boolean(),
20144
+ devices: array(NodeInferenceDeviceSchema).readonly()
20145
+ });
20036
20146
  method(object({
20037
20147
  deviceId: number(),
20038
20148
  agentNodeId: string()
@@ -20042,7 +20152,13 @@ method(object({
20042
20152
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20043
20153
  kind: "mutation",
20044
20154
  auth: "admin"
20045
- }), method(_void(), object({ migrated: number() }), {
20155
+ }), method(object({
20156
+ deviceId: number(),
20157
+ deviceKey: string()
20158
+ }), object({ success: literal(true) }), {
20159
+ kind: "mutation",
20160
+ auth: "admin"
20161
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20046
20162
  kind: "mutation",
20047
20163
  auth: "admin"
20048
20164
  }), 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({
@@ -20076,13 +20192,7 @@ method(object({
20076
20192
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20077
20193
  nodeId: string(),
20078
20194
  settings: AgentPipelineSettingsSchema
20079
- })).readonly()), method(object({
20080
- agentNodeId: string(),
20081
- defaults: record(string(), AgentAddonConfigSchema)
20082
- }), object({ success: literal(true) }), {
20083
- kind: "mutation",
20084
- auth: "admin"
20085
- }), method(object({ agentNodeId: string() }), object({
20195
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20086
20196
  success: boolean(),
20087
20197
  removed: boolean()
20088
20198
  }), {
@@ -20114,7 +20224,18 @@ method(object({
20114
20224
  }), object({ success: literal(true) }), {
20115
20225
  kind: "mutation",
20116
20226
  auth: "admin"
20117
- }), method(object({ agentNodeId: string() }), object({
20227
+ }), method(object({
20228
+ agentNodeId: string(),
20229
+ inferenceDevices: record(string(), object({
20230
+ enabled: boolean(),
20231
+ weight: number().positive().optional(),
20232
+ maxSessions: number().int().positive().optional(),
20233
+ steps: record(string(), DeviceStepConfigSchema).optional()
20234
+ }))
20235
+ }), object({ success: literal(true) }), {
20236
+ kind: "mutation",
20237
+ auth: "admin"
20238
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20118
20239
  success: literal(true),
20119
20240
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20120
20241
  effectiveModelId: string().nullable(),
@@ -20130,9 +20251,10 @@ method(object({
20130
20251
  }), object({ success: literal(true) }), {
20131
20252
  kind: "mutation",
20132
20253
  auth: "admin"
20133
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20254
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20134
20255
  deviceId: number(),
20135
20256
  agentNodeId: string(),
20257
+ deviceKey: string(),
20136
20258
  addonId: string(),
20137
20259
  patch: CameraStepOverridePatchSchema.nullable()
20138
20260
  }), object({ success: literal(true) }), {
@@ -20169,14 +20291,13 @@ method(object({
20169
20291
  });
20170
20292
  /**
20171
20293
  * server-management — per-NODE singleton capability for a node's ROOT
20172
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20173
- * agents).
20294
+ * package lifecycle (runtime-updatable node packages).
20174
20295
  *
20175
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20176
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20177
- * version describes the node. Updates install into
20178
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20179
- * starter (probation boot + auto-rollback to N-1).
20296
+ * Every node role runs the SAME root package (`@camstack/server`), which
20297
+ * carries the whole software stack in its npm dep tree, so ONE version
20298
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20299
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20300
+ * no auto-rollback).
20180
20301
  *
20181
20302
  * Providers:
20182
20303
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20284,7 +20405,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20284
20405
  /** Explicit target version; omitted = latest from the registry. */
20285
20406
  version: string().optional() }), ServerUpdateActionResultSchema, {
20286
20407
  kind: "mutation",
20287
- auth: "admin"
20408
+ auth: "admin",
20409
+ timeoutMs: 16 * 6e4
20288
20410
  }), method(_void(), ServerUpdateActionResultSchema, {
20289
20411
  kind: "mutation",
20290
20412
  auth: "admin"
@@ -21379,22 +21501,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21379
21501
  var RestartAddonResultSchema = unknown();
21380
21502
  var InstallPackageResultSchema = unknown();
21381
21503
  var ReloadPackagesResultSchema = unknown();
21382
- /**
21383
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21384
- * server restarts so the admin UI can react to the `restartingAt`
21385
- * timestamp (shows reconnect overlay). The transition from
21386
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21387
- * `system.restart-completed` event after the new process boots.
21388
- *
21389
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21390
- */
21391
- var UpdateFrameworkPackageResultSchema = object({
21392
- packageName: string(),
21393
- fromVersion: string(),
21394
- toVersion: string(),
21395
- /** Ms-epoch the server scheduled its self-restart. */
21396
- restartingAt: number()
21397
- });
21398
21504
  var BulkUpdateItemStatusSchema = _enum([
21399
21505
  "queued",
21400
21506
  "updating",
@@ -21522,13 +21628,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21522
21628
  }), object({ success: literal(true) }), {
21523
21629
  kind: "mutation",
21524
21630
  auth: "admin"
21525
- }), method(object({
21526
- packageName: string().min(1),
21527
- version: string().optional(),
21528
- deferRestart: boolean().optional()
21529
- }), UpdateFrameworkPackageResultSchema, {
21530
- kind: "mutation",
21531
- auth: "admin"
21532
21631
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21533
21632
  kind: "mutation",
21534
21633
  auth: "admin"
@@ -22394,10 +22493,10 @@ var TopologyCategorySchema = object({
22394
22493
  addons: array(TopologyCategoryAddonSchema).readonly()
22395
22494
  });
22396
22495
  /**
22397
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22398
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22399
- * version visibility for the Server management surface. Nullable: offline
22400
- * rows and pre-phase-2 nodes report none.
22496
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22497
+ * root package for every node role) as reported by its `registerNode`
22498
+ * manifest — version visibility for the Server management surface. Nullable:
22499
+ * offline rows and nodes that never reported one.
22401
22500
  */
22402
22501
  var TopologyRootPackageSchema = object({
22403
22502
  name: string(),
@@ -22831,17 +22930,28 @@ var PlatformScoreSchema = object({
22831
22930
  format: _enum([
22832
22931
  "onnx",
22833
22932
  "coreml",
22834
- "openvino"
22933
+ "openvino",
22934
+ "tflite"
22835
22935
  ]),
22836
22936
  score: number(),
22837
22937
  reason: string(),
22838
22938
  available: boolean()
22839
22939
  });
22940
+ var InferenceDeviceDescriptorSchema = object({
22941
+ key: string(),
22942
+ backend: string(),
22943
+ device: string(),
22944
+ format: ModelFormatSchema,
22945
+ runtime: literal("python"),
22946
+ score: number(),
22947
+ available: boolean()
22948
+ });
22840
22949
  var PlatformCapabilitiesSchema = object({
22841
22950
  hardware: HardwareInfoSchema,
22842
22951
  scores: array(PlatformScoreSchema).readonly(),
22843
22952
  bestScore: PlatformScoreSchema,
22844
- pythonPath: string().nullable()
22953
+ pythonPath: string().nullable(),
22954
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22845
22955
  });
22846
22956
  var ModelRequirementSchema = object({
22847
22957
  modelId: string(),
@@ -23973,12 +24083,6 @@ Object.freeze({
23973
24083
  addonId: null,
23974
24084
  access: "delete"
23975
24085
  },
23976
- "addons.updateFrameworkPackage": {
23977
- capName: "addons",
23978
- capScope: "system",
23979
- addonId: null,
23980
- access: "create"
23981
- },
23982
24086
  "addons.updatePackage": {
23983
24087
  capName: "addons",
23984
24088
  capScope: "system",
@@ -26751,12 +26855,6 @@ Object.freeze({
26751
26855
  addonId: null,
26752
26856
  access: "view"
26753
26857
  },
26754
- "pipelineExecutor.reprobeEngine": {
26755
- capName: "pipeline-executor",
26756
- capScope: "system",
26757
- addonId: null,
26758
- access: "create"
26759
- },
26760
26858
  "pipelineExecutor.runAudioTest": {
26761
26859
  capName: "pipeline-executor",
26762
26860
  capScope: "system",
@@ -26907,6 +27005,12 @@ Object.freeze({
26907
27005
  addonId: null,
26908
27006
  access: "view"
26909
27007
  },
27008
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27009
+ capName: "pipeline-orchestrator",
27010
+ capScope: "system",
27011
+ addonId: null,
27012
+ access: "view"
27013
+ },
26910
27014
  "pipelineOrchestrator.getPipelineAssignment": {
26911
27015
  capName: "pipeline-orchestrator",
26912
27016
  capScope: "system",
@@ -26919,6 +27023,12 @@ Object.freeze({
26919
27023
  addonId: null,
26920
27024
  access: "view"
26921
27025
  },
27026
+ "pipelineOrchestrator.getPipelineDevicePin": {
27027
+ capName: "pipeline-orchestrator",
27028
+ capScope: "system",
27029
+ addonId: null,
27030
+ access: "view"
27031
+ },
26922
27032
  "pipelineOrchestrator.listAgentSettings": {
26923
27033
  capName: "pipeline-orchestrator",
26924
27034
  capScope: "system",
@@ -26961,19 +27071,19 @@ Object.freeze({
26961
27071
  addonId: null,
26962
27072
  access: "create"
26963
27073
  },
26964
- "pipelineOrchestrator.setAgentAddonDefaults": {
27074
+ "pipelineOrchestrator.setAgentCapabilities": {
26965
27075
  capName: "pipeline-orchestrator",
26966
27076
  capScope: "system",
26967
27077
  addonId: null,
26968
27078
  access: "create"
26969
27079
  },
26970
- "pipelineOrchestrator.setAgentCapabilities": {
27080
+ "pipelineOrchestrator.setAgentDetectWeight": {
26971
27081
  capName: "pipeline-orchestrator",
26972
27082
  capScope: "system",
26973
27083
  addonId: null,
26974
27084
  access: "create"
26975
27085
  },
26976
- "pipelineOrchestrator.setAgentDetectWeight": {
27086
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26977
27087
  capName: "pipeline-orchestrator",
26978
27088
  capScope: "system",
26979
27089
  addonId: null,
@@ -27015,6 +27125,12 @@ Object.freeze({
27015
27125
  addonId: null,
27016
27126
  access: "create"
27017
27127
  },
27128
+ "pipelineOrchestrator.setPipelineDevicePin": {
27129
+ capName: "pipeline-orchestrator",
27130
+ capScope: "system",
27131
+ addonId: null,
27132
+ access: "create"
27133
+ },
27018
27134
  "pipelineOrchestrator.unassignAudio": {
27019
27135
  capName: "pipeline-orchestrator",
27020
27136
  capScope: "system",
@@ -28567,32 +28683,6 @@ Object.freeze({
28567
28683
  "network-access": "ingress",
28568
28684
  "smtp-provider": "email"
28569
28685
  });
28570
- var frameworkSwapPackageSchema = object({
28571
- name: string(),
28572
- stagedPath: string(),
28573
- backupPath: string(),
28574
- toVersion: string(),
28575
- fromVersion: string().nullable()
28576
- });
28577
- object({
28578
- jobId: string(),
28579
- taskId: string(),
28580
- packages: array(frameworkSwapPackageSchema),
28581
- requestedAtMs: number(),
28582
- schemaVersion: literal(1)
28583
- });
28584
- object({
28585
- jobId: string(),
28586
- taskId: string(),
28587
- backups: array(object({
28588
- name: string(),
28589
- backupPath: string(),
28590
- livePath: string()
28591
- })),
28592
- appliedAtMs: number(),
28593
- bootAttempts: number(),
28594
- schemaVersion: literal(1)
28595
- });
28596
28686
  //#endregion
28597
28687
  //#region src/schema.ts
28598
28688
  /** Addon id — must match `package.json`'s `camstack.addons[0].id`. */
package/dist/addon.mjs CHANGED
@@ -7078,6 +7078,17 @@ var ModelCatalogEntrySchema = object({
7078
7078
  "imagenet",
7079
7079
  "none"
7080
7080
  ]).optional(),
7081
+ /**
7082
+ * The model already applies softmax IN-GRAPH — its raw output is a
7083
+ * probability distribution, not logits. When set, the `softmax`
7084
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7085
+ * probability vector collapses it toward uniform (top-1 score craters far
7086
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7087
+ * the output is raw logits and the postprocessor applies softmax (the normal
7088
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7089
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7090
+ */
7091
+ outputProbabilities: boolean().optional(),
7081
7092
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7082
7093
  /**
7083
7094
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12458,10 +12469,7 @@ var ConfigUISchemaNullableBridge = custom();
12458
12469
  var InferenceCapabilitiesBridge = custom();
12459
12470
  var ModelAvailabilityListBridge = custom();
12460
12471
  var PipelineRunResultBridge = custom();
12461
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12462
- kind: "mutation",
12463
- auth: "admin"
12464
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12472
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12465
12473
  modelId: string(),
12466
12474
  settings: record(string(), unknown()).readonly()
12467
12475
  }))), method(object({ steps: record(string(), object({
@@ -12521,13 +12529,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12521
12529
  * (inputClasses ≠ null) are skipped and served per-track via
12522
12530
  * pipelineRunner.runDetailSubtree (two-plane design).
12523
12531
  */
12524
- plane: _enum(["full", "frame"]).optional()
12532
+ plane: _enum(["full", "frame"]).optional(),
12533
+ /**
12534
+ * Inference-device selector (Phase 2 multi-device). Format
12535
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12536
+ * Omitted ⇒ the runner's default device (current single-engine
12537
+ * behaviour). Selects WHICH device pool of the node runs the call.
12538
+ */
12539
+ deviceKey: string().optional()
12525
12540
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12526
12541
  engine: PipelineEngineChoiceSchema.optional(),
12527
12542
  steps: array(PipelineStepInputSchema).min(1),
12528
12543
  frames: array(FrameInputSchema).min(1).max(255),
12529
12544
  deviceId: number().optional(),
12530
- sessionId: string().optional()
12545
+ sessionId: string().optional(),
12546
+ /**
12547
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12548
+ * the batch to the Python pool's bench preprocess cache
12549
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12550
+ * preprocessed ONCE and every later inference is a pure-inference cache
12551
+ * hit — the sustained-throughput run measures inference, not
12552
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12553
+ * full preprocess every call, correct). Fresh per sustained run;
12554
+ * released via `uncacheFrame`.
12555
+ */
12556
+ frameId: number().int().nonnegative().optional(),
12557
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12558
+ deviceKey: string().optional()
12531
12559
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12532
12560
  data: _instanceof(Uint8Array),
12533
12561
  width: number().int().positive(),
@@ -12559,8 +12587,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12559
12587
  * - `runtime` — main camera-serving engine (no idle TTL).
12560
12588
  * - `warm-override` — benchmark/test override held in the warm
12561
12589
  * cache; auto-disposed after the idle TTL.
12590
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12591
+ * multi-device, keyed by `deviceKey`) resolved
12592
+ * via `resolveDeviceFactory`. Runs alongside the
12593
+ * `runtime` engine on a DIFFERENT accelerator
12594
+ * (NPU / iGPU / Coral) — this is how the
12595
+ * Engines tab shows all pools running at once.
12562
12596
  */
12563
- kind: _enum(["runtime", "warm-override"]),
12597
+ kind: _enum([
12598
+ "runtime",
12599
+ "warm-override",
12600
+ "device-pool"
12601
+ ]),
12564
12602
  /** Native pid of the underlying Python pool (null when no pool). */
12565
12603
  poolPid: number().nullable(),
12566
12604
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12935,7 +12973,14 @@ var RunnerCameraConfigSchema = object({
12935
12973
  * camera's detect node differs from its source-owner (P2d, gated by the
12936
12974
  * `remoteSourcingNodes` rollout setting).
12937
12975
  */
12938
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12976
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12977
+ /**
12978
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12979
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12980
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12981
+ * this only selects WHICH device pool of that node runs the session.
12982
+ */
12983
+ deviceKey: string().optional()
12939
12984
  });
12940
12985
  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;
12941
12986
  /**
@@ -12956,6 +13001,19 @@ var RunnerLocalLoadSchema = object({
12956
13001
  avgInferenceTimeMs: number(),
12957
13002
  /** Total queue depth across motion + detection queues. */
12958
13003
  queueDepthTotal: number(),
13004
+ /**
13005
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13006
+ * this runner currently has attached cameras on, so the orchestrator's second
13007
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13008
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13009
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13010
+ */
13011
+ devices: array(object({
13012
+ deviceKey: string(),
13013
+ backend: string(),
13014
+ attachedCameras: number(),
13015
+ queueDepthTotal: number()
13016
+ })).default([]),
12959
13017
  /** Hardware capability flags reported by this node. */
12960
13018
  hardware: object({
12961
13019
  hasGpu: boolean(),
@@ -16104,6 +16162,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16104
16162
  return toDeviceSummary(device, this.addonId);
16105
16163
  }
16106
16164
  };
16165
+ 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;
16166
+ new Set(Object.values(DeviceType));
16107
16167
  /**
16108
16168
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16109
16169
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19773,13 +19833,11 @@ var PipelineTemplateSchema = object({
19773
19833
  createdAt: string(),
19774
19834
  updatedAt: string()
19775
19835
  });
19776
- var AgentAddonConfigSchema = object({
19777
- enabled: boolean(),
19836
+ var DeviceStepConfigSchema = object({
19778
19837
  modelId: string().optional(),
19779
- settings: record(string(), unknown()).readonly()
19838
+ settings: record(string(), unknown()).optional()
19780
19839
  });
19781
19840
  var AgentPipelineSettingsSchema = object({
19782
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19783
19841
  maxCameras: number().int().nonnegative().nullable().default(null),
19784
19842
  /** Per-node detection weight (relative share for the quota balancer). */
19785
19843
  detectWeight: number().positive().optional(),
@@ -19803,7 +19861,22 @@ var AgentPipelineSettingsSchema = object({
19803
19861
  * it already uses to reach the hub). Set this only when the auto-detected
19804
19862
  * address is wrong (multi-homed host, NAT, custom interface).
19805
19863
  */
19806
- reachableHost: string().optional()
19864
+ reachableHost: string().optional(),
19865
+ /**
19866
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19867
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19868
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19869
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19870
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19871
+ * the default model/settings for every camera landing on that accelerator;
19872
+ * a stepId absent ⇒ the step uses that device's format default.
19873
+ */
19874
+ inferenceDevices: record(string(), object({
19875
+ enabled: boolean(),
19876
+ weight: number().positive().optional(),
19877
+ maxSessions: number().int().positive().optional(),
19878
+ steps: record(string(), DeviceStepConfigSchema).optional()
19879
+ })).optional()
19807
19880
  });
19808
19881
  var CameraPipelineForAgentSchema = object({
19809
19882
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19813,14 +19886,13 @@ var CameraPipelineForAgentSchema = object({
19813
19886
  }).nullable()
19814
19887
  });
19815
19888
  var CameraStepOverridePatchSchema = object({
19816
- enabled: boolean().optional(),
19817
19889
  modelId: string().optional(),
19818
19890
  settings: record(string(), unknown()).readonly().optional()
19819
19891
  });
19820
19892
  var CameraPipelineSettingsSchema = object({
19821
19893
  pinnedAgentNodeId: string().optional(),
19822
19894
  stepToggles: record(string(), boolean()).optional(),
19823
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19895
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19824
19896
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19825
19897
  });
19826
19898
  /**
@@ -20034,6 +20106,44 @@ var CameraStatusSchema = object({
20034
20106
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20035
20107
  fetchedAt: number()
20036
20108
  });
20109
+ var NodeInferenceDeviceSchema = object({
20110
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20111
+ key: string(),
20112
+ backend: string(),
20113
+ device: string(),
20114
+ format: _enum(MODEL_FORMATS),
20115
+ /** Whether the node's live probe reports the device as usable right now. */
20116
+ available: boolean(),
20117
+ /**
20118
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20119
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20120
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20121
+ * not a balanced target). An explicit stored value always wins; a stored-only
20122
+ * (unavailable) key keeps its stored value.
20123
+ */
20124
+ enabled: boolean(),
20125
+ /** Relative balancer weight for the enabled device (default 1). */
20126
+ weight: number(),
20127
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20128
+ maxSessions: number().nullable(),
20129
+ /** Object-detection model the executor defaults to for this deviceKey. */
20130
+ defaultModelId: string(),
20131
+ /**
20132
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20133
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20134
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20135
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20136
+ * available per format; this is the stored selection that becomes the
20137
+ * default for EVERY camera landing on this accelerator.
20138
+ */
20139
+ steps: record(string(), DeviceStepConfigSchema).optional()
20140
+ });
20141
+ var NodeInferenceDevicesSchema = object({
20142
+ nodeId: string(),
20143
+ /** False when the node's platform-probe was unreachable (no live device set). */
20144
+ reachable: boolean(),
20145
+ devices: array(NodeInferenceDeviceSchema).readonly()
20146
+ });
20037
20147
  method(object({
20038
20148
  deviceId: number(),
20039
20149
  agentNodeId: string()
@@ -20043,7 +20153,13 @@ method(object({
20043
20153
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20044
20154
  kind: "mutation",
20045
20155
  auth: "admin"
20046
- }), method(_void(), object({ migrated: number() }), {
20156
+ }), method(object({
20157
+ deviceId: number(),
20158
+ deviceKey: string()
20159
+ }), object({ success: literal(true) }), {
20160
+ kind: "mutation",
20161
+ auth: "admin"
20162
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20047
20163
  kind: "mutation",
20048
20164
  auth: "admin"
20049
20165
  }), 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({
@@ -20077,13 +20193,7 @@ method(object({
20077
20193
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20078
20194
  nodeId: string(),
20079
20195
  settings: AgentPipelineSettingsSchema
20080
- })).readonly()), method(object({
20081
- agentNodeId: string(),
20082
- defaults: record(string(), AgentAddonConfigSchema)
20083
- }), object({ success: literal(true) }), {
20084
- kind: "mutation",
20085
- auth: "admin"
20086
- }), method(object({ agentNodeId: string() }), object({
20196
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20087
20197
  success: boolean(),
20088
20198
  removed: boolean()
20089
20199
  }), {
@@ -20115,7 +20225,18 @@ method(object({
20115
20225
  }), object({ success: literal(true) }), {
20116
20226
  kind: "mutation",
20117
20227
  auth: "admin"
20118
- }), method(object({ agentNodeId: string() }), object({
20228
+ }), method(object({
20229
+ agentNodeId: string(),
20230
+ inferenceDevices: record(string(), object({
20231
+ enabled: boolean(),
20232
+ weight: number().positive().optional(),
20233
+ maxSessions: number().int().positive().optional(),
20234
+ steps: record(string(), DeviceStepConfigSchema).optional()
20235
+ }))
20236
+ }), object({ success: literal(true) }), {
20237
+ kind: "mutation",
20238
+ auth: "admin"
20239
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20119
20240
  success: literal(true),
20120
20241
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20121
20242
  effectiveModelId: string().nullable(),
@@ -20131,9 +20252,10 @@ method(object({
20131
20252
  }), object({ success: literal(true) }), {
20132
20253
  kind: "mutation",
20133
20254
  auth: "admin"
20134
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20255
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20135
20256
  deviceId: number(),
20136
20257
  agentNodeId: string(),
20258
+ deviceKey: string(),
20137
20259
  addonId: string(),
20138
20260
  patch: CameraStepOverridePatchSchema.nullable()
20139
20261
  }), object({ success: literal(true) }), {
@@ -20170,14 +20292,13 @@ method(object({
20170
20292
  });
20171
20293
  /**
20172
20294
  * server-management — per-NODE singleton capability for a node's ROOT
20173
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20174
- * agents).
20295
+ * package lifecycle (runtime-updatable node packages).
20175
20296
  *
20176
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20177
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20178
- * version describes the node. Updates install into
20179
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20180
- * starter (probation boot + auto-rollback to N-1).
20297
+ * Every node role runs the SAME root package (`@camstack/server`), which
20298
+ * carries the whole software stack in its npm dep tree, so ONE version
20299
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20300
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20301
+ * no auto-rollback).
20181
20302
  *
20182
20303
  * Providers:
20183
20304
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20285,7 +20406,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20285
20406
  /** Explicit target version; omitted = latest from the registry. */
20286
20407
  version: string().optional() }), ServerUpdateActionResultSchema, {
20287
20408
  kind: "mutation",
20288
- auth: "admin"
20409
+ auth: "admin",
20410
+ timeoutMs: 16 * 6e4
20289
20411
  }), method(_void(), ServerUpdateActionResultSchema, {
20290
20412
  kind: "mutation",
20291
20413
  auth: "admin"
@@ -21380,22 +21502,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21380
21502
  var RestartAddonResultSchema = unknown();
21381
21503
  var InstallPackageResultSchema = unknown();
21382
21504
  var ReloadPackagesResultSchema = unknown();
21383
- /**
21384
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21385
- * server restarts so the admin UI can react to the `restartingAt`
21386
- * timestamp (shows reconnect overlay). The transition from
21387
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21388
- * `system.restart-completed` event after the new process boots.
21389
- *
21390
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21391
- */
21392
- var UpdateFrameworkPackageResultSchema = object({
21393
- packageName: string(),
21394
- fromVersion: string(),
21395
- toVersion: string(),
21396
- /** Ms-epoch the server scheduled its self-restart. */
21397
- restartingAt: number()
21398
- });
21399
21505
  var BulkUpdateItemStatusSchema = _enum([
21400
21506
  "queued",
21401
21507
  "updating",
@@ -21523,13 +21629,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21523
21629
  }), object({ success: literal(true) }), {
21524
21630
  kind: "mutation",
21525
21631
  auth: "admin"
21526
- }), method(object({
21527
- packageName: string().min(1),
21528
- version: string().optional(),
21529
- deferRestart: boolean().optional()
21530
- }), UpdateFrameworkPackageResultSchema, {
21531
- kind: "mutation",
21532
- auth: "admin"
21533
21632
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21534
21633
  kind: "mutation",
21535
21634
  auth: "admin"
@@ -22395,10 +22494,10 @@ var TopologyCategorySchema = object({
22395
22494
  addons: array(TopologyCategoryAddonSchema).readonly()
22396
22495
  });
22397
22496
  /**
22398
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22399
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22400
- * version visibility for the Server management surface. Nullable: offline
22401
- * rows and pre-phase-2 nodes report none.
22497
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22498
+ * root package for every node role) as reported by its `registerNode`
22499
+ * manifest — version visibility for the Server management surface. Nullable:
22500
+ * offline rows and nodes that never reported one.
22402
22501
  */
22403
22502
  var TopologyRootPackageSchema = object({
22404
22503
  name: string(),
@@ -22832,17 +22931,28 @@ var PlatformScoreSchema = object({
22832
22931
  format: _enum([
22833
22932
  "onnx",
22834
22933
  "coreml",
22835
- "openvino"
22934
+ "openvino",
22935
+ "tflite"
22836
22936
  ]),
22837
22937
  score: number(),
22838
22938
  reason: string(),
22839
22939
  available: boolean()
22840
22940
  });
22941
+ var InferenceDeviceDescriptorSchema = object({
22942
+ key: string(),
22943
+ backend: string(),
22944
+ device: string(),
22945
+ format: ModelFormatSchema,
22946
+ runtime: literal("python"),
22947
+ score: number(),
22948
+ available: boolean()
22949
+ });
22841
22950
  var PlatformCapabilitiesSchema = object({
22842
22951
  hardware: HardwareInfoSchema,
22843
22952
  scores: array(PlatformScoreSchema).readonly(),
22844
22953
  bestScore: PlatformScoreSchema,
22845
- pythonPath: string().nullable()
22954
+ pythonPath: string().nullable(),
22955
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22846
22956
  });
22847
22957
  var ModelRequirementSchema = object({
22848
22958
  modelId: string(),
@@ -23974,12 +24084,6 @@ Object.freeze({
23974
24084
  addonId: null,
23975
24085
  access: "delete"
23976
24086
  },
23977
- "addons.updateFrameworkPackage": {
23978
- capName: "addons",
23979
- capScope: "system",
23980
- addonId: null,
23981
- access: "create"
23982
- },
23983
24087
  "addons.updatePackage": {
23984
24088
  capName: "addons",
23985
24089
  capScope: "system",
@@ -26752,12 +26856,6 @@ Object.freeze({
26752
26856
  addonId: null,
26753
26857
  access: "view"
26754
26858
  },
26755
- "pipelineExecutor.reprobeEngine": {
26756
- capName: "pipeline-executor",
26757
- capScope: "system",
26758
- addonId: null,
26759
- access: "create"
26760
- },
26761
26859
  "pipelineExecutor.runAudioTest": {
26762
26860
  capName: "pipeline-executor",
26763
26861
  capScope: "system",
@@ -26908,6 +27006,12 @@ Object.freeze({
26908
27006
  addonId: null,
26909
27007
  access: "view"
26910
27008
  },
27009
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27010
+ capName: "pipeline-orchestrator",
27011
+ capScope: "system",
27012
+ addonId: null,
27013
+ access: "view"
27014
+ },
26911
27015
  "pipelineOrchestrator.getPipelineAssignment": {
26912
27016
  capName: "pipeline-orchestrator",
26913
27017
  capScope: "system",
@@ -26920,6 +27024,12 @@ Object.freeze({
26920
27024
  addonId: null,
26921
27025
  access: "view"
26922
27026
  },
27027
+ "pipelineOrchestrator.getPipelineDevicePin": {
27028
+ capName: "pipeline-orchestrator",
27029
+ capScope: "system",
27030
+ addonId: null,
27031
+ access: "view"
27032
+ },
26923
27033
  "pipelineOrchestrator.listAgentSettings": {
26924
27034
  capName: "pipeline-orchestrator",
26925
27035
  capScope: "system",
@@ -26962,19 +27072,19 @@ Object.freeze({
26962
27072
  addonId: null,
26963
27073
  access: "create"
26964
27074
  },
26965
- "pipelineOrchestrator.setAgentAddonDefaults": {
27075
+ "pipelineOrchestrator.setAgentCapabilities": {
26966
27076
  capName: "pipeline-orchestrator",
26967
27077
  capScope: "system",
26968
27078
  addonId: null,
26969
27079
  access: "create"
26970
27080
  },
26971
- "pipelineOrchestrator.setAgentCapabilities": {
27081
+ "pipelineOrchestrator.setAgentDetectWeight": {
26972
27082
  capName: "pipeline-orchestrator",
26973
27083
  capScope: "system",
26974
27084
  addonId: null,
26975
27085
  access: "create"
26976
27086
  },
26977
- "pipelineOrchestrator.setAgentDetectWeight": {
27087
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26978
27088
  capName: "pipeline-orchestrator",
26979
27089
  capScope: "system",
26980
27090
  addonId: null,
@@ -27016,6 +27126,12 @@ Object.freeze({
27016
27126
  addonId: null,
27017
27127
  access: "create"
27018
27128
  },
27129
+ "pipelineOrchestrator.setPipelineDevicePin": {
27130
+ capName: "pipeline-orchestrator",
27131
+ capScope: "system",
27132
+ addonId: null,
27133
+ access: "create"
27134
+ },
27019
27135
  "pipelineOrchestrator.unassignAudio": {
27020
27136
  capName: "pipeline-orchestrator",
27021
27137
  capScope: "system",
@@ -28568,32 +28684,6 @@ Object.freeze({
28568
28684
  "network-access": "ingress",
28569
28685
  "smtp-provider": "email"
28570
28686
  });
28571
- var frameworkSwapPackageSchema = object({
28572
- name: string(),
28573
- stagedPath: string(),
28574
- backupPath: string(),
28575
- toVersion: string(),
28576
- fromVersion: string().nullable()
28577
- });
28578
- object({
28579
- jobId: string(),
28580
- taskId: string(),
28581
- packages: array(frameworkSwapPackageSchema),
28582
- requestedAtMs: number(),
28583
- schemaVersion: literal(1)
28584
- });
28585
- object({
28586
- jobId: string(),
28587
- taskId: string(),
28588
- backups: array(object({
28589
- name: string(),
28590
- backupPath: string(),
28591
- livePath: string()
28592
- })),
28593
- appliedAtMs: number(),
28594
- bootAttempts: number(),
28595
- schemaVersion: literal(1)
28596
- });
28597
28687
  //#endregion
28598
28688
  //#region src/schema.ts
28599
28689
  /** Addon id — must match `package.json`'s `camstack.addons[0].id`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-amcrest",
3
- "version": "0.1.15",
3
+ "version": "0.2.0",
4
4
  "description": "Amcrest/Dahua camera device provider addon for CamStack — Dahua CGI over HTTP(S) with digest auth (snapshot, RTSP catalog, PTZ, image/day-night config)",
5
5
  "keywords": [
6
6
  "camstack",