@camstack/addon-provider-amcrest 0.1.15 → 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
@@ -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). */
@@ -12694,7 +12732,21 @@ var NativeCropResultSchema = object({
12694
12732
  /** Packed rgb (24-bit) pixels of the crop. */
12695
12733
  bytes: _instanceof(Uint8Array),
12696
12734
  width: number().int().positive(),
12697
- height: number().int().positive()
12735
+ height: number().int().positive(),
12736
+ /**
12737
+ * Which source served this crop, so a quality-sensitive consumer (the native
12738
+ * `keyFrame`) can reject a degraded fallback:
12739
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12740
+ * quality path).
12741
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12742
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12743
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12744
+ *
12745
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12746
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12747
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12748
+ */
12749
+ tier: _enum(["native", "ram-fullframe"]).optional()
12698
12750
  });
12699
12751
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12700
12752
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12934,7 +12986,14 @@ var RunnerCameraConfigSchema = object({
12934
12986
  * camera's detect node differs from its source-owner (P2d, gated by the
12935
12987
  * `remoteSourcingNodes` rollout setting).
12936
12988
  */
12937
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12989
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12990
+ /**
12991
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12992
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12993
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12994
+ * this only selects WHICH device pool of that node runs the session.
12995
+ */
12996
+ deviceKey: string().optional()
12938
12997
  });
12939
12998
  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
12999
  /**
@@ -12955,6 +13014,19 @@ var RunnerLocalLoadSchema = object({
12955
13014
  avgInferenceTimeMs: number(),
12956
13015
  /** Total queue depth across motion + detection queues. */
12957
13016
  queueDepthTotal: number(),
13017
+ /**
13018
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13019
+ * this runner currently has attached cameras on, so the orchestrator's second
13020
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13021
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13022
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13023
+ */
13024
+ devices: array(object({
13025
+ deviceKey: string(),
13026
+ backend: string(),
13027
+ attachedCameras: number(),
13028
+ queueDepthTotal: number()
13029
+ })).default([]),
12958
13030
  /** Hardware capability flags reported by this node. */
12959
13031
  hardware: object({
12960
13032
  hasGpu: boolean(),
@@ -16103,6 +16175,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16103
16175
  return toDeviceSummary(device, this.addonId);
16104
16176
  }
16105
16177
  };
16178
+ 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;
16179
+ new Set(Object.values(DeviceType));
16106
16180
  /**
16107
16181
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16108
16182
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17676,7 +17750,8 @@ var LinkedDeviceSchema = object({
17676
17750
  deviceId: number(),
17677
17751
  name: string(),
17678
17752
  location: string().nullable(),
17679
- features: array(string())
17753
+ features: array(string()),
17754
+ producesTrackedEvents: boolean().optional()
17680
17755
  });
17681
17756
  var SavedDeviceRowSchema = object({
17682
17757
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19306,6 +19381,7 @@ var TrackSchema = object({
19306
19381
  deviceId: number(),
19307
19382
  className: string(),
19308
19383
  label: string().optional(),
19384
+ producingDeviceName: string().optional(),
19309
19385
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19310
19386
  source: TrackSourceSchema.optional(),
19311
19387
  firstSeen: number(),
@@ -19443,7 +19519,8 @@ var MediaFileKindEnum = _enum([
19443
19519
  "fullFrameBoxed",
19444
19520
  "faceCrop",
19445
19521
  "plateCrop",
19446
- "keyFrame"
19522
+ "keyFrame",
19523
+ "keyFrameSmall"
19447
19524
  ]);
19448
19525
  var MediaFileSchema = object({
19449
19526
  key: string(),
@@ -19772,13 +19849,11 @@ var PipelineTemplateSchema = object({
19772
19849
  createdAt: string(),
19773
19850
  updatedAt: string()
19774
19851
  });
19775
- var AgentAddonConfigSchema = object({
19776
- enabled: boolean(),
19852
+ var DeviceStepConfigSchema = object({
19777
19853
  modelId: string().optional(),
19778
- settings: record(string(), unknown()).readonly()
19854
+ settings: record(string(), unknown()).optional()
19779
19855
  });
19780
19856
  var AgentPipelineSettingsSchema = object({
19781
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19782
19857
  maxCameras: number().int().nonnegative().nullable().default(null),
19783
19858
  /** Per-node detection weight (relative share for the quota balancer). */
