camstack 1.1.32 → 1.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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-D8ZkNoYz.mjs
14525
+ // ../types/dist/sleep-BQ60XlE9.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -15389,6 +15389,17 @@ var ModelCatalogEntrySchema = external_exports.object({
15389
15389
  "imagenet",
15390
15390
  "none"
15391
15391
  ]).optional(),
15392
+ /**
15393
+ * The model already applies softmax IN-GRAPH — its raw output is a
15394
+ * probability distribution, not logits. When set, the `softmax`
15395
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
15396
+ * probability vector collapses it toward uniform (top-1 score craters far
15397
+ * below its true value, making every confidence gate meaningless). Absent ⇒
15398
+ * the output is raw logits and the postprocessor applies softmax (the normal
15399
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
15400
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
15401
+ */
15402
+ outputProbabilities: external_exports.boolean().optional(),
15392
15403
  preprocessMode: external_exports.enum(["letterbox", "resize"]).optional(),
15393
15404
  /**
15394
15405
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -19694,20 +19705,6 @@ var pipelineExecutorCapability = {
19694
19705
  getSelectedEngine: method(external_exports.void(), PipelineEngineChoiceSchema),
19695
19706
  getDefaultSteps: method(PipelineEngineChoiceSchema, external_exports.array(PipelineDefaultStepSchema)),
19696
19707
  /**
19697
- * Re-run the platform probe for the inference engine and write the
19698
- * detected runtime / backend / device back into the addon's own
19699
- * global settings store. Returns the engine that was picked so the
19700
- * UI can confirm without a second round-trip. Triggers the standard
19701
- * `requiresRestart` auto-restart flow because all three fields are
19702
- * marked restart-required. Replaces the legacy per-agent
19703
- * `pipeline-orchestrator.reprobeAgentHwAccel` surface for the
19704
- * engine dimension — each addon now owns its own probe.
19705
- */
19706
- reprobeEngine: method(external_exports.void(), PipelineEngineChoiceSchema, {
19707
- kind: "mutation",
19708
- auth: "admin"
19709
- }),
19710
- /**
19711
19708
  * Per-node detection-engine provisioning snapshot. Returns the live
19712
19709
  * state of the lazy runtime-provisioning machine on `nodeId`
19713
19710
  * (idle / installing / verifying / ready / failed). The UI pairs this
@@ -19845,7 +19842,14 @@ var pipelineExecutorCapability = {
19845
19842
  * (inputClasses ≠ null) are skipped and served per-track via
19846
19843
  * pipelineRunner.runDetailSubtree (two-plane design).
19847
19844
  */
19848
- plane: external_exports.enum(["full", "frame"]).optional()
19845
+ plane: external_exports.enum(["full", "frame"]).optional(),
19846
+ /**
19847
+ * Inference-device selector (Phase 2 multi-device). Format
19848
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
19849
+ * Omitted ⇒ the runner's default device (current single-engine
19850
+ * behaviour). Selects WHICH device pool of the node runs the call.
19851
+ */
19852
+ deviceKey: external_exports.string().optional()
19849
19853
  }), PipelineRunResultBridge, { kind: "mutation" }),
