@camstack/addon-remote-storage 1.1.29 → 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.
package/dist/s3.addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-CZDYRlzo.js");
5
+ const require_shared = require("./shared-C77pnEyI.js");
6
6
  let node_stream = require("node:stream");
7
7
  let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
8
8
  let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
package/dist/s3.addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-DW5ccoQG.mjs";
1
+ import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-BXdymO1F.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { DeleteObjectCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
4
  import { Upload } from "@aws-sdk/lib-storage";
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-CZDYRlzo.js");
5
+ const require_shared = require("./shared-C77pnEyI.js");
6
6
  let ssh2 = require("ssh2");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_shared.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-DW5ccoQG.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-BXdymO1F.mjs";
2
2
  import { Client } from "ssh2";
3
3
  import * as path from "node:path";
4
4
  //#region src/providers/sftp/sftp-config-schema.ts
@@ -7069,6 +7069,17 @@ var ModelCatalogEntrySchema = object({
7069
7069
  "imagenet",
7070
7070
  "none"
7071
7071
  ]).optional(),
7072
+ /**
7073
+ * The model already applies softmax IN-GRAPH — its raw output is a
7074
+ * probability distribution, not logits. When set, the `softmax`
7075
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7076
+ * probability vector collapses it toward uniform (top-1 score craters far
7077
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7078
+ * the output is raw logits and the postprocessor applies softmax (the normal
7079
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7080
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7081
+ */
7082
+ outputProbabilities: boolean().optional(),
7072
7083
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7073
7084
  /**
7074
7085
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -11082,10 +11093,7 @@ var ConfigUISchemaNullableBridge = custom();
11082
11093
  var InferenceCapabilitiesBridge = custom();
11083
11094
  var ModelAvailabilityListBridge = custom();
11084
11095
  var PipelineRunResultBridge = custom();
11085
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11086
- kind: "mutation",
11087
- auth: "admin"
11088
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11096
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11089
11097
  modelId: string(),
11090
11098
  settings: record(string(), unknown()).readonly()
11091
11099
  }))), method(object({ steps: record(string(), object({
@@ -11145,13 +11153,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11145
11153
  * (inputClasses ≠ null) are skipped and served per-track via
11146
11154
  * pipelineRunner.runDetailSubtree (two-plane design).
11147
11155
  */
11148
- plane: _enum(["full", "frame"]).optional()
11156
+ plane: _enum(["full", "frame"]).optional(),
11157
+ /**
11158
+ * Inference-device selector (Phase 2 multi-device). Format
11159
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11160
+ * Omitted ⇒ the runner's default device (current single-engine
11161
+ * behaviour). Selects WHICH device pool of the node runs the call.
11162
+ */
11163
+ deviceKey: string().optional()
11149
11164
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11150
11165
  engine: PipelineEngineChoiceSchema.optional(),
11151
11166
  steps: array(PipelineStepInputSchema).min(1),
11152
11167
  frames: array(FrameInputSchema).min(1).max(255),
11153
11168
  deviceId: number().optional(),
11154
- sessionId: string().optional()
11169
+ sessionId: string().optional(),
11170
+ /**
11171
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11172
+ * the batch to the Python pool's bench preprocess cache
11173
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11174
+ * preprocessed ONCE and every later inference is a pure-inference cache
11175
+ * hit — the sustained-throughput run measures inference, not
11176
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11177
+ * full preprocess every call, correct). Fresh per sustained run;
11178
+ * released via `uncacheFrame`.
11179
+ */
11180
+ frameId: number().int().nonnegative().optional(),
11181
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11182
+ deviceKey: string().optional()
11155
11183
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11156
11184
  data: _instanceof(Uint8Array),
11157
11185
  width: number().int().positive(),
@@ -11183,8 +11211,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11183
11211
  * - `runtime` — main camera-serving engine (no idle TTL).
11184
11212
  * - `warm-override` — benchmark/test override held in the warm
11185
11213
  * cache; auto-disposed after the idle TTL.
11214
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11215
+ * multi-device, keyed by `deviceKey`) resolved
11216
+ * via `resolveDeviceFactory`. Runs alongside the
11217
+ * `runtime` engine on a DIFFERENT accelerator
11218
+ * (NPU / iGPU / Coral) — this is how the
11219
+ * Engines tab shows all pools running at once.
11186
11220
  */
11187
- kind: _enum(["runtime", "warm-override"]),
11221
+ kind: _enum([
11222
+ "runtime",
11223
+ "warm-override",
11224
+ "device-pool"
11225
+ ]),
11188
11226
  /** Native pid of the underlying Python pool (null when no pool). */
11189
11227
  poolPid: number().nullable(),
11190
11228
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11286,7 +11324,21 @@ var NativeCropResultSchema = object({
11286
11324
  /** Packed rgb (24-bit) pixels of the crop. */
11287
11325
  bytes: _instanceof(Uint8Array),
11288
11326
  width: number().int().positive(),
11289
- height: number().int().positive()
11327
+ height: number().int().positive(),
11328
+ /**
11329
+ * Which source served this crop, so a quality-sensitive consumer (the native
11330
+ * `keyFrame`) can reject a degraded fallback:
11331
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
11332
+ * quality path).
11333
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
11334
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
11335
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
11336
+ *
11337
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
11338
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
11339
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
11340
+ */
11341
+ tier: _enum(["native", "ram-fullframe"]).optional()
11290
11342
  });
11291
11343
  /** Parent detection context passed to `runDetailSubtree` — the crop's
11292
11344
  * originating detection, in FRAME-space coordinates. Reuses
@@ -11526,7 +11578,14 @@ var RunnerCameraConfigSchema = object({
11526
11578
  * camera's detect node differs from its source-owner (P2d, gated by the
11527
11579
  * `remoteSourcingNodes` rollout setting).
11528
11580
  */
11529
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11581
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11582
+ /**
11583
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11584
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11585
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11586
+ * this only selects WHICH device pool of that node runs the session.
11587
+ */
11588
+ deviceKey: string().optional()
11530
11589
  });
11531
11590
  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;