19784
19859
  detectWeight: number().positive().optional(),
@@ -19802,7 +19877,22 @@ var AgentPipelineSettingsSchema = object({
19802
19877
  * it already uses to reach the hub). Set this only when the auto-detected
19803
19878
  * address is wrong (multi-homed host, NAT, custom interface).
19804
19879
  */
19805
- reachableHost: string().optional()
19880
+ reachableHost: string().optional(),
19881
+ /**
19882
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19883
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19884
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19885
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19886
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19887
+ * the default model/settings for every camera landing on that accelerator;
19888
+ * a stepId absent ⇒ the step uses that device's format default.
19889
+ */
19890
+ inferenceDevices: record(string(), object({
19891
+ enabled: boolean(),
19892
+ weight: number().positive().optional(),
19893
+ maxSessions: number().int().positive().optional(),
19894
+ steps: record(string(), DeviceStepConfigSchema).optional()
19895
+ })).optional()
19806
19896
  });
19807
19897
  var CameraPipelineForAgentSchema = object({
19808
19898
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19812,14 +19902,13 @@ var CameraPipelineForAgentSchema = object({
19812
19902
  }).nullable()
19813
19903
  });
19814
19904
  var CameraStepOverridePatchSchema = object({
19815
- enabled: boolean().optional(),
19816
19905
  modelId: string().optional(),
19817
19906
  settings: record(string(), unknown()).readonly().optional()
19818
19907
  });
19819
19908
  var CameraPipelineSettingsSchema = object({
19820
19909
  pinnedAgentNodeId: string().optional(),
19821
19910
  stepToggles: record(string(), boolean()).optional(),
19822
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19911
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19823
19912
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19824
19913
  });