19850
19854
  /**
19851
19855
  * Batched run — N raw frames packed into one cap call. The provider
@@ -19863,7 +19867,20 @@ var pipelineExecutorCapability = {
19863
19867
  steps: external_exports.array(PipelineStepInputSchema).min(1),
19864
19868
  frames: external_exports.array(FrameInputSchema).min(1).max(255),
19865
19869
  deviceId: external_exports.number().optional(),
19866
- sessionId: external_exports.string().optional()
19870
+ sessionId: external_exports.string().optional(),
19871
+ /**
19872
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
19873
+ * the batch to the Python pool's bench preprocess cache
19874
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
19875
+ * preprocessed ONCE and every later inference is a pure-inference cache
19876
+ * hit — the sustained-throughput run measures inference, not
19877
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
19878
+ * full preprocess every call, correct). Fresh per sustained run;
19879
+ * released via `uncacheFrame`.
19880
+ */
19881
+ frameId: external_exports.number().int().nonnegative().optional(),
19882
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
19883
+ deviceKey: external_exports.string().optional()
19867
19884
  }), external_exports.object({ results: external_exports.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
19868
19885
  /**
19869
19886
  * Cache a raw frame inside the Python inference pool's memory.
@@ -19920,8 +19937,18 @@ var pipelineExecutorCapability = {
19920
19937
  * - `runtime` — main camera-serving engine (no idle TTL).
19921
19938
  * - `warm-override` — benchmark/test override held in the warm
19922
19939
  * cache; auto-disposed after the idle TTL.
19940
+ * - `device-pool` — a concurrent per-device pool (Phase 2
19941
+ * multi-device, keyed by `deviceKey`) resolved
19942
+ * via `resolveDeviceFactory`. Runs alongside the
19943
+ * `runtime` engine on a DIFFERENT accelerator
19944
+ * (NPU / iGPU / Coral) — this is how the
19945
+ * Engines tab shows all pools running at once.
19923
19946
  */
19924
- kind: external_exports.enum(["runtime", "warm-override"]),
19947
+ kind: external_exports.enum([
19948
+ "runtime",
19949
+ "warm-override",
19950
+ "device-pool"
19951
+ ]),
19925
19952
  /** Native pid of the underlying Python pool (null when no pool). */
19926
19953
  poolPid: external_exports.number().nullable(),
19927
19954
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -20026,7 +20053,21 @@ var NativeCropResultSchema = external_exports.object({
20026
20053
  /** Packed rgb (24-bit) pixels of the crop. */
20027
20054
  bytes: external_exports.instanceof(Uint8Array),
20028
20055
  width: external_exports.number().int().positive(),
20029
- height: external_exports.number().int().positive()
20056
+ height: external_exports.number().int().positive(),
20057
+ /**
20058
+ * Which source served this crop, so a quality-sensitive consumer (the native
20059
+ * `keyFrame`) can reject a degraded fallback:
20060
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
20061
+ * quality path).
20062
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
20063
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
20064
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
20065
+ *
20066
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
20067
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
20068
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
20069
+ */
20070
+ tier: external_exports.enum(["native", "ram-fullframe"]).optional()
20030
20071
  });
20031
20072
  var DetailParentSchema = external_exports.object({
20032
20073
  bbox: NativeCropBboxSchema,
@@ -20194,7 +20235,14 @@ var RunnerCameraConfigSchema = external_exports.object({
20194
20235
  * camera's detect node differs from its source-owner (P2d, gated by the
20195
20236
  * `remoteSourcingNodes` rollout setting).
20196
20237
  */
20197
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
20238
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
20239
+ /**
20240
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
20241
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
20242
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
20243
+ * this only selects WHICH device pool of that node runs the session.
20244
+ */
20245
+ deviceKey: external_exports.string().optional()
20198
20246
  });
20199
20247
  var RunnerCameraDeviceUIFields = [
20200
20248
  {
@@ -20319,6 +20367,19 @@ var RunnerLocalLoadSchema = external_exports.object({
20319
20367
  avgInferenceTimeMs: external_exports.number(),
20320
20368
  /** Total queue depth across motion + detection queues. */
20321
20369
  queueDepthTotal: external_exports.number(),
20370
+ /**
20371
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
20372
+ * this runner currently has attached cameras on, so the orchestrator's second
20373
+ * `balance()` pass (over a node's devices) weights on real per-pool session
20374
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
20375
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
20376
+ */
20377
+ devices: external_exports.array(external_exports.object({
20378
+ deviceKey: external_exports.string(),
20379
+ backend: external_exports.string(),
20380
+ attachedCameras: external_exports.number(),
20381
+ queueDepthTotal: external_exports.number()
20382
+ })).default([]),
20322
20383
  /** Hardware capability flags reported by this node. */
20323
20384
  hardware: external_exports.object({
20324
20385
  hasGpu: external_exports.boolean(),
@@ -20373,10 +20434,15 @@ var pipelineRunnerCapability = {
20373
20434
  * `FrameHandle` that rode an inference-result event and a normalized `bbox`,
20374
20435
  * the runner asks the decode worker still holding that frame's NATIVE
20375
20436
  * surface to GPU/CPU-crop ONLY the ROI at native res and download just the
20376
- * crop. Returns `null` on a miss (handle not registered, or the worker's
20377
- * tiny native-retention ring already evicted the frame) — the caller falls
20378
- * back to a detection-frame crop. Routed to the frame's owning node by
20379
- * `handle.nodeId`; NEVER ships a full native frame.
20437
+ * crop. Routed to the frame's owning node by `handle.nodeId`; NEVER ships a
20438
+ * full native frame.
20439
+ *
20440
+ * Miss behaviour differs by request shape (see `NativeCropResultSchema.tier`):
20441
+ * - an ROI (subject-crop) miss returns `null` — the caller reports the miss
20442
+ * and retries; it is NEVER handed a ≤640 crop masquerading as native.
20443
+ * - a FULL-FRAME miss falls back to the ≤640 RAM `RetainedFrameStore`, tagged
20444
+ * `tier: 'ram-fullframe'` (honest lower-res for the blank-frame guard /
20445
+ * 640-snapshot resolve). A native hit is tagged `tier: 'native'`.
20380
20446
  */
20381
20447
  getNativeCrop: method(external_exports.object({
20382
20448
  handle: FrameHandleSchema,
@@ -22501,6 +22567,40 @@ var deviceProviderCapability = {
22501
22567
  })
22502
22568
  }
22503
22569
  };
22570
+ var DEVICE_TYPE_CONTROL_KIND = {
22571
+ [DeviceType.Cover]: "cover",
22572
+ [DeviceType.Valve]: "valve",
22573
+ [DeviceType.Humidifier]: "humidifier",
22574
+ [DeviceType.WaterHeater]: "water-heater",
22575
+ [DeviceType.Camera]: "dummy",
22576
+ [DeviceType.Hub]: "dummy",
22577
+ [DeviceType.Switch]: "switch",
22578
+ [DeviceType.Siren]: "switch",
22579
+ [DeviceType.Light]: "light",
22580
+ [DeviceType.Fan]: "fan",
22581
+ [DeviceType.Sensor]: "sensor",
22582
+ [DeviceType.Thermostat]: "thermostat",
22583
+ [DeviceType.Climate]: "climate",
22584
+ [DeviceType.Button]: "button",
22585
+ [DeviceType.EventEmitter]: "dummy",
22586
+ [DeviceType.Update]: "update",
22587
+ [DeviceType.Generic]: "dummy",
22588
+ [DeviceType.Notifier]: "dummy",
22589
+ [DeviceType.Script]: "dummy",
22590
+ [DeviceType.Automation]: "dummy",
22591
+ [DeviceType.Lock]: "lock",
22592
+ [DeviceType.MediaPlayer]: "media-player",
22593
+ [DeviceType.AlarmPanel]: "alarm",
22594
+ [DeviceType.Control]: "control",
22595
+ [DeviceType.Presence]: "sensor",
22596
+ [DeviceType.Weather]: "weather",
22597
+ [DeviceType.Vacuum]: "vacuum",
22598
+ [DeviceType.LawnMower]: "lawn-mower",
22599
+ [DeviceType.Container]: "dummy",
22600
+ [DeviceType.Image]: "image",
22601
+ [DeviceType.PetFeeder]: "pet-feeder"
22602
+ };
22603
+ var DEVICE_TYPE_VALUES = new Set(Object.values(DeviceType));
22504
22604
  var AddonPageDeclarationSchema$1 = external_exports.object({
22505
22605
  id: external_exports.string(),
22506
22606
  label: external_exports.string(),
@@ -24065,7 +24165,8 @@ var LinkedDeviceSchema = external_exports.object({
24065
24165
  deviceId: external_exports.number(),
24066
24166
  name: external_exports.string(),
24067
24167
  location: external_exports.string().nullable(),
24068
- features: external_exports.array(external_exports.string())
24168
+ features: external_exports.array(external_exports.string()),
24169
+ producesTrackedEvents: external_exports.boolean().optional()
24069
24170
  });
24070
24171
  var SavedDeviceRowSchema = external_exports.object({
24071
24172
  /** Numeric id reserved at allocateDeviceId time. */
@@ -25982,6 +26083,7 @@ var TrackSchema = external_exports.object({
25982
26083
  deviceId: external_exports.number(),
25983
26084
  className: external_exports.string(),
25984
26085
  label: external_exports.string().optional(),
26086
+ producingDeviceName: external_exports.string().optional(),
25985
26087
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
25986
26088
  source: TrackSourceSchema.optional(),
25987
26089
  firstSeen: external_exports.number(),
@@ -26111,7 +26213,8 @@ var MediaFileKindEnum = external_exports.enum([
26111
26213
  "fullFrameBoxed",
26112
26214
  "faceCrop",
26113
26215
  "plateCrop",
26114
- "keyFrame"
26216
+ "keyFrame",
26217
+ "keyFrameSmall"
26115
26218
  ]);
26116
26219
  var MediaFileSchema = external_exports.object({
26117
26220
  key: external_exports.string(),
@@ -26570,13 +26673,11 @@ var PipelineTemplateSchema = external_exports.object({
26570
26673
  createdAt: external_exports.string(),
26571
26674
  updatedAt: external_exports.string()
26572
26675
  });
26573
- var AgentAddonConfigSchema = external_exports.object({
26574
- enabled: external_exports.boolean(),
26676
+ var DeviceStepConfigSchema = external_exports.object({
26575
26677
  modelId: external_exports.string().optional(),
26576
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).readonly()
26678
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
26577
26679
  });
26578
26680
  var AgentPipelineSettingsSchema = external_exports.object({
26579
- addonDefaults: external_exports.record(external_exports.string(), AgentAddonConfigSchema).readonly(),
26580
26681
  maxCameras: external_exports.number().int().nonnegative().nullable().default(null),
26581
26682
  /** Per-node detection weight (relative share for the quota balancer). */
26582
26683
  detectWeight: external_exports.number().positive().optional(),
@@ -26600,7 +26701,22 @@ var AgentPipelineSettingsSchema = external_exports.object({
26600
26701
  * it already uses to reach the hub). Set this only when the auto-detected
26601
26702
  * address is wrong (multi-homed host, NAT, custom interface).
26602
26703
  */
26603
- reachableHost: external_exports.string().optional()
26704
+ reachableHost: external_exports.string().optional(),
26705
+ /**
26706
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
26707
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
26708
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
26709
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
26710
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
26711
+ * the default model/settings for every camera landing on that accelerator;
26712
+ * a stepId absent ⇒ the step uses that device's format default.
26713
+ */
26714
+ inferenceDevices: external_exports.record(external_exports.string(), external_exports.object({
26715
+ enabled: external_exports.boolean(),
26716
+ weight: external_exports.number().positive().optional(),
26717
+ maxSessions: external_exports.number().int().positive().optional(),
26718
+ steps: external_exports.record(external_exports.string(), DeviceStepConfigSchema).optional()
26719
+ })).optional()
26604
26720
  });
26605
26721
  var CameraPipelineForAgentSchema = external_exports.object({
26606
26722
  steps: external_exports.array(PipelineStepInputSchema).readonly(),
@@ -26610,14 +26726,13 @@ var CameraPipelineForAgentSchema = external_exports.object({
26610
26726
  }).nullable()
26611
26727
  });
26612
26728
  var CameraStepOverridePatchSchema = external_exports.object({
26613
- enabled: external_exports.boolean().optional(),
26614
26729
  modelId: external_exports.string().optional(),
26615
26730
  settings: external_exports.record(external_exports.string(), external_exports.unknown()).readonly().optional()
26616
26731
  });
26617
26732
  var CameraPipelineSettingsSchema = external_exports.object({
26618
26733
  pinnedAgentNodeId: external_exports.string().optional(),
26619
26734
  stepToggles: external_exports.record(external_exports.string(), external_exports.boolean()).optional(),
26620
- stepOverridesByAgent: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), CameraStepOverridePatchSchema)).optional(),
26735
+ stepOverridesByDevice: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), CameraStepOverridePatchSchema))).optional(),
26621
26736
  pipelineByAgent: external_exports.record(external_exports.string(), CameraPipelineForAgentSchema).optional()
26622
26737
  });
26623
26738
  var PipelineAssignmentSchema = external_exports.object({
@@ -26783,6 +26898,44 @@ var CameraStatusSchema = external_exports.object({
26783
26898
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
26784
26899
  fetchedAt: external_exports.number()
26785
26900
  });
26901
+ var NodeInferenceDeviceSchema = external_exports.object({
26902
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
26903
+ key: external_exports.string(),
26904
+ backend: external_exports.string(),
26905
+ device: external_exports.string(),
26906
+ format: external_exports.enum(MODEL_FORMATS),
26907
+ /** Whether the node's live probe reports the device as usable right now. */
26908
+ available: external_exports.boolean(),
26909
+ /**
26910
+ * Whether this device participates in dispatch. AUTO default (spec C2):
26911
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
26912
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
26913
+ * not a balanced target). An explicit stored value always wins; a stored-only
26914
+ * (unavailable) key keeps its stored value.
26915
+ */
26916
+ enabled: external_exports.boolean(),
26917
+ /** Relative balancer weight for the enabled device (default 1). */
26918
+ weight: external_exports.number(),
26919
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
26920
+ maxSessions: external_exports.number().nullable(),
26921
+ /** Object-detection model the executor defaults to for this deviceKey. */
26922
+ defaultModelId: external_exports.string(),
26923
+ /**
26924
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
26925
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
26926
+ * Absent/empty ⇒ no base (every step uses its device format default). The
26927
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
26928
+ * available per format; this is the stored selection that becomes the
26929
+ * default for EVERY camera landing on this accelerator.
26930
+ */
26931
+ steps: external_exports.record(external_exports.string(), DeviceStepConfigSchema).optional()
26932
+ });
26933
+ var NodeInferenceDevicesSchema = external_exports.object({
26934
+ nodeId: external_exports.string(),
26935
+ /** False when the node's platform-probe was unreachable (no live device set). */
26936
+ reachable: external_exports.boolean(),
26937
+ devices: external_exports.array(NodeInferenceDeviceSchema).readonly()
26938
+ });
26786
26939
  var pipelineOrchestratorCapability = {
26787
26940
  name: "pipeline-orchestrator",
26788
26941
  scope: "system",
@@ -26820,6 +26973,34 @@ var pipelineOrchestratorCapability = {
26820
26973
  auth: "admin"
26821
26974
  }),
26822
26975
  /**
26976
+ * Pin a camera's detection to a specific INFERENCE DEVICE (accelerator)
26977
+ * within its node — a SOFT L1 device affinity honoured by the per-session
26978
+ * device `balance()` pass (sibling of the node-level `pipelineNodeId` pin;
26979
+ * persisted as `pipelineDeviceKey` in the device store, read by
26980
+ * `readPipelineDevicePin`). `deviceKey` is a concrete key
26981
+ * (`openvino:npu` / `edgetpu:usb` / `cpu`), or the `'auto'` sentinel /
26982
+ * empty string to CLEAR the pin and let the balancer pick the device.
26983
+ *
26984
+ * Applies promptly: the device is chosen per-session at dispatch, so any
26985
+ * running detection session is closed and re-dispatched onto the new pin
26986
+ * (the camera stays on the SAME node — a device pin never moves the camera).
26987
+ * Cameras with no active session pick it up on their next dispatch.
26988
+ */
26989
+ setPipelineDevicePin: method(external_exports.object({
26990
+ deviceId: external_exports.number(),
26991
+ deviceKey: external_exports.string()
26992
+ }), external_exports.object({ success: external_exports.literal(true) }), {
26993
+ kind: "mutation",
26994
+ auth: "admin"
26995
+ }),
26996
+ /**
26997
+ * Read a camera's persisted inference-device pin (`pipelineDeviceKey`).
26998
+ * Returns the concrete device key, or `null` when unpinned (auto). Lets the
26999
+ * UI show the current pin alongside the node pin (parity with the node-pin
27000
+ * value carried on the assignment record).
27001
+ */
27002
+ getPipelineDevicePin: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.object({ deviceKey: external_exports.string().nullable() })),
27003
+ /**
26823
27004
  * Trigger a full rebalance pass. Iterates over all assigned cameras,
26824
27005
  * recomputes the optimal agent for each one based on current load +
26825
27006
  * hardware affinity, and migrates cameras whose current agent is no
@@ -26904,14 +27085,6 @@ var pipelineOrchestratorCapability = {
26904
27085
  nodeId: external_exports.string(),
26905
27086
  settings: AgentPipelineSettingsSchema
26906
27087
  })).readonly()),
26907
- /** Bulk-replace ALL addon configs on one agent. Used by the agent-level PipelineEditor which emits the full map per edit. Merges on top of existing entries — omitted addonIds are left alone. */
26908
- setAgentAddonDefaults: method(external_exports.object({
26909
- agentNodeId: external_exports.string(),
26910
- defaults: external_exports.record(external_exports.string(), AgentAddonConfigSchema)
26911
- }), external_exports.object({ success: external_exports.literal(true) }), {
26912
- kind: "mutation",
26913
- auth: "admin"
26914
- }),
26915
27088
  /**
26916
27089
  * Drop a single agent's persisted settings. Used by the cluster
26917
27090
  * "Offline agents" panel to evict stale entries left behind when an
@@ -26998,6 +27171,46 @@ var pipelineOrchestratorCapability = {
26998
27171
  auth: "admin"
26999
27172
  }),
27000
27173
  /**
27174
+ * Set one node's multi-device inference opt-in (Phase 4 multi-device) — the
27175
+ * per-node `deviceKey → { enabled, weight }` map that decides which
27176
+ * accelerators the dispatcher may balance detection sessions across.
27177
+ * The map REPLACES the node's stored `inferenceDevices` wholesale (pass the
27178
+ * complete desired set; an empty map clears the opt-in → the node reverts to
27179
+ * its single DEFAULT accelerator). `weight` is optional and defaults to 1.
27180
+ * `models` is the optional per-device model override (`stepId → modelId`,
27181
+ * C7.2/C7.4) — a stepId absent ⇒ the step uses that device's default model.
27182
+ * Creates the node's settings entry if absent. Changes take effect on the
27183
+ * next dispatch/rebalance cycle — the dispatcher reads the enabled set via
27184
+ * `PipelineSettingsStore.buildNodeInferenceDevices` when placing sessions
27185
+ * and the per-(node,device) base provisioning via
27186
+ * `buildNodeInferenceDeviceSteps`.
27187
+ */
27188
+ setAgentInferenceDevices: method(external_exports.object({
27189
+ agentNodeId: external_exports.string(),
27190
+ inferenceDevices: external_exports.record(external_exports.string(), external_exports.object({
27191
+ enabled: external_exports.boolean(),
27192
+ weight: external_exports.number().positive().optional(),
27193
+ maxSessions: external_exports.number().int().positive().optional(),
27194
+ steps: external_exports.record(external_exports.string(), DeviceStepConfigSchema).optional()
27195
+ }))
27196
+ }), external_exports.object({ success: external_exports.literal(true) }), {
27197
+ kind: "mutation",
27198
+ auth: "admin"
27199
+ }),
27200
+ /**
27201
+ * DEVICE-AWARE merged view of ONE node's inference accelerators. Joins each
27202
+ * physical device the node HAS — probed live via a node-pinned
27203
+ * `platformProbe.getCapabilities` (the platform-probe singleton is targeted
27204
+ * by the `nodeId` cap INPUT, since a `nodePin` context is ignored by a
27205
+ * singleton mount) — with its stored opt-in (`enabled`/`weight`) and the
27206
+ * object-detection `defaultModelId` the executor would run for that
27207
+ * deviceKey. Disabled-but-configured keys still appear. An unreachable node
27208
+ * returns `reachable:false` + best-effort stored-only devices (never
27209
+ * throws). Read surface for the cluster/agent multi-device UI — hence a
27210
+ * default (read-level) auth, matching the other `getAgent*` reads.
27211
+ */
27212
+ getNodeInferenceDevices: method(external_exports.object({ nodeId: external_exports.string() }), NodeInferenceDevicesSchema),
27213
+ /**
27001
27214
  * Reset one node's pipeline to its hardware-aware defaults — the HONEST
27002
27215
  * reset (replaces the executor's legacy `resetToDefault`, which cleared a
27003
27216
  * dead persisted step-tree seed nothing in the live path read):
@@ -27036,15 +27249,18 @@ var pipelineOrchestratorCapability = {
27036
27249
  kind: "mutation",
27037
27250
  auth: "admin"
27038
27251
  }),
27039
- /** Read every L2.5 override map for a device. Null when device has no overrides. */
27040
- getCameraStepOverrides: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), CameraStepOverridePatchSchema)).nullable()),
27252
+ /** Read every per-(node,device) override map for a device. Null when device has no overrides. */
27253
+ getCameraStepOverrides: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), CameraStepOverridePatchSchema))).nullable()),
27041
27254
  /**
27042
- * Write or clear the L2.5 per-agent override for one addonId on a device.
27043
- * `patch: null` clears the entry entirely for that (agent, addon) pair.
27255
+ * Write or clear the per-(camera,node,device) override for one addonId.
27256
+ * `patch: null` clears the entry entirely for that (node, deviceKey, addon)
27257
+ * triple (empty parents are pruned). Model/settings only — enablement is
27258
+ * the per-camera `stepToggles` authority.
27044
27259
  */
27045
27260
  setCameraStepOverride: method(external_exports.object({
27046
27261
  deviceId: external_exports.number(),
27047
27262
  agentNodeId: external_exports.string(),
27263
+ deviceKey: external_exports.string(),
27048
27264
  addonId: external_exports.string(),
27049
27265
  patch: CameraStepOverridePatchSchema.nullable()
27050
27266
  }), external_exports.object({ success: external_exports.literal(true) }), {
@@ -27197,7 +27413,8 @@ var serverManagementCapability = {
27197
27413
  version: external_exports.string().optional()
27198
27414
  }), ServerUpdateActionResultSchema, {
27199
27415
  kind: "mutation",
27200
- auth: "admin"
27416
+ auth: "admin",
27417
+ timeoutMs: 16 * 6e4
27201
27418
  }),
27202
27419
  rollbackServerUpdate: method(external_exports.void(), ServerUpdateActionResultSchema, {
27203
27420
  kind: "mutation",
@@ -28389,13 +28606,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
28389
28606
  var RestartAddonResultSchema = external_exports.unknown();
28390
28607
  var InstallPackageResultSchema = external_exports.unknown();
28391
28608
  var ReloadPackagesResultSchema = external_exports.unknown();
28392
- var UpdateFrameworkPackageResultSchema = external_exports.object({
28393
- packageName: external_exports.string(),
28394
- fromVersion: external_exports.string(),
28395
- toVersion: external_exports.string(),
28396
- /** Ms-epoch the server scheduled its self-restart. */
28397
- restartingAt: external_exports.number()
28398
- });
28399
28609
  var BulkUpdateItemStatusSchema = external_exports.enum([
28400
28610
  "queued",
28401
28611
  "updating",
@@ -28623,28 +28833,6 @@ var addonsCapability = {
28623
28833
  kind: "mutation",
28624
28834
  auth: "admin"
28625
28835
  }),
28626
- /**
28627
- * Live-update one of the framework packages marked
28628
- * `camstack.system: true` (`@camstack/types|kernel|core|sdk|ui-library`).
28629
- * Runs `npm install --prefix <appRoot> <name>@<version> --no-save`,
28630
- * writes a `.restart-pending` marker, emits `system.restarting`
28631
- * and schedules a graceful process exit. The supervisor (Docker /
28632
- * Electron / systemd) brings the hub back up; on first boot after
28633
- * the restart the marker fires `system.restart-completed`.
28634
- *
28635
- * `version` defaults to `'latest'`. The allow-list of valid
28636
- * `packageName` values is enforced server-side.
28637
- *
28638
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
28639
- */
28640
- updateFrameworkPackage: method(external_exports.object({
28641
- packageName: external_exports.string().min(1),
28642
- version: external_exports.string().optional(),
28643
- deferRestart: external_exports.boolean().optional()
28644
- }), UpdateFrameworkPackageResultSchema, {
28645
- kind: "mutation",
28646
- auth: "admin"
28647
- }),
28648
28836
  getVersions: method(external_exports.object({ name: external_exports.string() }), external_exports.array(PackageVersionInfoSchema).readonly()),
28649
28837
  restartAddon: method(external_exports.object({ addonId: external_exports.string() }), RestartAddonResultSchema, {
28650
28838
  kind: "mutation",
@@ -30140,17 +30328,28 @@ var PlatformScoreSchema = external_exports.object({
30140
30328
  format: external_exports.enum([
30141
30329
  "onnx",
30142
30330
  "coreml",
30143
- "openvino"
30331
+ "openvino",
30332
+ "tflite"
30144
30333
  ]),
30145
30334
  score: external_exports.number(),
30146
30335
  reason: external_exports.string(),
30147
30336
  available: external_exports.boolean()
30148
30337
  });
30338
+ var InferenceDeviceDescriptorSchema = external_exports.object({
30339
+ key: external_exports.string(),
30340
+ backend: external_exports.string(),
30341
+ device: external_exports.string(),
30342
+ format: ModelFormatSchema,
30343
+ runtime: external_exports.literal("python"),
30344
+ score: external_exports.number(),
30345
+ available: external_exports.boolean()
30346
+ });
30149
30347
  var PlatformCapabilitiesSchema = external_exports.object({
30150
30348
  hardware: HardwareInfoSchema,
30151
30349
  scores: external_exports.array(PlatformScoreSchema).readonly(),
30152
30350
  bestScore: PlatformScoreSchema,
30153
- pythonPath: external_exports.string().nullable()
30351
+ pythonPath: external_exports.string().nullable(),
30352
+ devices: external_exports.array(InferenceDeviceDescriptorSchema).readonly()
30154
30353
  });
30155
30354
  var ModelRequirementSchema = external_exports.object({
30156
30355
  modelId: external_exports.string(),
@@ -31122,12 +31321,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
31122
31321
  addonId: null,
31123
31322
  access: "delete"
31124
31323
  },
31125
- "addons.updateFrameworkPackage": {
31126
- capName: "addons",
31127
- capScope: "system",
31128
- addonId: null,
31129
- access: "create"
31130
- },
31131
31324
  "addons.updatePackage": {
31132
31325
  capName: "addons",
31133
31326
  capScope: "system",
@@ -33900,12 +34093,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
33900
34093
  addonId: null,
33901
34094
  access: "view"
33902
34095
  },
33903
- "pipelineExecutor.reprobeEngine": {
33904
- capName: "pipeline-executor",
33905
- capScope: "system",
33906
- addonId: null,
33907
- access: "create"
33908
- },
33909
34096
  "pipelineExecutor.runAudioTest": {
33910
34097
  capName: "pipeline-executor",
33911
34098
  capScope: "system",
@@ -34056,6 +34243,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34056
34243
  addonId: null,
34057
34244
  access: "view"
34058
34245
  },
34246
+ "pipelineOrchestrator.getNodeInferenceDevices": {
34247
+ capName: "pipeline-orchestrator",
34248
+ capScope: "system",
34249
+ addonId: null,
34250
+ access: "view"
34251
+ },
34059
34252
  "pipelineOrchestrator.getPipelineAssignment": {
34060
34253
  capName: "pipeline-orchestrator",
34061
34254
  capScope: "system",
@@ -34068,6 +34261,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34068
34261
  addonId: null,
34069
34262
  access: "view"
34070
34263
  },
34264
+ "pipelineOrchestrator.getPipelineDevicePin": {
34265
+ capName: "pipeline-orchestrator",
34266
+ capScope: "system",
34267
+ addonId: null,
34268
+ access: "view"
34269
+ },
34071
34270
  "pipelineOrchestrator.listAgentSettings": {
34072
34271
  capName: "pipeline-orchestrator",
34073
34272
  capScope: "system",
@@ -34110,19 +34309,19 @@ var METHOD_ACCESS_MAP = Object.freeze({
34110
34309
  addonId: null,
34111
34310
  access: "create"
34112
34311
  },
34113
- "pipelineOrchestrator.setAgentAddonDefaults": {
34312
+ "pipelineOrchestrator.setAgentCapabilities": {
34114
34313
  capName: "pipeline-orchestrator",
34115
34314
  capScope: "system",
34116
34315
  addonId: null,
34117
34316
  access: "create"
34118
34317
  },
34119
- "pipelineOrchestrator.setAgentCapabilities": {
34318
+ "pipelineOrchestrator.setAgentDetectWeight": {
34120
34319
  capName: "pipeline-orchestrator",
34121
34320
  capScope: "system",
34122
34321
  addonId: null,
34123
34322
  access: "create"
34124
34323
  },
34125
- "pipelineOrchestrator.setAgentDetectWeight": {
34324
+ "pipelineOrchestrator.setAgentInferenceDevices": {
34126
34325
  capName: "pipeline-orchestrator",
34127
34326
  capScope: "system",
34128
34327
  addonId: null,
@@ -34164,6 +34363,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34164
34363
  addonId: null,
34165
34364
  access: "create"
34166
34365
  },
34366
+ "pipelineOrchestrator.setPipelineDevicePin": {
34367
+ capName: "pipeline-orchestrator",
34368
+ capScope: "system",
34369
+ addonId: null,
34370
+ access: "create"
34371
+ },
34167
34372
  "pipelineOrchestrator.unassignAudio": {
34168
34373
  capName: "pipeline-orchestrator",
34169
34374
  capScope: "system",
@@ -35716,32 +35921,6 @@ var CAP_PROVIDER_KIND_MAP = Object.freeze({
35716
35921
  "network-access": "ingress",
35717
35922
  "smtp-provider": "email"
35718
35923
  });
35719
- var frameworkSwapPackageSchema = external_exports.object({
35720
- name: external_exports.string(),
35721
- stagedPath: external_exports.string(),
35722
- backupPath: external_exports.string(),
35723
- toVersion: external_exports.string(),
35724
- fromVersion: external_exports.string().nullable()
35725
- });
35726
- var pendingFrameworkSwapSchema = external_exports.object({
35727
- jobId: external_exports.string(),
35728
- taskId: external_exports.string(),
35729
- packages: external_exports.array(frameworkSwapPackageSchema),
35730
- requestedAtMs: external_exports.number(),
35731
- schemaVersion: external_exports.literal(1)
35732
- });
35733
- var frameworkSwapConfirmSchema = external_exports.object({
35734
- jobId: external_exports.string(),
35735
- taskId: external_exports.string(),
35736
- backups: external_exports.array(external_exports.object({
35737
- name: external_exports.string(),
35738
- backupPath: external_exports.string(),
35739
- livePath: external_exports.string()
35740
- })),
35741
- appliedAtMs: external_exports.number(),
35742
- bootAttempts: external_exports.number(),
35743
- schemaVersion: external_exports.literal(1)
35744
- });
35745
35924
 
35746
35925
  // src/commands/discover.ts
35747
35926
  var DEFAULT_MULTICAST_GROUP = "239.0.0.0";
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDiscover
4
- } from "./chunk-ZFONIXEK.js";
4
+ } from "./chunk-UW26FXNH.js";
5
5
  import "./chunk-K3NQKI34.js";
6
6
 
7
7
  // src/cli.ts
@@ -1079,7 +1079,7 @@ function isUnknown(_value) {
1079
1079
  return true;
1080
1080
  }
1081
1081
  async function resolveServerInteractive(presetNamespace) {
1082
- const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-ZBSZJQ2R.js");
1082
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-YZCMQEZ5.js");
1083
1083
  if (presetNamespace) {
1084
1084
  const spinner4 = clack.spinner();
1085
1085
  spinner4.start(`Discovering hub on LAN (namespace "${presetNamespace}")`);
@@ -5,7 +5,7 @@ import {
5
5
  filterHubNodes,
6
6
  resolveHubFromDiscovered,
7
7
  runDiscover
8
- } from "./chunk-ZFONIXEK.js";
8
+ } from "./chunk-UW26FXNH.js";
9
9
  import "./chunk-K3NQKI34.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "1.1.32",
3
+ "version": "1.2.1",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",