11532
11591
  /**
@@ -11547,6 +11606,19 @@ var RunnerLocalLoadSchema = object({
11547
11606
  avgInferenceTimeMs: number(),
11548
11607
  /** Total queue depth across motion + detection queues. */
11549
11608
  queueDepthTotal: number(),
11609
+ /**
11610
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11611
+ * this runner currently has attached cameras on, so the orchestrator's second
11612
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11613
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11614
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11615
+ */
11616
+ devices: array(object({
11617
+ deviceKey: string(),
11618
+ backend: string(),
11619
+ attachedCameras: number(),
11620
+ queueDepthTotal: number()
11621
+ })).default([]),
11550
11622
  /** Hardware capability flags reported by this node. */
11551
11623
  hardware: object({
11552
11624
  hasGpu: boolean(),
@@ -13022,6 +13094,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13022
13094
  kind: "mutation",
13023
13095
  auth: "admin"
13024
13096
  });
13097
+ 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;
13098
+ new Set(Object.values(DeviceType));
13025
13099
  /**
13026
13100
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13027
13101
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -14595,7 +14669,8 @@ var LinkedDeviceSchema = object({
14595
14669
  deviceId: number(),
14596
14670
  name: string(),
14597
14671
  location: string().nullable(),
14598
- features: array(string())
14672
+ features: array(string()),
14673
+ producesTrackedEvents: boolean().optional()
14599
14674
  });
14600
14675
  var SavedDeviceRowSchema = object({
14601
14676
  /** Numeric id reserved at allocateDeviceId time. */