19825
19914
  /**
@@ -20033,6 +20122,44 @@ var CameraStatusSchema = object({
20033
20122
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20034
20123
  fetchedAt: number()
20035
20124
  });
20125
+ var NodeInferenceDeviceSchema = object({
20126
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20127
+ key: string(),
20128
+ backend: string(),
20129
+ device: string(),
20130
+ format: _enum(MODEL_FORMATS),
20131
+ /** Whether the node's live probe reports the device as usable right now. */
20132
+ available: boolean(),
20133
+ /**
20134
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20135
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20136
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20137
+ * not a balanced target). An explicit stored value always wins; a stored-only
20138
+ * (unavailable) key keeps its stored value.
20139
+ */
20140
+ enabled: boolean(),
20141
+ /** Relative balancer weight for the enabled device (default 1). */
20142
+ weight: number(),
20143
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20144
+ maxSessions: number().nullable(),
20145
+ /** Object-detection model the executor defaults to for this deviceKey. */
20146
+ defaultModelId: string(),
20147
+ /**
20148
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20149
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20150
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20151
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20152
+ * available per format; this is the stored selection that becomes the
20153
+ * default for EVERY camera landing on this accelerator.
20154
+ */
20155
+ steps: record(string(), DeviceStepConfigSchema).optional()
20156
+ });
20157
+ var NodeInferenceDevicesSchema = object({
20158
+ nodeId: string(),
20159
+ /** False when the node's platform-probe was unreachable (no live device set). */
20160
+ reachable: boolean(),
20161
+ devices: array(NodeInferenceDeviceSchema).readonly()
20162
+ });
20036
20163
  method(object({
20037
20164
  deviceId: number(),
20038
20165
  agentNodeId: string()
@@ -20042,7 +20169,13 @@ method(object({
20042
20169
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20043
20170
  kind: "mutation",
20044
20171
  auth: "admin"
20045
- }), method(_void(), object({ migrated: number() }), {
20172
+ }), method(object({
20173
+ deviceId: number(),
20174
+ deviceKey: string()
20175
+ }), object({ success: literal(true) }), {
20176
+ kind: "mutation",
20177
+ auth: "admin"
20178
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20046
20179
  kind: "mutation",
20047
20180
  auth: "admin"
20048
20181
  }), 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 +20209,7 @@ method(object({
20076
20209
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20077
20210
  nodeId: string(),
20078
20211
  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({
20212
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20086
20213
  success: boolean(),
20087
20214
  removed: boolean()
20088
20215
  }), {
@@ -20114,7 +20241,18 @@ method(object({
20114
20241
  }), object({ success: literal(true) }), {
20115
20242
  kind: "mutation",
20116
20243
  auth: "admin"
20117
- }), method(object({ agentNodeId: string() }), object({
20244
+ }), method(object({
20245
+ agentNodeId: string(),
20246
+ inferenceDevices: record(string(), object({
20247
+ enabled: boolean(),
20248
+ weight: number().positive().optional(),
20249
+ maxSessions: number().int().positive().optional(),
20250
+ steps: record(string(), DeviceStepConfigSchema).optional()
20251
+ }))
20252
+ }), object({ success: literal(true) }), {
20253
+ kind: "mutation",
20254
+ auth: "admin"
20255
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20118
20256
  success: literal(true),
20119
20257
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20120
20258
  effectiveModelId: string().nullable(),
@@ -20130,9 +20268,10 @@ method(object({
20130
20268
  }), object({ success: literal(true) }), {
20131
20269
  kind: "mutation",
20132
20270
  auth: "admin"
20133
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20271
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20134
20272
  deviceId: number(),
20135
20273
  agentNodeId: string(),
20274
+ deviceKey: string(),
20136
20275
  addonId: string(),
20137
20276
  patch: CameraStepOverridePatchSchema.nullable()
20138
20277
  }), object({ success: literal(true) }), {
@@ -20169,14 +20308,13 @@ method(object({
20169
20308
  });
20170
20309
  /**
20171
20310
  * server-management — per-NODE singleton capability for a node's ROOT
20172
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20173
- * agents).
20311
+ * package lifecycle (runtime-updatable node packages).
20174
20312
  *
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).
20313
+ * Every node role runs the SAME root package (`@camstack/server`), which
20314
+ * carries the whole software stack in its npm dep tree, so ONE version
20315
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20316
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20317
+ * no auto-rollback).
20180
20318
  *
20181
20319
  * Providers:
20182
20320
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20284,7 +20422,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20284
20422
  /** Explicit target version; omitted = latest from the registry. */
20285
20423
  version: string().optional() }), ServerUpdateActionResultSchema, {
20286
20424
  kind: "mutation",
20287
- auth: "admin"
20425
+ auth: "admin",
20426
+ timeoutMs: 16 * 6e4
20288
20427
  }), method(_void(), ServerUpdateActionResultSchema, {
20289
20428
  kind: "mutation",
20290
20429
  auth: "admin"
@@ -21379,22 +21518,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21379
21518
  var RestartAddonResultSchema = unknown();
21380
21519
  var InstallPackageResultSchema = unknown();
21381
21520
  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
21521
  var BulkUpdateItemStatusSchema = _enum([
21399
21522
  "queued",
21400
21523
  "updating",
@@ -21522,13 +21645,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21522
21645
  }), object({ success: literal(true) }), {
21523
21646
  kind: "mutation",
21524
21647
  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
21648
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21533
21649
  kind: "mutation",
21534
21650
  auth: "admin"
@@ -22394,10 +22510,10 @@ var TopologyCategorySchema = object({
22394
22510
  addons: array(TopologyCategoryAddonSchema).readonly()
22395
22511
  });
22396
22512
  /**
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.
22513
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22514
+ * root package for every node role) as reported by its `registerNode`
22515
+ * manifest — version visibility for the Server management surface. Nullable:
22516
+ * offline rows and nodes that never reported one.
22401
22517
  */
22402
22518
  var TopologyRootPackageSchema = object({
22403
22519
  name: string(),
@@ -22831,17 +22947,28 @@ var PlatformScoreSchema = object({
22831
22947
  format: _enum([
22832
22948
  "onnx",
22833
22949
  "coreml",
22834
- "openvino"
22950
+ "openvino",
22951
+ "tflite"
22835
22952
  ]),
22836
22953
  score: number(),
22837
22954
  reason: string(),
22838
22955
  available: boolean()
22839
22956
  });
22957
+ var InferenceDeviceDescriptorSchema = object({
22958
+ key: string(),
22959
+ backend: string(),
22960
+ device: string(),
22961
+ format: ModelFormatSchema,
22962
+ runtime: literal("python"),
22963
+ score: number(),
22964
+ available: boolean()
22965
+ });
22840
22966
  var PlatformCapabilitiesSchema = object({
22841
22967
  hardware: HardwareInfoSchema,
22842
22968
  scores: array(PlatformScoreSchema).readonly(),
22843
22969
  bestScore: PlatformScoreSchema,
22844
- pythonPath: string().nullable()
22970
+ pythonPath: string().nullable(),
22971
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22845
22972
  });
22846
22973
  var ModelRequirementSchema = object({
22847
22974
  modelId: string(),
@@ -23973,12 +24100,6 @@ Object.freeze({
23973
24100
  addonId: null,
23974
24101
  access: "delete"
23975
24102
  },
23976
- "addons.updateFrameworkPackage": {
23977
- capName: "addons",
23978
- capScope: "system",
23979
- addonId: null,
23980
- access: "create"
23981
- },
23982
24103
  "addons.updatePackage": {
23983
24104
  capName: "addons",
23984
24105
  capScope: "system",
@@ -26751,12 +26872,6 @@ Object.freeze({
26751
26872
  addonId: null,
26752
26873
  access: "view"
26753
26874
  },
26754
- "pipelineExecutor.reprobeEngine": {
26755
- capName: "pipeline-executor",
26756
- capScope: "system",
26757
- addonId: null,
26758
- access: "create"
26759
- },
26760
26875
  "pipelineExecutor.runAudioTest": {
26761
26876
  capName: "pipeline-executor",
26762
26877
  capScope: "system",
@@ -26907,6 +27022,12 @@ Object.freeze({
26907
27022
  addonId: null,
26908
27023
  access: "view"
26909
27024
  },
27025
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27026
+ capName: "pipeline-orchestrator",
27027
+ capScope: "system",
27028
+ addonId: null,
27029
+ access: "view"
27030
+ },
26910
27031
  "pipelineOrchestrator.getPipelineAssignment": {
26911
27032
  capName: "pipeline-orchestrator",
26912
27033
  capScope: "system",
@@ -26919,6 +27040,12 @@ Object.freeze({
26919
27040
  addonId: null,
26920
27041
  access: "view"
26921
27042
  },
27043
+ "pipelineOrchestrator.getPipelineDevicePin": {
27044
+ capName: "pipeline-orchestrator",
27045
+ capScope: "system",
27046
+ addonId: null,
27047
+ access: "view"
27048
+ },
26922
27049
  "pipelineOrchestrator.listAgentSettings": {
26923
27050
  capName: "pipeline-orchestrator",
26924
27051
  capScope: "system",
@@ -26961,19 +27088,19 @@ Object.freeze({
26961
27088
  addonId: null,
26962
27089
  access: "create"
26963
27090
  },
26964
- "pipelineOrchestrator.setAgentAddonDefaults": {
27091
+ "pipelineOrchestrator.setAgentCapabilities": {
26965
27092
  capName: "pipeline-orchestrator",
26966
27093
  capScope: "system",
26967
27094
  addonId: null,
26968
27095
  access: "create"
26969
27096
  },
26970
- "pipelineOrchestrator.setAgentCapabilities": {
27097
+ "pipelineOrchestrator.setAgentDetectWeight": {
26971
27098
  capName: "pipeline-orchestrator",
26972
27099
  capScope: "system",
26973
27100
  addonId: null,
26974
27101
  access: "create"
26975
27102
  },
26976
- "pipelineOrchestrator.setAgentDetectWeight": {
27103
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26977
27104
  capName: "pipeline-orchestrator",
26978
27105
  capScope: "system",
26979
27106
  addonId: null,
@@ -27015,6 +27142,12 @@ Object.freeze({
27015
27142
  addonId: null,
27016
27143
  access: "create"
27017
27144
  },
27145
+ "pipelineOrchestrator.setPipelineDevicePin": {
27146
+ capName: "pipeline-orchestrator",
27147
+ capScope: "system",
27148
+ addonId: null,
27149
+ access: "create"
27150
+ },
27018
27151
  "pipelineOrchestrator.unassignAudio": {
27019
27152
  capName: "pipeline-orchestrator",
27020
27153
  capScope: "system",
@@ -28567,32 +28700,6 @@ Object.freeze({
28567
28700
  "network-access": "ingress",
28568
28701
  "smtp-provider": "email"
28569
28702
  });
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
28703
  //#endregion
28597
28704
  //#region src/schema.ts
28598
28705
  /** 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). */
@@ -12695,7 +12733,21 @@ var NativeCropResultSchema = object({
12695
12733
  /** Packed rgb (24-bit) pixels of the crop. */
12696
12734
  bytes: _instanceof(Uint8Array),
12697
12735
  width: number().int().positive(),
12698
- height: number().int().positive()
12736
+ height: number().int().positive(),
12737
+ /**
12738
+ * Which source served this crop, so a quality-sensitive consumer (the native
12739
+ * `keyFrame`) can reject a degraded fallback:
12740
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12741
+ * quality path).
12742
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12743
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12744
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12745
+ *
12746
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12747
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12748
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12749
+ */
12750
+ tier: _enum(["native", "ram-fullframe"]).optional()
12699
12751
  });
12700
12752
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12701
12753
  * originating detection, in FRAME-space coordinates. Reuses
@@ -12935,7 +12987,14 @@ var RunnerCameraConfigSchema = object({
12935
12987
  * camera's detect node differs from its source-owner (P2d, gated by the
12936
12988
  * `remoteSourcingNodes` rollout setting).
12937
12989
  */
12938
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12990
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12991
+ /**
12992
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12993
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12994
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12995
+ * this only selects WHICH device pool of that node runs the session.
12996
+ */
12997
+ deviceKey: string().optional()
12939
12998
  });
12940
12999
  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
13000
  /**
@@ -12956,6 +13015,19 @@ var RunnerLocalLoadSchema = object({
12956
13015
  avgInferenceTimeMs: number(),
12957
13016
  /** Total queue depth across motion + detection queues. */
12958
13017
  queueDepthTotal: number(),
13018
+ /**
13019
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13020
+ * this runner currently has attached cameras on, so the orchestrator's second
13021
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13022
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13023
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13024
+ */
13025
+ devices: array(object({
13026
+ deviceKey: string(),
13027
+ backend: string(),
13028
+ attachedCameras: number(),
13029
+ queueDepthTotal: number()
13030
+ })).default([]),
12959
13031
  /** Hardware capability flags reported by this node. */
12960
13032
  hardware: object({
12961
13033
  hasGpu: boolean(),
@@ -16104,6 +16176,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16104
16176
  return toDeviceSummary(device, this.addonId);
16105
16177
  }
16106
16178
  };
16179
+ 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;
16180
+ new Set(Object.values(DeviceType));
16107
16181
  /**
16108
16182
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16109
16183
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17677,7 +17751,8 @@ var LinkedDeviceSchema = object({
17677
17751
  deviceId: number(),
17678
17752
  name: string(),
17679
17753
  location: string().nullable(),
17680
- features: array(string())
17754
+ features: array(string()),
17755
+ producesTrackedEvents: boolean().optional()
17681
17756
  });
17682
17757
  var SavedDeviceRowSchema = object({
17683
17758
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19307,6 +19382,7 @@ var TrackSchema = object({
19307
19382
  deviceId: number(),
19308
19383
  className: string(),
19309
19384
  label: string().optional(),
19385
+ producingDeviceName: string().optional(),
19310
19386
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19311
19387
  source: TrackSourceSchema.optional(),
19312
19388
  firstSeen: number(),
@@ -19444,7 +19520,8 @@ var MediaFileKindEnum = _enum([
19444
19520
  "fullFrameBoxed",
19445
19521
  "faceCrop",
19446
19522
  "plateCrop",
19447
- "keyFrame"
19523
+ "keyFrame",
19524
+ "keyFrameSmall"
19448
19525
  ]);
19449
19526
  var MediaFileSchema = object({
19450
19527
  key: string(),
@@ -19773,13 +19850,11 @@ var PipelineTemplateSchema = object({
19773
19850
  createdAt: string(),
19774
19851
  updatedAt: string()
19775
19852
  });
19776
- var AgentAddonConfigSchema = object({
19777
- enabled: boolean(),
19853
+ var DeviceStepConfigSchema = object({
19778
19854
  modelId: string().optional(),
19779
- settings: record(string(), unknown()).readonly()
19855
+ settings: record(string(), unknown()).optional()
19780
19856
  });
19781
19857
  var AgentPipelineSettingsSchema = object({
19782
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19783
19858
  maxCameras: number().int().nonnegative().nullable().default(null),
19784
19859
  /** Per-node detection weight (relative share for the quota balancer). */
19785
19860
  detectWeight: number().positive().optional(),
@@ -19803,7 +19878,22 @@ var AgentPipelineSettingsSchema = object({
19803
19878
  * it already uses to reach the hub). Set this only when the auto-detected
19804
19879
  * address is wrong (multi-homed host, NAT, custom interface).
19805
19880
  */
19806
- reachableHost: string().optional()
19881
+ reachableHost: string().optional(),
19882
+ /**
19883
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19884
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19885
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19886
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19887
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19888
+ * the default model/settings for every camera landing on that accelerator;
19889
+ * a stepId absent ⇒ the step uses that device's format default.
19890
+ */
19891
+ inferenceDevices: record(string(), object({
19892
+ enabled: boolean(),
19893
+ weight: number().positive().optional(),
19894
+ maxSessions: number().int().positive().optional(),
19895
+ steps: record(string(), DeviceStepConfigSchema).optional()
19896
+ })).optional()
19807
19897
  });
19808
19898
  var CameraPipelineForAgentSchema = object({
19809
19899
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19813,14 +19903,13 @@ var CameraPipelineForAgentSchema = object({
19813
19903
  }).nullable()
19814
19904
  });
19815
19905
  var CameraStepOverridePatchSchema = object({
19816
- enabled: boolean().optional(),
19817
19906
  modelId: string().optional(),
19818
19907
  settings: record(string(), unknown()).readonly().optional()
19819
19908
  });
19820
19909
  var CameraPipelineSettingsSchema = object({
19821
19910
  pinnedAgentNodeId: string().optional(),
19822
19911
  stepToggles: record(string(), boolean()).optional(),
19823
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19912
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19824
19913
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19825
19914
  });
19826
19915
  /**
@@ -20034,6 +20123,44 @@ var CameraStatusSchema = object({
20034
20123
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20035
20124
  fetchedAt: number()
20036
20125
  });
20126
+ var NodeInferenceDeviceSchema = object({
20127
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20128
+ key: string(),
20129
+ backend: string(),
20130
+ device: string(),
20131
+ format: _enum(MODEL_FORMATS),
20132
+ /** Whether the node's live probe reports the device as usable right now. */
20133
+ available: boolean(),
20134
+ /**
20135
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20136
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20137
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20138
+ * not a balanced target). An explicit stored value always wins; a stored-only
20139
+ * (unavailable) key keeps its stored value.
20140
+ */
20141
+ enabled: boolean(),
20142
+ /** Relative balancer weight for the enabled device (default 1). */
20143
+ weight: number(),
20144
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20145
+ maxSessions: number().nullable(),
20146
+ /** Object-detection model the executor defaults to for this deviceKey. */
20147
+ defaultModelId: string(),
20148
+ /**
20149
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20150
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20151
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20152
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20153
+ * available per format; this is the stored selection that becomes the
20154
+ * default for EVERY camera landing on this accelerator.
20155
+ */
20156
+ steps: record(string(), DeviceStepConfigSchema).optional()
20157
+ });
20158
+ var NodeInferenceDevicesSchema = object({
20159
+ nodeId: string(),
20160
+ /** False when the node's platform-probe was unreachable (no live device set). */
20161
+ reachable: boolean(),
20162
+ devices: array(NodeInferenceDeviceSchema).readonly()
20163
+ });
20037
20164
  method(object({
20038
20165
  deviceId: number(),
20039
20166
  agentNodeId: string()
@@ -20043,7 +20170,13 @@ method(object({
20043
20170
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20044
20171
  kind: "mutation",
20045
20172
  auth: "admin"
20046
- }), method(_void(), object({ migrated: number() }), {
20173
+ }), method(object({
20174
+ deviceId: number(),
20175
+ deviceKey: string()
20176
+ }), object({ success: literal(true) }), {
20177
+ kind: "mutation",
20178
+ auth: "admin"
20179
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20047
20180
  kind: "mutation",
20048
20181
  auth: "admin"
20049
20182
  }), 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 +20210,7 @@ method(object({
20077
20210
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20078
20211
  nodeId: string(),
20079
20212
  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({
20213
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20087
20214
  success: boolean(),
20088
20215
  removed: boolean()
20089
20216
  }), {
@@ -20115,7 +20242,18 @@ method(object({
20115
20242
  }), object({ success: literal(true) }), {
20116
20243
  kind: "mutation",
20117
20244
  auth: "admin"
20118
- }), method(object({ agentNodeId: string() }), object({
20245
+ }), method(object({
20246
+ agentNodeId: string(),
20247
+ inferenceDevices: record(string(), object({
20248
+ enabled: boolean(),
20249
+ weight: number().positive().optional(),
20250
+ maxSessions: number().int().positive().optional(),
20251
+ steps: record(string(), DeviceStepConfigSchema).optional()
20252
+ }))
20253
+ }), object({ success: literal(true) }), {
20254
+ kind: "mutation",
20255
+ auth: "admin"
20256
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20119
20257
  success: literal(true),
20120
20258
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20121
20259
  effectiveModelId: string().nullable(),
@@ -20131,9 +20269,10 @@ method(object({
20131
20269
  }), object({ success: literal(true) }), {
20132
20270
  kind: "mutation",
20133
20271
  auth: "admin"
20134
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20272
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20135
20273
  deviceId: number(),
20136
20274
  agentNodeId: string(),
20275
+ deviceKey: string(),
20137
20276
  addonId: string(),
20138
20277
  patch: CameraStepOverridePatchSchema.nullable()
20139
20278
  }), object({ success: literal(true) }), {
@@ -20170,14 +20309,13 @@ method(object({
20170
20309
  });
20171
20310
  /**
20172
20311
  * server-management — per-NODE singleton capability for a node's ROOT
20173
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20174
- * agents).
20312
+ * package lifecycle (runtime-updatable node packages).
20175
20313
  *
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).
20314
+ * Every node role runs the SAME root package (`@camstack/server`), which
20315
+ * carries the whole software stack in its npm dep tree, so ONE version
20316
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20317
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20318
+ * no auto-rollback).
20181
20319
  *
20182
20320
  * Providers:
20183
20321
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20285,7 +20423,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20285
20423
  /** Explicit target version; omitted = latest from the registry. */
20286
20424
  version: string().optional() }), ServerUpdateActionResultSchema, {
20287
20425
  kind: "mutation",
20288
- auth: "admin"
20426
+ auth: "admin",
20427
+ timeoutMs: 16 * 6e4
20289
20428
  }), method(_void(), ServerUpdateActionResultSchema, {
20290
20429
  kind: "mutation",
20291
20430
  auth: "admin"
@@ -21380,22 +21519,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21380
21519
  var RestartAddonResultSchema = unknown();
21381
21520
  var InstallPackageResultSchema = unknown();
21382
21521
  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
21522
  var BulkUpdateItemStatusSchema = _enum([
21400
21523
  "queued",
21401
21524
  "updating",
@@ -21523,13 +21646,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21523
21646
  }), object({ success: literal(true) }), {
21524
21647
  kind: "mutation",
21525
21648
  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
21649
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21534
21650
  kind: "mutation",
21535
21651
  auth: "admin"
@@ -22395,10 +22511,10 @@ var TopologyCategorySchema = object({
22395
22511
  addons: array(TopologyCategoryAddonSchema).readonly()
22396
22512
  });
22397
22513
  /**
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.
22514
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22515
+ * root package for every node role) as reported by its `registerNode`
22516
+ * manifest — version visibility for the Server management surface. Nullable:
22517
+ * offline rows and nodes that never reported one.
22402
22518
  */
22403
22519
  var TopologyRootPackageSchema = object({
22404
22520
  name: string(),
@@ -22832,17 +22948,28 @@ var PlatformScoreSchema = object({
22832
22948
  format: _enum([
22833
22949
  "onnx",
22834
22950
  "coreml",
22835
- "openvino"
22951
+ "openvino",
22952
+ "tflite"
22836
22953
  ]),
22837
22954
  score: number(),
22838
22955
  reason: string(),
22839
22956
  available: boolean()
22840
22957
  });
22958
+ var InferenceDeviceDescriptorSchema = object({
22959
+ key: string(),
22960
+ backend: string(),
22961
+ device: string(),
22962
+ format: ModelFormatSchema,
22963
+ runtime: literal("python"),
22964
+ score: number(),
22965
+ available: boolean()
22966
+ });
22841
22967
  var PlatformCapabilitiesSchema = object({
22842
22968
  hardware: HardwareInfoSchema,
22843
22969
  scores: array(PlatformScoreSchema).readonly(),
22844
22970
  bestScore: PlatformScoreSchema,
22845
- pythonPath: string().nullable()
22971
+ pythonPath: string().nullable(),
22972
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22846
22973
  });
22847
22974
  var ModelRequirementSchema = object({
22848
22975
  modelId: string(),
@@ -23974,12 +24101,6 @@ Object.freeze({
23974
24101
  addonId: null,
23975
24102
  access: "delete"
23976
24103
  },
23977
- "addons.updateFrameworkPackage": {
23978
- capName: "addons",
23979
- capScope: "system",
23980
- addonId: null,
23981
- access: "create"
23982
- },
23983
24104
  "addons.updatePackage": {
23984
24105
  capName: "addons",
23985
24106
  capScope: "system",
@@ -26752,12 +26873,6 @@ Object.freeze({
26752
26873
  addonId: null,
26753
26874
  access: "view"
26754
26875
  },
26755
- "pipelineExecutor.reprobeEngine": {
26756
- capName: "pipeline-executor",
26757
- capScope: "system",
26758
- addonId: null,
26759
- access: "create"
26760
- },
26761
26876
  "pipelineExecutor.runAudioTest": {
26762
26877
  capName: "pipeline-executor",
26763
26878
  capScope: "system",
@@ -26908,6 +27023,12 @@ Object.freeze({
26908
27023
  addonId: null,
26909
27024
  access: "view"
26910
27025
  },
27026
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27027
+ capName: "pipeline-orchestrator",
27028
+ capScope: "system",
27029
+ addonId: null,
27030
+ access: "view"
27031
+ },
26911
27032
  "pipelineOrchestrator.getPipelineAssignment": {
26912
27033
  capName: "pipeline-orchestrator",
26913
27034
  capScope: "system",
@@ -26920,6 +27041,12 @@ Object.freeze({
26920
27041
  addonId: null,
26921
27042
  access: "view"
26922
27043
  },
27044
+ "pipelineOrchestrator.getPipelineDevicePin": {
27045
+ capName: "pipeline-orchestrator",
27046
+ capScope: "system",
27047
+ addonId: null,
27048
+ access: "view"
27049
+ },
26923
27050
  "pipelineOrchestrator.listAgentSettings": {
26924
27051
  capName: "pipeline-orchestrator",
26925
27052
  capScope: "system",
@@ -26962,19 +27089,19 @@ Object.freeze({
26962
27089
  addonId: null,
26963
27090
  access: "create"
26964
27091
  },
26965
- "pipelineOrchestrator.setAgentAddonDefaults": {
27092
+ "pipelineOrchestrator.setAgentCapabilities": {
26966
27093
  capName: "pipeline-orchestrator",
26967
27094
  capScope: "system",
26968
27095
  addonId: null,
26969
27096
  access: "create"
26970
27097
  },
26971
- "pipelineOrchestrator.setAgentCapabilities": {
27098
+ "pipelineOrchestrator.setAgentDetectWeight": {
26972
27099
  capName: "pipeline-orchestrator",
26973
27100
  capScope: "system",
26974
27101
  addonId: null,
26975
27102
  access: "create"
26976
27103
  },
26977
- "pipelineOrchestrator.setAgentDetectWeight": {
27104
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26978
27105
  capName: "pipeline-orchestrator",
26979
27106
  capScope: "system",
26980
27107
  addonId: null,
@@ -27016,6 +27143,12 @@ Object.freeze({
27016
27143
  addonId: null,
27017
27144
  access: "create"
27018
27145
  },
27146
+ "pipelineOrchestrator.setPipelineDevicePin": {
27147
+ capName: "pipeline-orchestrator",
27148
+ capScope: "system",
27149
+ addonId: null,
27150
+ access: "create"
27151
+ },
27019
27152
  "pipelineOrchestrator.unassignAudio": {
27020
27153
  capName: "pipeline-orchestrator",
27021
27154
  capScope: "system",
@@ -28568,32 +28701,6 @@ Object.freeze({
28568
28701
  "network-access": "ingress",
28569
28702
  "smtp-provider": "email"
28570
28703
  });
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
28704
  //#endregion
28598
28705
  //#region src/schema.ts
28599
28706
  /** 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.1",
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",