@@ -16225,6 +16300,7 @@ var TrackSchema = object({
16225
16300
  deviceId: number(),
16226
16301
  className: string(),
16227
16302
  label: string().optional(),
16303
+ producingDeviceName: string().optional(),
16228
16304
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16229
16305
  source: TrackSourceSchema.optional(),
16230
16306
  firstSeen: number(),
@@ -16362,7 +16438,8 @@ var MediaFileKindEnum = _enum([
16362
16438
  "fullFrameBoxed",
16363
16439
  "faceCrop",
16364
16440
  "plateCrop",
16365
- "keyFrame"
16441
+ "keyFrame",
16442
+ "keyFrameSmall"
16366
16443
  ]);
16367
16444
  var MediaFileSchema = object({
16368
16445
  key: string(),
@@ -16691,13 +16768,11 @@ var PipelineTemplateSchema = object({
16691
16768
  createdAt: string(),
16692
16769
  updatedAt: string()
16693
16770
  });
16694
- var AgentAddonConfigSchema = object({
16695
- enabled: boolean(),
16771
+ var DeviceStepConfigSchema = object({
16696
16772
  modelId: string().optional(),
16697
- settings: record(string(), unknown()).readonly()
16773
+ settings: record(string(), unknown()).optional()
16698
16774
  });
16699
16775
  var AgentPipelineSettingsSchema = object({
16700
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
16701
16776
  maxCameras: number().int().nonnegative().nullable().default(null),
16702
16777
  /** Per-node detection weight (relative share for the quota balancer). */
16703
16778
  detectWeight: number().positive().optional(),
@@ -16721,7 +16796,22 @@ var AgentPipelineSettingsSchema = object({
16721
16796
  * it already uses to reach the hub). Set this only when the auto-detected
16722
16797
  * address is wrong (multi-homed host, NAT, custom interface).
16723
16798
  */
16724
- reachableHost: string().optional()
16799
+ reachableHost: string().optional(),
16800
+ /**
16801
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
16802
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
16803
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
16804
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
16805
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
16806
+ * the default model/settings for every camera landing on that accelerator;
16807
+ * a stepId absent ⇒ the step uses that device's format default.
16808
+ */
16809
+ inferenceDevices: record(string(), object({
16810
+ enabled: boolean(),
16811
+ weight: number().positive().optional(),
16812
+ maxSessions: number().int().positive().optional(),
16813
+ steps: record(string(), DeviceStepConfigSchema).optional()
16814
+ })).optional()
16725
16815
  });
16726
16816
  var CameraPipelineForAgentSchema = object({
16727
16817
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16731,14 +16821,13 @@ var CameraPipelineForAgentSchema = object({
16731
16821
  }).nullable()
16732
16822
  });
16733
16823
  var CameraStepOverridePatchSchema = object({
16734
- enabled: boolean().optional(),
16735
16824
  modelId: string().optional(),
16736
16825
  settings: record(string(), unknown()).readonly().optional()
16737
16826
  });
16738
16827
  var CameraPipelineSettingsSchema = object({
16739
16828
  pinnedAgentNodeId: string().optional(),
16740
16829
  stepToggles: record(string(), boolean()).optional(),
16741
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
16830
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
16742
16831
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
16743
16832
  });
16744
16833
  /**
@@ -16952,6 +17041,44 @@ var CameraStatusSchema = object({
16952
17041
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
16953
17042
  fetchedAt: number()
16954
17043
  });
17044
+ var NodeInferenceDeviceSchema = object({
17045
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17046
+ key: string(),
17047
+ backend: string(),
17048
+ device: string(),
17049
+ format: _enum(MODEL_FORMATS),
17050
+ /** Whether the node's live probe reports the device as usable right now. */
17051
+ available: boolean(),
17052
+ /**
17053
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17054
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17055
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17056
+ * not a balanced target). An explicit stored value always wins; a stored-only
17057
+ * (unavailable) key keeps its stored value.
17058
+ */
17059
+ enabled: boolean(),
17060
+ /** Relative balancer weight for the enabled device (default 1). */
17061
+ weight: number(),
17062
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17063
+ maxSessions: number().nullable(),
17064
+ /** Object-detection model the executor defaults to for this deviceKey. */
17065
+ defaultModelId: string(),
17066
+ /**
17067
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17068
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17069
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17070
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17071
+ * available per format; this is the stored selection that becomes the
17072
+ * default for EVERY camera landing on this accelerator.
17073
+ */
17074
+ steps: record(string(), DeviceStepConfigSchema).optional()
17075
+ });
17076
+ var NodeInferenceDevicesSchema = object({
17077
+ nodeId: string(),
17078
+ /** False when the node's platform-probe was unreachable (no live device set). */
17079
+ reachable: boolean(),
17080
+ devices: array(NodeInferenceDeviceSchema).readonly()
17081
+ });
16955
17082
  method(object({
16956
17083
  deviceId: number(),
16957
17084
  agentNodeId: string()
@@ -16961,7 +17088,13 @@ method(object({
16961
17088
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
16962
17089
  kind: "mutation",
16963
17090
  auth: "admin"
16964
- }), method(_void(), object({ migrated: number() }), {
17091
+ }), method(object({
17092
+ deviceId: number(),
17093
+ deviceKey: string()
17094
+ }), object({ success: literal(true) }), {
17095
+ kind: "mutation",
17096
+ auth: "admin"
17097
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
16965
17098
  kind: "mutation",
16966
17099
  auth: "admin"
16967
17100
  }), 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({
@@ -16995,13 +17128,7 @@ method(object({
16995
17128
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
16996
17129
  nodeId: string(),
16997
17130
  settings: AgentPipelineSettingsSchema
16998
- })).readonly()), method(object({
16999
- agentNodeId: string(),
17000
- defaults: record(string(), AgentAddonConfigSchema)
17001
- }), object({ success: literal(true) }), {
17002
- kind: "mutation",
17003
- auth: "admin"
17004
- }), method(object({ agentNodeId: string() }), object({
17131
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17005
17132
  success: boolean(),
17006
17133
  removed: boolean()
17007
17134
  }), {
@@ -17033,7 +17160,18 @@ method(object({
17033
17160
  }), object({ success: literal(true) }), {
17034
17161
  kind: "mutation",
17035
17162
  auth: "admin"
17036
- }), method(object({ agentNodeId: string() }), object({
17163
+ }), method(object({
17164
+ agentNodeId: string(),
17165
+ inferenceDevices: record(string(), object({
17166
+ enabled: boolean(),
17167
+ weight: number().positive().optional(),
17168
+ maxSessions: number().int().positive().optional(),
17169
+ steps: record(string(), DeviceStepConfigSchema).optional()
17170
+ }))
17171
+ }), object({ success: literal(true) }), {
17172
+ kind: "mutation",
17173
+ auth: "admin"
17174
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17037
17175
  success: literal(true),
17038
17176
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17039
17177
  effectiveModelId: string().nullable(),
@@ -17049,9 +17187,10 @@ method(object({
17049
17187
  }), object({ success: literal(true) }), {
17050
17188
  kind: "mutation",
17051
17189
  auth: "admin"
17052
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17190
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17053
17191
  deviceId: number(),
17054
17192
  agentNodeId: string(),
17193
+ deviceKey: string(),
17055
17194
  addonId: string(),
17056
17195
  patch: CameraStepOverridePatchSchema.nullable()
17057
17196
  }), object({ success: literal(true) }), {
@@ -17088,14 +17227,13 @@ method(object({
17088
17227
  });
17089
17228
  /**
17090
17229
  * server-management — per-NODE singleton capability for a node's ROOT
17091
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17092
- * agents).
17230
+ * package lifecycle (runtime-updatable node packages).
17093
17231
  *
17094
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
17095
- * on agents) carries the whole software stack in its npm dep tree, so ONE
17096
- * version describes the node. Updates install into
17097
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
17098
- * starter (probation boot + auto-rollback to N-1).
17232
+ * Every node role runs the SAME root package (`@camstack/server`), which
17233
+ * carries the whole software stack in its npm dep tree, so ONE version
17234
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17235
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17236
+ * no auto-rollback).
17099
17237
  *
17100
17238
  * Providers:
17101
17239
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17203,7 +17341,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17203
17341
  /** Explicit target version; omitted = latest from the registry. */
17204
17342
  version: string().optional() }), ServerUpdateActionResultSchema, {
17205
17343
  kind: "mutation",
17206
- auth: "admin"
17344
+ auth: "admin",
17345
+ timeoutMs: 16 * 6e4
17207
17346
  }), method(_void(), ServerUpdateActionResultSchema, {
17208
17347
  kind: "mutation",
17209
17348
  auth: "admin"
@@ -18292,22 +18431,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18292
18431
  var RestartAddonResultSchema = unknown();
18293
18432
  var InstallPackageResultSchema = unknown();
18294
18433
  var ReloadPackagesResultSchema = unknown();
18295
- /**
18296
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18297
- * server restarts so the admin UI can react to the `restartingAt`
18298
- * timestamp (shows reconnect overlay). The transition from
18299
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18300
- * `system.restart-completed` event after the new process boots.
18301
- *
18302
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18303
- */
18304
- var UpdateFrameworkPackageResultSchema = object({
18305
- packageName: string(),
18306
- fromVersion: string(),
18307
- toVersion: string(),
18308
- /** Ms-epoch the server scheduled its self-restart. */
18309
- restartingAt: number()
18310
- });
18311
18434
  var BulkUpdateItemStatusSchema = _enum([
18312
18435
  "queued",
18313
18436
  "updating",
@@ -18435,13 +18558,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18435
18558
  }), object({ success: literal(true) }), {
18436
18559
  kind: "mutation",
18437
18560
  auth: "admin"
18438
- }), method(object({
18439
- packageName: string().min(1),
18440
- version: string().optional(),
18441
- deferRestart: boolean().optional()
18442
- }), UpdateFrameworkPackageResultSchema, {
18443
- kind: "mutation",
18444
- auth: "admin"
18445
18561
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18446
18562
  kind: "mutation",
18447
18563
  auth: "admin"
@@ -19307,10 +19423,10 @@ var TopologyCategorySchema = object({
19307
19423
  addons: array(TopologyCategoryAddonSchema).readonly()
19308
19424
  });
19309
19425
  /**
19310
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19311
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19312
- * version visibility for the Server management surface. Nullable: offline
19313
- * rows and pre-phase-2 nodes report none.
19426
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19427
+ * root package for every node role) as reported by its `registerNode`
19428
+ * manifest — version visibility for the Server management surface. Nullable:
19429
+ * offline rows and nodes that never reported one.
19314
19430
  */
19315
19431
  var TopologyRootPackageSchema = object({
19316
19432
  name: string(),
@@ -19698,17 +19814,28 @@ var PlatformScoreSchema = object({
19698
19814
  format: _enum([
19699
19815
  "onnx",
19700
19816
  "coreml",
19701
- "openvino"
19817
+ "openvino",
19818
+ "tflite"
19702
19819
  ]),
19703
19820
  score: number(),
19704
19821
  reason: string(),
19705
19822
  available: boolean()
19706
19823
  });
19824
+ var InferenceDeviceDescriptorSchema = object({
19825
+ key: string(),
19826
+ backend: string(),
19827
+ device: string(),
19828
+ format: ModelFormatSchema,
19829
+ runtime: literal("python"),
19830
+ score: number(),
19831
+ available: boolean()
19832
+ });
19707
19833
  var PlatformCapabilitiesSchema = object({
19708
19834
  hardware: HardwareInfoSchema,
19709
19835
  scores: array(PlatformScoreSchema).readonly(),
19710
19836
  bestScore: PlatformScoreSchema,
19711
- pythonPath: string().nullable()
19837
+ pythonPath: string().nullable(),
19838
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
19712
19839
  });
19713
19840
  var ModelRequirementSchema = object({
19714
19841
  modelId: string(),
@@ -20587,12 +20714,6 @@ Object.freeze({
20587
20714
  addonId: null,
20588
20715
  access: "delete"
20589
20716
  },
20590
- "addons.updateFrameworkPackage": {
20591
- capName: "addons",
20592
- capScope: "system",
20593
- addonId: null,
20594
- access: "create"
20595
- },
20596
20717
  "addons.updatePackage": {
20597
20718
  capName: "addons",
20598
20719
  capScope: "system",
@@ -23365,12 +23486,6 @@ Object.freeze({
23365
23486
  addonId: null,
23366
23487
  access: "view"
23367
23488
  },
23368
- "pipelineExecutor.reprobeEngine": {
23369
- capName: "pipeline-executor",
23370
- capScope: "system",
23371
- addonId: null,
23372
- access: "create"
23373
- },
23374
23489
  "pipelineExecutor.runAudioTest": {
23375
23490
  capName: "pipeline-executor",
23376
23491
  capScope: "system",
@@ -23521,6 +23636,12 @@ Object.freeze({
23521
23636
  addonId: null,
23522
23637
  access: "view"
23523
23638
  },
23639
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23640
+ capName: "pipeline-orchestrator",
23641
+ capScope: "system",
23642
+ addonId: null,
23643
+ access: "view"
23644
+ },
23524
23645
  "pipelineOrchestrator.getPipelineAssignment": {
23525
23646
  capName: "pipeline-orchestrator",
23526
23647
  capScope: "system",
@@ -23533,6 +23654,12 @@ Object.freeze({
23533
23654
  addonId: null,
23534
23655
  access: "view"
23535
23656
  },
23657
+ "pipelineOrchestrator.getPipelineDevicePin": {
23658
+ capName: "pipeline-orchestrator",
23659
+ capScope: "system",
23660
+ addonId: null,
23661
+ access: "view"
23662
+ },
23536
23663
  "pipelineOrchestrator.listAgentSettings": {
23537
23664
  capName: "pipeline-orchestrator",
23538
23665
  capScope: "system",
@@ -23575,19 +23702,19 @@ Object.freeze({
23575
23702
  addonId: null,
23576
23703
  access: "create"
23577
23704
  },
23578
- "pipelineOrchestrator.setAgentAddonDefaults": {
23705
+ "pipelineOrchestrator.setAgentCapabilities": {
23579
23706
  capName: "pipeline-orchestrator",
23580
23707
  capScope: "system",
23581
23708
  addonId: null,
23582
23709
  access: "create"
23583
23710
  },
23584
- "pipelineOrchestrator.setAgentCapabilities": {
23711
+ "pipelineOrchestrator.setAgentDetectWeight": {
23585
23712
  capName: "pipeline-orchestrator",
23586
23713
  capScope: "system",
23587
23714
  addonId: null,
23588
23715
  access: "create"
23589
23716
  },
23590
- "pipelineOrchestrator.setAgentDetectWeight": {
23717
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23591
23718
  capName: "pipeline-orchestrator",
23592
23719
  capScope: "system",
23593
23720
  addonId: null,
@@ -23629,6 +23756,12 @@ Object.freeze({
23629
23756
  addonId: null,
23630
23757
  access: "create"
23631
23758
  },
23759
+ "pipelineOrchestrator.setPipelineDevicePin": {
23760
+ capName: "pipeline-orchestrator",
23761
+ capScope: "system",
23762
+ addonId: null,
23763
+ access: "create"
23764
+ },
23632
23765
  "pipelineOrchestrator.unassignAudio": {
23633
23766
  capName: "pipeline-orchestrator",
23634
23767
  capScope: "system",
@@ -25181,32 +25314,6 @@ Object.freeze({
25181
25314
  "network-access": "ingress",
25182
25315
  "smtp-provider": "email"
25183
25316
  });
25184
- var frameworkSwapPackageSchema = object({
25185
- name: string(),
25186
- stagedPath: string(),
25187
- backupPath: string(),
25188
- toVersion: string(),
25189
- fromVersion: string().nullable()
25190
- });
25191
- object({
25192
- jobId: string(),
25193
- taskId: string(),
25194
- packages: array(frameworkSwapPackageSchema),
25195
- requestedAtMs: number(),
25196
- schemaVersion: literal(1)
25197
- });
25198
- object({
25199
- jobId: string(),
25200
- taskId: string(),
25201
- backups: array(object({
25202
- name: string(),
25203
- backupPath: string(),
25204
- livePath: string()
25205
- })),
25206
- appliedAtMs: number(),
25207
- bootAttempts: number(),
25208
- schemaVersion: literal(1)
25209
- });
25210
25317
  //#endregion
25211
25318
  //#region src/shared.ts
25212
25319
  /**
@@ -7092,6 +7092,17 @@ var ModelCatalogEntrySchema = object({
7092
7092
  "imagenet",
7093
7093
  "none"
7094
7094
  ]).optional(),
7095
+ /**
7096
+ * The model already applies softmax IN-GRAPH — its raw output is a
7097
+ * probability distribution, not logits. When set, the `softmax`
7098
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7099
+ * probability vector collapses it toward uniform (top-1 score craters far
7100
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7101
+ * the output is raw logits and the postprocessor applies softmax (the normal
7102
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7103
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7104
+ */
7105
+ outputProbabilities: boolean().optional(),
7095
7106
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7096
7107
  /**
7097
7108
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -11105,10 +11116,7 @@ var ConfigUISchemaNullableBridge = custom();
11105
11116
  var InferenceCapabilitiesBridge = custom();
11106
11117
  var ModelAvailabilityListBridge = custom();
11107
11118
  var PipelineRunResultBridge = custom();
11108
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11109
- kind: "mutation",
11110
- auth: "admin"
11111
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11119
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11112
11120
  modelId: string(),
11113
11121
  settings: record(string(), unknown()).readonly()
11114
11122
  }))), method(object({ steps: record(string(), object({
@@ -11168,13 +11176,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11168
11176
  * (inputClasses ≠ null) are skipped and served per-track via
11169
11177
  * pipelineRunner.runDetailSubtree (two-plane design).
11170
11178
  */
11171
- plane: _enum(["full", "frame"]).optional()
11179
+ plane: _enum(["full", "frame"]).optional(),
11180
+ /**
11181
+ * Inference-device selector (Phase 2 multi-device). Format
11182
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11183
+ * Omitted ⇒ the runner's default device (current single-engine
11184
+ * behaviour). Selects WHICH device pool of the node runs the call.
11185
+ */
11186
+ deviceKey: string().optional()
11172
11187
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11173
11188
  engine: PipelineEngineChoiceSchema.optional(),
11174
11189
  steps: array(PipelineStepInputSchema).min(1),
11175
11190
  frames: array(FrameInputSchema).min(1).max(255),
11176
11191
  deviceId: number().optional(),
11177
- sessionId: string().optional()
11192
+ sessionId: string().optional(),
11193
+ /**
11194
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11195
+ * the batch to the Python pool's bench preprocess cache
11196
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11197
+ * preprocessed ONCE and every later inference is a pure-inference cache
11198
+ * hit — the sustained-throughput run measures inference, not
11199
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11200
+ * full preprocess every call, correct). Fresh per sustained run;
11201
+ * released via `uncacheFrame`.
11202
+ */
11203
+ frameId: number().int().nonnegative().optional(),
11204
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11205
+ deviceKey: string().optional()
11178
11206
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11179
11207
  data: _instanceof(Uint8Array),
11180
11208
  width: number().int().positive(),
@@ -11206,8 +11234,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11206
11234
  * - `runtime` — main camera-serving engine (no idle TTL).
11207
11235
  * - `warm-override` — benchmark/test override held in the warm
11208
11236
  * cache; auto-disposed after the idle TTL.
11237
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11238
+ * multi-device, keyed by `deviceKey`) resolved
11239
+ * via `resolveDeviceFactory`. Runs alongside the
11240
+ * `runtime` engine on a DIFFERENT accelerator
11241
+ * (NPU / iGPU / Coral) — this is how the
11242
+ * Engines tab shows all pools running at once.
11209
11243
  */
11210
- kind: _enum(["runtime", "warm-override"]),
11244
+ kind: _enum([
11245
+ "runtime",
11246
+ "warm-override",
11247
+ "device-pool"
11248
+ ]),
11211
11249
  /** Native pid of the underlying Python pool (null when no pool). */
11212
11250
  poolPid: number().nullable(),
11213
11251
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11309,7 +11347,21 @@ var NativeCropResultSchema = object({
11309
11347
  /** Packed rgb (24-bit) pixels of the crop. */
11310
11348
  bytes: _instanceof(Uint8Array),
11311
11349
  width: number().int().positive(),
11312
- height: number().int().positive()
11350
+ height: number().int().positive(),
11351
+ /**
11352
+ * Which source served this crop, so a quality-sensitive consumer (the native
11353
+ * `keyFrame`) can reject a degraded fallback:
11354
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
11355
+ * quality path).
11356
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
11357
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
11358
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
11359
+ *
11360
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
11361
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
11362
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
11363
+ */
11364
+ tier: _enum(["native", "ram-fullframe"]).optional()
11313
11365
  });
11314
11366
  /** Parent detection context passed to `runDetailSubtree` — the crop's
11315
11367
  * originating detection, in FRAME-space coordinates. Reuses
@@ -11549,7 +11601,14 @@ var RunnerCameraConfigSchema = object({
11549
11601
  * camera's detect node differs from its source-owner (P2d, gated by the
11550
11602
  * `remoteSourcingNodes` rollout setting).
11551
11603
  */
11552
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11604
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11605
+ /**
11606
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11607
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11608
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11609
+ * this only selects WHICH device pool of that node runs the session.
11610
+ */
11611
+ deviceKey: string().optional()
11553
11612
  });
11554
11613
  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;
11555
11614
  /**
@@ -11570,6 +11629,19 @@ var RunnerLocalLoadSchema = object({
11570
11629
  avgInferenceTimeMs: number(),
11571
11630
  /** Total queue depth across motion + detection queues. */
11572
11631
  queueDepthTotal: number(),
11632
+ /**
11633
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11634
+ * this runner currently has attached cameras on, so the orchestrator's second
11635
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11636
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11637
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11638
+ */
11639
+ devices: array(object({
11640
+ deviceKey: string(),
11641
+ backend: string(),
11642
+ attachedCameras: number(),
11643
+ queueDepthTotal: number()
11644
+ })).default([]),
11573
11645
  /** Hardware capability flags reported by this node. */
11574
11646
  hardware: object({
11575
11647
  hasGpu: boolean(),
@@ -13045,6 +13117,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13045
13117
  kind: "mutation",
13046
13118
  auth: "admin"
13047
13119
  });
13120
+ 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;
13121
+ new Set(Object.values(DeviceType));
13048
13122
  /**
13049
13123
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13050
13124
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -14618,7 +14692,8 @@ var LinkedDeviceSchema = object({
14618
14692
  deviceId: number(),
14619
14693
  name: string(),
14620
14694
  location: string().nullable(),
14621
- features: array(string())
14695
+ features: array(string()),
14696
+ producesTrackedEvents: boolean().optional()
14622
14697
  });
14623
14698
  var SavedDeviceRowSchema = object({
14624
14699
  /** Numeric id reserved at allocateDeviceId time. */
@@ -16248,6 +16323,7 @@ var TrackSchema = object({
16248
16323
  deviceId: number(),
16249
16324
  className: string(),
16250
16325
  label: string().optional(),
16326
+ producingDeviceName: string().optional(),
16251
16327
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16252
16328
  source: TrackSourceSchema.optional(),
16253
16329
  firstSeen: number(),
@@ -16385,7 +16461,8 @@ var MediaFileKindEnum = _enum([
16385
16461
  "fullFrameBoxed",
16386
16462
  "faceCrop",
16387
16463
  "plateCrop",
16388
- "keyFrame"
16464
+ "keyFrame",
16465
+ "keyFrameSmall"
16389
16466
  ]);
16390
16467
  var MediaFileSchema = object({
16391
16468
  key: string(),
@@ -16714,13 +16791,11 @@ var PipelineTemplateSchema = object({
16714
16791
  createdAt: string(),
16715
16792
  updatedAt: string()
16716
16793
  });
16717
- var AgentAddonConfigSchema = object({
16718
- enabled: boolean(),
16794
+ var DeviceStepConfigSchema = object({
16719
16795
  modelId: string().optional(),
16720
- settings: record(string(), unknown()).readonly()
16796
+ settings: record(string(), unknown()).optional()
16721
16797
  });
16722
16798
  var AgentPipelineSettingsSchema = object({
16723
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
16724
16799
  maxCameras: number().int().nonnegative().nullable().default(null),
16725
16800
  /** Per-node detection weight (relative share for the quota balancer). */
16726
16801
  detectWeight: number().positive().optional(),
@@ -16744,7 +16819,22 @@ var AgentPipelineSettingsSchema = object({
16744
16819
  * it already uses to reach the hub). Set this only when the auto-detected
16745
16820
  * address is wrong (multi-homed host, NAT, custom interface).
16746
16821
  */
16747
- reachableHost: string().optional()
16822
+ reachableHost: string().optional(),
16823
+ /**
16824
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
16825
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
16826
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
16827
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
16828
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
16829
+ * the default model/settings for every camera landing on that accelerator;
16830
+ * a stepId absent ⇒ the step uses that device's format default.
16831
+ */
16832
+ inferenceDevices: record(string(), object({
16833
+ enabled: boolean(),
16834
+ weight: number().positive().optional(),
16835
+ maxSessions: number().int().positive().optional(),
16836
+ steps: record(string(), DeviceStepConfigSchema).optional()
16837
+ })).optional()
16748
16838
  });
16749
16839
  var CameraPipelineForAgentSchema = object({
16750
16840
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16754,14 +16844,13 @@ var CameraPipelineForAgentSchema = object({
16754
16844
  }).nullable()
16755
16845
  });
16756
16846
  var CameraStepOverridePatchSchema = object({
16757
- enabled: boolean().optional(),
16758
16847
  modelId: string().optional(),
16759
16848
  settings: record(string(), unknown()).readonly().optional()
16760
16849
  });
16761
16850
  var CameraPipelineSettingsSchema = object({
16762
16851
  pinnedAgentNodeId: string().optional(),
16763
16852
  stepToggles: record(string(), boolean()).optional(),
16764
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
16853
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
16765
16854
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
16766
16855
  });
16767
16856
  /**
@@ -16975,6 +17064,44 @@ var CameraStatusSchema = object({
16975
17064
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
16976
17065
  fetchedAt: number()
16977
17066
  });
17067
+ var NodeInferenceDeviceSchema = object({
17068
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17069
+ key: string(),
17070
+ backend: string(),
17071
+ device: string(),
17072
+ format: _enum(MODEL_FORMATS),
17073
+ /** Whether the node's live probe reports the device as usable right now. */
17074
+ available: boolean(),
17075
+ /**
17076
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17077
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17078
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17079
+ * not a balanced target). An explicit stored value always wins; a stored-only
17080
+ * (unavailable) key keeps its stored value.
17081
+ */
17082
+ enabled: boolean(),
17083
+ /** Relative balancer weight for the enabled device (default 1). */
17084
+ weight: number(),
17085
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17086
+ maxSessions: number().nullable(),
17087
+ /** Object-detection model the executor defaults to for this deviceKey. */
17088
+ defaultModelId: string(),
17089
+ /**
17090
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17091
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17092
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17093
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17094
+ * available per format; this is the stored selection that becomes the
17095
+ * default for EVERY camera landing on this accelerator.
17096
+ */
17097
+ steps: record(string(), DeviceStepConfigSchema).optional()
17098
+ });
17099
+ var NodeInferenceDevicesSchema = object({
17100
+ nodeId: string(),
17101
+ /** False when the node's platform-probe was unreachable (no live device set). */
17102
+ reachable: boolean(),
17103
+ devices: array(NodeInferenceDeviceSchema).readonly()
17104
+ });
16978
17105
  method(object({
16979
17106
  deviceId: number(),
16980
17107
  agentNodeId: string()
@@ -16984,7 +17111,13 @@ method(object({
16984
17111
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
16985
17112
  kind: "mutation",
16986
17113
  auth: "admin"
16987
- }), method(_void(), object({ migrated: number() }), {
17114
+ }), method(object({
17115
+ deviceId: number(),
17116
+ deviceKey: string()
17117
+ }), object({ success: literal(true) }), {
17118
+ kind: "mutation",
17119
+ auth: "admin"
17120
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
16988
17121
  kind: "mutation",
16989
17122
  auth: "admin"
16990
17123
  }), 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({
@@ -17018,13 +17151,7 @@ method(object({
17018
17151
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17019
17152
  nodeId: string(),
17020
17153
  settings: AgentPipelineSettingsSchema
17021
- })).readonly()), method(object({
17022
- agentNodeId: string(),
17023
- defaults: record(string(), AgentAddonConfigSchema)
17024
- }), object({ success: literal(true) }), {
17025
- kind: "mutation",
17026
- auth: "admin"
17027
- }), method(object({ agentNodeId: string() }), object({
17154
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17028
17155
  success: boolean(),
17029
17156
  removed: boolean()
17030
17157
  }), {
@@ -17056,7 +17183,18 @@ method(object({
17056
17183
  }), object({ success: literal(true) }), {
17057
17184
  kind: "mutation",
17058
17185
  auth: "admin"
17059
- }), method(object({ agentNodeId: string() }), object({
17186
+ }), method(object({
17187
+ agentNodeId: string(),
17188
+ inferenceDevices: record(string(), object({
17189
+ enabled: boolean(),
17190
+ weight: number().positive().optional(),
17191
+ maxSessions: number().int().positive().optional(),
17192
+ steps: record(string(), DeviceStepConfigSchema).optional()
17193
+ }))
17194
+ }), object({ success: literal(true) }), {
17195
+ kind: "mutation",
17196
+ auth: "admin"
17197
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17060
17198
  success: literal(true),
17061
17199
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17062
17200
  effectiveModelId: string().nullable(),
@@ -17072,9 +17210,10 @@ method(object({
17072
17210
  }), object({ success: literal(true) }), {
17073
17211
  kind: "mutation",
17074
17212
  auth: "admin"
17075
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17213
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17076
17214
  deviceId: number(),
17077
17215
  agentNodeId: string(),
17216
+ deviceKey: string(),
17078
17217
  addonId: string(),
17079
17218
  patch: CameraStepOverridePatchSchema.nullable()
17080
17219
  }), object({ success: literal(true) }), {
@@ -17111,14 +17250,13 @@ method(object({
17111
17250
  });
17112
17251
  /**
17113
17252
  * server-management — per-NODE singleton capability for a node's ROOT
17114
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17115
- * agents).
17253
+ * package lifecycle (runtime-updatable node packages).
17116
17254
  *
17117
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
17118
- * on agents) carries the whole software stack in its npm dep tree, so ONE
17119
- * version describes the node. Updates install into
17120
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
17121
- * starter (probation boot + auto-rollback to N-1).
17255
+ * Every node role runs the SAME root package (`@camstack/server`), which
17256
+ * carries the whole software stack in its npm dep tree, so ONE version
17257
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17258
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17259
+ * no auto-rollback).
17122
17260
  *
17123
17261
  * Providers:
17124
17262
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17226,7 +17364,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17226
17364
  /** Explicit target version; omitted = latest from the registry. */
17227
17365
  version: string().optional() }), ServerUpdateActionResultSchema, {
17228
17366
  kind: "mutation",
17229
- auth: "admin"
17367
+ auth: "admin",
17368
+ timeoutMs: 16 * 6e4
17230
17369
  }), method(_void(), ServerUpdateActionResultSchema, {
17231
17370
  kind: "mutation",
17232
17371
  auth: "admin"
@@ -18315,22 +18454,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18315
18454
  var RestartAddonResultSchema = unknown();
18316
18455
  var InstallPackageResultSchema = unknown();
18317
18456
  var ReloadPackagesResultSchema = unknown();
18318
- /**
18319
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18320
- * server restarts so the admin UI can react to the `restartingAt`
18321
- * timestamp (shows reconnect overlay). The transition from
18322
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18323
- * `system.restart-completed` event after the new process boots.
18324
- *
18325
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18326
- */
18327
- var UpdateFrameworkPackageResultSchema = object({
18328
- packageName: string(),
18329
- fromVersion: string(),
18330
- toVersion: string(),
18331
- /** Ms-epoch the server scheduled its self-restart. */
18332
- restartingAt: number()
18333
- });
18334
18457
  var BulkUpdateItemStatusSchema = _enum([
18335
18458
  "queued",
18336
18459
  "updating",
@@ -18458,13 +18581,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18458
18581
  }), object({ success: literal(true) }), {
18459
18582
  kind: "mutation",
18460
18583
  auth: "admin"
18461
- }), method(object({
18462
- packageName: string().min(1),
18463
- version: string().optional(),
18464
- deferRestart: boolean().optional()
18465
- }), UpdateFrameworkPackageResultSchema, {
18466
- kind: "mutation",
18467
- auth: "admin"
18468
18584
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18469
18585
  kind: "mutation",
18470
18586
  auth: "admin"
@@ -19330,10 +19446,10 @@ var TopologyCategorySchema = object({
19330
19446
  addons: array(TopologyCategoryAddonSchema).readonly()
19331
19447
  });
19332
19448
  /**
19333
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19334
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19335
- * version visibility for the Server management surface. Nullable: offline
19336
- * rows and pre-phase-2 nodes report none.
19449
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19450
+ * root package for every node role) as reported by its `registerNode`
19451
+ * manifest — version visibility for the Server management surface. Nullable:
19452
+ * offline rows and nodes that never reported one.
19337
19453
  */
19338
19454
  var TopologyRootPackageSchema = object({
19339
19455
  name: string(),
@@ -19721,17 +19837,28 @@ var PlatformScoreSchema = object({
19721
19837
  format: _enum([
19722
19838
  "onnx",
19723
19839
  "coreml",
19724
- "openvino"
19840
+ "openvino",
19841
+ "tflite"
19725
19842
  ]),
19726
19843
  score: number(),
19727
19844
  reason: string(),
19728
19845
  available: boolean()
19729
19846
  });
19847
+ var InferenceDeviceDescriptorSchema = object({
19848
+ key: string(),
19849
+ backend: string(),
19850
+ device: string(),
19851
+ format: ModelFormatSchema,
19852
+ runtime: literal("python"),
19853
+ score: number(),
19854
+ available: boolean()
19855
+ });
19730
19856
  var PlatformCapabilitiesSchema = object({
19731
19857
  hardware: HardwareInfoSchema,
19732
19858
  scores: array(PlatformScoreSchema).readonly(),
19733
19859
  bestScore: PlatformScoreSchema,
19734
- pythonPath: string().nullable()
19860
+ pythonPath: string().nullable(),
19861
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
19735
19862
  });
19736
19863
  var ModelRequirementSchema = object({
19737
19864
  modelId: string(),
@@ -20610,12 +20737,6 @@ Object.freeze({
20610
20737
  addonId: null,
20611
20738
  access: "delete"
20612
20739
  },
20613
- "addons.updateFrameworkPackage": {
20614
- capName: "addons",
20615
- capScope: "system",
20616
- addonId: null,
20617
- access: "create"
20618
- },
20619
20740
  "addons.updatePackage": {
20620
20741
  capName: "addons",
20621
20742
  capScope: "system",
@@ -23388,12 +23509,6 @@ Object.freeze({
23388
23509
  addonId: null,
23389
23510
  access: "view"
23390
23511
  },
23391
- "pipelineExecutor.reprobeEngine": {
23392
- capName: "pipeline-executor",
23393
- capScope: "system",
23394
- addonId: null,
23395
- access: "create"
23396
- },
23397
23512
  "pipelineExecutor.runAudioTest": {
23398
23513
  capName: "pipeline-executor",
23399
23514
  capScope: "system",
@@ -23544,6 +23659,12 @@ Object.freeze({
23544
23659
  addonId: null,
23545
23660
  access: "view"
23546
23661
  },
23662
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23663
+ capName: "pipeline-orchestrator",
23664
+ capScope: "system",
23665
+ addonId: null,
23666
+ access: "view"
23667
+ },
23547
23668
  "pipelineOrchestrator.getPipelineAssignment": {
23548
23669
  capName: "pipeline-orchestrator",
23549
23670
  capScope: "system",
@@ -23556,6 +23677,12 @@ Object.freeze({
23556
23677
  addonId: null,
23557
23678
  access: "view"
23558
23679
  },
23680
+ "pipelineOrchestrator.getPipelineDevicePin": {
23681
+ capName: "pipeline-orchestrator",
23682
+ capScope: "system",
23683
+ addonId: null,
23684
+ access: "view"
23685
+ },
23559
23686
  "pipelineOrchestrator.listAgentSettings": {
23560
23687
  capName: "pipeline-orchestrator",
23561
23688
  capScope: "system",
@@ -23598,19 +23725,19 @@ Object.freeze({
23598
23725
  addonId: null,
23599
23726
  access: "create"
23600
23727
  },
23601
- "pipelineOrchestrator.setAgentAddonDefaults": {
23728
+ "pipelineOrchestrator.setAgentCapabilities": {
23602
23729
  capName: "pipeline-orchestrator",
23603
23730
  capScope: "system",
23604
23731
  addonId: null,
23605
23732
  access: "create"
23606
23733
  },
23607
- "pipelineOrchestrator.setAgentCapabilities": {
23734
+ "pipelineOrchestrator.setAgentDetectWeight": {
23608
23735
  capName: "pipeline-orchestrator",
23609
23736
  capScope: "system",
23610
23737
  addonId: null,
23611
23738
  access: "create"
23612
23739
  },
23613
- "pipelineOrchestrator.setAgentDetectWeight": {
23740
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23614
23741
  capName: "pipeline-orchestrator",
23615
23742
  capScope: "system",
23616
23743
  addonId: null,
@@ -23652,6 +23779,12 @@ Object.freeze({
23652
23779
  addonId: null,
23653
23780
  access: "create"
23654
23781
  },
23782
+ "pipelineOrchestrator.setPipelineDevicePin": {
23783
+ capName: "pipeline-orchestrator",
23784
+ capScope: "system",
23785
+ addonId: null,
23786
+ access: "create"
23787
+ },
23655
23788
  "pipelineOrchestrator.unassignAudio": {
23656
23789
  capName: "pipeline-orchestrator",
23657
23790
  capScope: "system",
@@ -25204,32 +25337,6 @@ Object.freeze({
25204
25337
  "network-access": "ingress",
25205
25338
  "smtp-provider": "email"
25206
25339
  });
25207
- var frameworkSwapPackageSchema = object({
25208
- name: string(),
25209
- stagedPath: string(),
25210
- backupPath: string(),
25211
- toVersion: string(),
25212
- fromVersion: string().nullable()
25213
- });
25214
- object({
25215
- jobId: string(),
25216
- taskId: string(),
25217
- packages: array(frameworkSwapPackageSchema),
25218
- requestedAtMs: number(),
25219
- schemaVersion: literal(1)
25220
- });
25221
- object({
25222
- jobId: string(),
25223
- taskId: string(),
25224
- backups: array(object({
25225
- name: string(),
25226
- backupPath: string(),
25227
- livePath: string()
25228
- })),
25229
- appliedAtMs: number(),
25230
- bootAttempts: number(),
25231
- schemaVersion: literal(1)
25232
- });
25233
25340
  //#endregion
25234
25341
  //#region src/shared.ts
25235
25342
  /**
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-CZDYRlzo.js");
5
+ const require_shared = require("./shared-C77pnEyI.js");
6
6
  let node_stream = require("node:stream");
7
7
  let webdav = require("webdav");
8
8
  //#region src/providers/webdav/webdav-config-schema.ts
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-DW5ccoQG.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-BXdymO1F.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { AuthType, createClient } from "webdav";
4
4
  //#region src/providers/webdav/webdav-config-schema.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-remote-storage",
3
- "version": "1.1.29",
3
+ "version": "1.2.1",
4
4
  "description": "Remote storage providers (SFTP, S3, WebDAV) — unifies remote backends behind the storage-provider cap",
5
5
  "keywords": [
6
6
  "camstack",