@camstack/addon-import-alexa 0.1.25 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +213 -106
  2. package/dist/addon.mjs +213 -106
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7137,6 +7137,17 @@ var ModelCatalogEntrySchema = object({
7137
7137
  "imagenet",
7138
7138
  "none"
7139
7139
  ]).optional(),
7140
+ /**
7141
+ * The model already applies softmax IN-GRAPH — its raw output is a
7142
+ * probability distribution, not logits. When set, the `softmax`
7143
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7144
+ * probability vector collapses it toward uniform (top-1 score craters far
7145
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7146
+ * the output is raw logits and the postprocessor applies softmax (the normal
7147
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7148
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7149
+ */
7150
+ outputProbabilities: boolean().optional(),
7140
7151
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7141
7152
  /**
7142
7153
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12544,10 +12555,7 @@ var ConfigUISchemaNullableBridge = custom();
12544
12555
  var InferenceCapabilitiesBridge = custom();
12545
12556
  var ModelAvailabilityListBridge = custom();
12546
12557
  var PipelineRunResultBridge = custom();
12547
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12548
- kind: "mutation",
12549
- auth: "admin"
12550
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12558
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12551
12559
  modelId: string(),
12552
12560
  settings: record(string(), unknown()).readonly()
12553
12561
  }))), method(object({ steps: record(string(), object({
@@ -12607,13 +12615,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12607
12615
  * (inputClasses ≠ null) are skipped and served per-track via
12608
12616
  * pipelineRunner.runDetailSubtree (two-plane design).
12609
12617
  */
12610
- plane: _enum(["full", "frame"]).optional()
12618
+ plane: _enum(["full", "frame"]).optional(),
12619
+ /**
12620
+ * Inference-device selector (Phase 2 multi-device). Format
12621
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12622
+ * Omitted ⇒ the runner's default device (current single-engine
12623
+ * behaviour). Selects WHICH device pool of the node runs the call.
12624
+ */
12625
+ deviceKey: string().optional()
12611
12626
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12612
12627
  engine: PipelineEngineChoiceSchema.optional(),
12613
12628
  steps: array(PipelineStepInputSchema).min(1),
12614
12629
  frames: array(FrameInputSchema).min(1).max(255),
12615
12630
  deviceId: number().optional(),
12616
- sessionId: string().optional()
12631
+ sessionId: string().optional(),
12632
+ /**
12633
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12634
+ * the batch to the Python pool's bench preprocess cache
12635
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12636
+ * preprocessed ONCE and every later inference is a pure-inference cache
12637
+ * hit — the sustained-throughput run measures inference, not
12638
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12639
+ * full preprocess every call, correct). Fresh per sustained run;
12640
+ * released via `uncacheFrame`.
12641
+ */
12642
+ frameId: number().int().nonnegative().optional(),
12643
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12644
+ deviceKey: string().optional()
12617
12645
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12618
12646
  data: _instanceof(Uint8Array),
12619
12647
  width: number().int().positive(),
@@ -12645,8 +12673,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12645
12673
  * - `runtime` — main camera-serving engine (no idle TTL).
12646
12674
  * - `warm-override` — benchmark/test override held in the warm
12647
12675
  * cache; auto-disposed after the idle TTL.
12676
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12677
+ * multi-device, keyed by `deviceKey`) resolved
12678
+ * via `resolveDeviceFactory`. Runs alongside the
12679
+ * `runtime` engine on a DIFFERENT accelerator
12680
+ * (NPU / iGPU / Coral) — this is how the
12681
+ * Engines tab shows all pools running at once.
12648
12682
  */
12649
- kind: _enum(["runtime", "warm-override"]),
12683
+ kind: _enum([
12684
+ "runtime",
12685
+ "warm-override",
12686
+ "device-pool"
12687
+ ]),
12650
12688
  /** Native pid of the underlying Python pool (null when no pool). */
12651
12689
  poolPid: number().nullable(),
12652
12690
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12781,7 +12819,21 @@ var NativeCropResultSchema = object({
12781
12819
  /** Packed rgb (24-bit) pixels of the crop. */
12782
12820
  bytes: _instanceof(Uint8Array),
12783
12821
  width: number().int().positive(),
12784
- height: number().int().positive()
12822
+ height: number().int().positive(),
12823
+ /**
12824
+ * Which source served this crop, so a quality-sensitive consumer (the native
12825
+ * `keyFrame`) can reject a degraded fallback:
12826
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12827
+ * quality path).
12828
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12829
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12830
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12831
+ *
12832
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12833
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12834
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12835
+ */
12836
+ tier: _enum(["native", "ram-fullframe"]).optional()
12785
12837
  });
12786
12838
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12787
12839
  * originating detection, in FRAME-space coordinates. Reuses
@@ -13021,7 +13073,14 @@ var RunnerCameraConfigSchema = object({
13021
13073
  * camera's detect node differs from its source-owner (P2d, gated by the
13022
13074
  * `remoteSourcingNodes` rollout setting).
13023
13075
  */
13024
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13076
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13077
+ /**
13078
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13079
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13080
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13081
+ * this only selects WHICH device pool of that node runs the session.
13082
+ */
13083
+ deviceKey: string().optional()
13025
13084
  });
13026
13085
  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;
13027
13086
  /**
@@ -13042,6 +13101,19 @@ var RunnerLocalLoadSchema = object({
13042
13101
  avgInferenceTimeMs: number(),
13043
13102
  /** Total queue depth across motion + detection queues. */
13044
13103
  queueDepthTotal: number(),
13104
+ /**
13105
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13106
+ * this runner currently has attached cameras on, so the orchestrator's second
13107
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13108
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13109
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13110
+ */
13111
+ devices: array(object({
13112
+ deviceKey: string(),
13113
+ backend: string(),
13114
+ attachedCameras: number(),
13115
+ queueDepthTotal: number()
13116
+ })).default([]),
13045
13117
  /** Hardware capability flags reported by this node. */
13046
13118
  hardware: object({
13047
13119
  hasGpu: boolean(),
@@ -16190,6 +16262,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16190
16262
  return toDeviceSummary(device, this.addonId);
16191
16263
  }
16192
16264
  };
16265
+ 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;
16266
+ new Set(Object.values(DeviceType));
16193
16267
  /**
16194
16268
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16195
16269
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17848,7 +17922,8 @@ var LinkedDeviceSchema = object({
17848
17922
  deviceId: number(),
17849
17923
  name: string(),
17850
17924
  location: string().nullable(),
17851
- features: array(string())
17925
+ features: array(string()),
17926
+ producesTrackedEvents: boolean().optional()
17852
17927
  });
17853
17928
  var SavedDeviceRowSchema = object({
17854
17929
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19478,6 +19553,7 @@ var TrackSchema = object({
19478
19553
  deviceId: number(),
19479
19554
  className: string(),
19480
19555
  label: string().optional(),
19556
+ producingDeviceName: string().optional(),
19481
19557
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19482
19558
  source: TrackSourceSchema.optional(),
19483
19559
  firstSeen: number(),
@@ -19615,7 +19691,8 @@ var MediaFileKindEnum = _enum([
19615
19691
  "fullFrameBoxed",
19616
19692
  "faceCrop",
19617
19693
  "plateCrop",
19618
- "keyFrame"
19694
+ "keyFrame",
19695
+ "keyFrameSmall"
19619
19696
  ]);
19620
19697
  var MediaFileSchema = object({
19621
19698
  key: string(),
@@ -19944,13 +20021,11 @@ var PipelineTemplateSchema = object({
19944
20021
  createdAt: string(),
19945
20022
  updatedAt: string()
19946
20023
  });
19947
- var AgentAddonConfigSchema = object({
19948
- enabled: boolean(),
20024
+ var DeviceStepConfigSchema = object({
19949
20025
  modelId: string().optional(),
19950
- settings: record(string(), unknown()).readonly()
20026
+ settings: record(string(), unknown()).optional()
19951
20027
  });
19952
20028
  var AgentPipelineSettingsSchema = object({
19953
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19954
20029
  maxCameras: number().int().nonnegative().nullable().default(null),
19955
20030
  /** Per-node detection weight (relative share for the quota balancer). */
19956
20031
  detectWeight: number().positive().optional(),
@@ -19974,7 +20049,22 @@ var AgentPipelineSettingsSchema = object({
19974
20049
  * it already uses to reach the hub). Set this only when the auto-detected
19975
20050
  * address is wrong (multi-homed host, NAT, custom interface).
19976
20051
  */
19977
- reachableHost: string().optional()
20052
+ reachableHost: string().optional(),
20053
+ /**
20054
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20055
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20056
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20057
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20058
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20059
+ * the default model/settings for every camera landing on that accelerator;
20060
+ * a stepId absent ⇒ the step uses that device's format default.
20061
+ */
20062
+ inferenceDevices: record(string(), object({
20063
+ enabled: boolean(),
20064
+ weight: number().positive().optional(),
20065
+ maxSessions: number().int().positive().optional(),
20066
+ steps: record(string(), DeviceStepConfigSchema).optional()
20067
+ })).optional()
19978
20068
  });
19979
20069
  var CameraPipelineForAgentSchema = object({
19980
20070
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19984,14 +20074,13 @@ var CameraPipelineForAgentSchema = object({
19984
20074
  }).nullable()
19985
20075
  });
19986
20076
  var CameraStepOverridePatchSchema = object({
19987
- enabled: boolean().optional(),
19988
20077
  modelId: string().optional(),
19989
20078
  settings: record(string(), unknown()).readonly().optional()
19990
20079
  });
19991
20080
  var CameraPipelineSettingsSchema = object({
19992
20081
  pinnedAgentNodeId: string().optional(),
19993
20082
  stepToggles: record(string(), boolean()).optional(),
19994
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20083
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19995
20084
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19996
20085
  });
19997
20086
  /**
@@ -20205,6 +20294,44 @@ var CameraStatusSchema = object({
20205
20294
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20206
20295
  fetchedAt: number()
20207
20296
  });
20297
+ var NodeInferenceDeviceSchema = object({
20298
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20299
+ key: string(),
20300
+ backend: string(),
20301
+ device: string(),
20302
+ format: _enum(MODEL_FORMATS),
20303
+ /** Whether the node's live probe reports the device as usable right now. */
20304
+ available: boolean(),
20305
+ /**
20306
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20307
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20308
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20309
+ * not a balanced target). An explicit stored value always wins; a stored-only
20310
+ * (unavailable) key keeps its stored value.
20311
+ */
20312
+ enabled: boolean(),
20313
+ /** Relative balancer weight for the enabled device (default 1). */
20314
+ weight: number(),
20315
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20316
+ maxSessions: number().nullable(),
20317
+ /** Object-detection model the executor defaults to for this deviceKey. */
20318
+ defaultModelId: string(),
20319
+ /**
20320
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20321
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20322
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20323
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20324
+ * available per format; this is the stored selection that becomes the
20325
+ * default for EVERY camera landing on this accelerator.
20326
+ */
20327
+ steps: record(string(), DeviceStepConfigSchema).optional()
20328
+ });
20329
+ var NodeInferenceDevicesSchema = object({
20330
+ nodeId: string(),
20331
+ /** False when the node's platform-probe was unreachable (no live device set). */
20332
+ reachable: boolean(),
20333
+ devices: array(NodeInferenceDeviceSchema).readonly()
20334
+ });
20208
20335
  method(object({
20209
20336
  deviceId: number(),
20210
20337
  agentNodeId: string()
@@ -20214,7 +20341,13 @@ method(object({
20214
20341
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20215
20342
  kind: "mutation",
20216
20343
  auth: "admin"
20217
- }), method(_void(), object({ migrated: number() }), {
20344
+ }), method(object({
20345
+ deviceId: number(),
20346
+ deviceKey: string()
20347
+ }), object({ success: literal(true) }), {
20348
+ kind: "mutation",
20349
+ auth: "admin"
20350
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20218
20351
  kind: "mutation",
20219
20352
  auth: "admin"
20220
20353
  }), 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({
@@ -20248,13 +20381,7 @@ method(object({
20248
20381
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20249
20382
  nodeId: string(),
20250
20383
  settings: AgentPipelineSettingsSchema
20251
- })).readonly()), method(object({
20252
- agentNodeId: string(),
20253
- defaults: record(string(), AgentAddonConfigSchema)
20254
- }), object({ success: literal(true) }), {
20255
- kind: "mutation",
20256
- auth: "admin"
20257
- }), method(object({ agentNodeId: string() }), object({
20384
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20258
20385
  success: boolean(),
20259
20386
  removed: boolean()
20260
20387
  }), {
@@ -20286,7 +20413,18 @@ method(object({
20286
20413
  }), object({ success: literal(true) }), {
20287
20414
  kind: "mutation",
20288
20415
  auth: "admin"
20289
- }), method(object({ agentNodeId: string() }), object({
20416
+ }), method(object({
20417
+ agentNodeId: string(),
20418
+ inferenceDevices: record(string(), object({
20419
+ enabled: boolean(),
20420
+ weight: number().positive().optional(),
20421
+ maxSessions: number().int().positive().optional(),
20422
+ steps: record(string(), DeviceStepConfigSchema).optional()
20423
+ }))
20424
+ }), object({ success: literal(true) }), {
20425
+ kind: "mutation",
20426
+ auth: "admin"
20427
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20290
20428
  success: literal(true),
20291
20429
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20292
20430
  effectiveModelId: string().nullable(),
@@ -20302,9 +20440,10 @@ method(object({
20302
20440
  }), object({ success: literal(true) }), {
20303
20441
  kind: "mutation",
20304
20442
  auth: "admin"
20305
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20443
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20306
20444
  deviceId: number(),
20307
20445
  agentNodeId: string(),
20446
+ deviceKey: string(),
20308
20447
  addonId: string(),
20309
20448
  patch: CameraStepOverridePatchSchema.nullable()
20310
20449
  }), object({ success: literal(true) }), {
@@ -20341,14 +20480,13 @@ method(object({
20341
20480
  });
20342
20481
  /**
20343
20482
  * server-management — per-NODE singleton capability for a node's ROOT
20344
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20345
- * agents).
20483
+ * package lifecycle (runtime-updatable node packages).
20346
20484
  *
20347
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20348
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20349
- * version describes the node. Updates install into
20350
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20351
- * starter (probation boot + auto-rollback to N-1).
20485
+ * Every node role runs the SAME root package (`@camstack/server`), which
20486
+ * carries the whole software stack in its npm dep tree, so ONE version
20487
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20488
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20489
+ * no auto-rollback).
20352
20490
  *
20353
20491
  * Providers:
20354
20492
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20456,7 +20594,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20456
20594
  /** Explicit target version; omitted = latest from the registry. */
20457
20595
  version: string().optional() }), ServerUpdateActionResultSchema, {
20458
20596
  kind: "mutation",
20459
- auth: "admin"
20597
+ auth: "admin",
20598
+ timeoutMs: 16 * 6e4
20460
20599
  }), method(_void(), ServerUpdateActionResultSchema, {
20461
20600
  kind: "mutation",
20462
20601
  auth: "admin"
@@ -21500,22 +21639,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21500
21639
  var RestartAddonResultSchema = unknown();
21501
21640
  var InstallPackageResultSchema = unknown();
21502
21641
  var ReloadPackagesResultSchema = unknown();
21503
- /**
21504
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21505
- * server restarts so the admin UI can react to the `restartingAt`
21506
- * timestamp (shows reconnect overlay). The transition from
21507
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21508
- * `system.restart-completed` event after the new process boots.
21509
- *
21510
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21511
- */
21512
- var UpdateFrameworkPackageResultSchema = object({
21513
- packageName: string(),
21514
- fromVersion: string(),
21515
- toVersion: string(),
21516
- /** Ms-epoch the server scheduled its self-restart. */
21517
- restartingAt: number()
21518
- });
21519
21642
  var BulkUpdateItemStatusSchema = _enum([
21520
21643
  "queued",
21521
21644
  "updating",
@@ -21643,13 +21766,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21643
21766
  }), object({ success: literal(true) }), {
21644
21767
  kind: "mutation",
21645
21768
  auth: "admin"
21646
- }), method(object({
21647
- packageName: string().min(1),
21648
- version: string().optional(),
21649
- deferRestart: boolean().optional()
21650
- }), UpdateFrameworkPackageResultSchema, {
21651
- kind: "mutation",
21652
- auth: "admin"
21653
21769
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21654
21770
  kind: "mutation",
21655
21771
  auth: "admin"
@@ -22532,10 +22648,10 @@ var TopologyCategorySchema = object({
22532
22648
  addons: array(TopologyCategoryAddonSchema).readonly()
22533
22649
  });
22534
22650
  /**
22535
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22536
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22537
- * version visibility for the Server management surface. Nullable: offline
22538
- * rows and pre-phase-2 nodes report none.
22651
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22652
+ * root package for every node role) as reported by its `registerNode`
22653
+ * manifest — version visibility for the Server management surface. Nullable:
22654
+ * offline rows and nodes that never reported one.
22539
22655
  */
22540
22656
  var TopologyRootPackageSchema = object({
22541
22657
  name: string(),
@@ -22923,17 +23039,28 @@ var PlatformScoreSchema = object({
22923
23039
  format: _enum([
22924
23040
  "onnx",
22925
23041
  "coreml",
22926
- "openvino"
23042
+ "openvino",
23043
+ "tflite"
22927
23044
  ]),
22928
23045
  score: number(),
22929
23046
  reason: string(),
22930
23047
  available: boolean()
22931
23048
  });
23049
+ var InferenceDeviceDescriptorSchema = object({
23050
+ key: string(),
23051
+ backend: string(),
23052
+ device: string(),
23053
+ format: ModelFormatSchema,
23054
+ runtime: literal("python"),
23055
+ score: number(),
23056
+ available: boolean()
23057
+ });
22932
23058
  var PlatformCapabilitiesSchema = object({
22933
23059
  hardware: HardwareInfoSchema,
22934
23060
  scores: array(PlatformScoreSchema).readonly(),
22935
23061
  bestScore: PlatformScoreSchema,
22936
- pythonPath: string().nullable()
23062
+ pythonPath: string().nullable(),
23063
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22937
23064
  });
22938
23065
  var ModelRequirementSchema = object({
22939
23066
  modelId: string(),
@@ -23812,12 +23939,6 @@ Object.freeze({
23812
23939
  addonId: null,
23813
23940
  access: "delete"
23814
23941
  },
23815
- "addons.updateFrameworkPackage": {
23816
- capName: "addons",
23817
- capScope: "system",
23818
- addonId: null,
23819
- access: "create"
23820
- },
23821
23942
  "addons.updatePackage": {
23822
23943
  capName: "addons",
23823
23944
  capScope: "system",
@@ -26590,12 +26711,6 @@ Object.freeze({
26590
26711
  addonId: null,
26591
26712
  access: "view"
26592
26713
  },
26593
- "pipelineExecutor.reprobeEngine": {
26594
- capName: "pipeline-executor",
26595
- capScope: "system",
26596
- addonId: null,
26597
- access: "create"
26598
- },
26599
26714
  "pipelineExecutor.runAudioTest": {
26600
26715
  capName: "pipeline-executor",
26601
26716
  capScope: "system",
@@ -26746,6 +26861,12 @@ Object.freeze({
26746
26861
  addonId: null,
26747
26862
  access: "view"
26748
26863
  },
26864
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26865
+ capName: "pipeline-orchestrator",
26866
+ capScope: "system",
26867
+ addonId: null,
26868
+ access: "view"
26869
+ },
26749
26870
  "pipelineOrchestrator.getPipelineAssignment": {
26750
26871
  capName: "pipeline-orchestrator",
26751
26872
  capScope: "system",
@@ -26758,6 +26879,12 @@ Object.freeze({
26758
26879
  addonId: null,
26759
26880
  access: "view"
26760
26881
  },
26882
+ "pipelineOrchestrator.getPipelineDevicePin": {
26883
+ capName: "pipeline-orchestrator",
26884
+ capScope: "system",
26885
+ addonId: null,
26886
+ access: "view"
26887
+ },
26761
26888
  "pipelineOrchestrator.listAgentSettings": {
26762
26889
  capName: "pipeline-orchestrator",
26763
26890
  capScope: "system",
@@ -26800,19 +26927,19 @@ Object.freeze({
26800
26927
  addonId: null,
26801
26928
  access: "create"
26802
26929
  },
26803
- "pipelineOrchestrator.setAgentAddonDefaults": {
26930
+ "pipelineOrchestrator.setAgentCapabilities": {
26804
26931
  capName: "pipeline-orchestrator",
26805
26932
  capScope: "system",
26806
26933
  addonId: null,
26807
26934
  access: "create"
26808
26935
  },
26809
- "pipelineOrchestrator.setAgentCapabilities": {
26936
+ "pipelineOrchestrator.setAgentDetectWeight": {
26810
26937
  capName: "pipeline-orchestrator",
26811
26938
  capScope: "system",
26812
26939
  addonId: null,
26813
26940
  access: "create"
26814
26941
  },
26815
- "pipelineOrchestrator.setAgentDetectWeight": {
26942
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26816
26943
  capName: "pipeline-orchestrator",
26817
26944
  capScope: "system",
26818
26945
  addonId: null,
@@ -26854,6 +26981,12 @@ Object.freeze({
26854
26981
  addonId: null,
26855
26982
  access: "create"
26856
26983
  },
26984
+ "pipelineOrchestrator.setPipelineDevicePin": {
26985
+ capName: "pipeline-orchestrator",
26986
+ capScope: "system",
26987
+ addonId: null,
26988
+ access: "create"
26989
+ },
26857
26990
  "pipelineOrchestrator.unassignAudio": {
26858
26991
  capName: "pipeline-orchestrator",
26859
26992
  capScope: "system",
@@ -28406,32 +28539,6 @@ Object.freeze({
28406
28539
  "network-access": "ingress",
28407
28540
  "smtp-provider": "email"
28408
28541
  });
28409
- var frameworkSwapPackageSchema = object({
28410
- name: string(),
28411
- stagedPath: string(),
28412
- backupPath: string(),
28413
- toVersion: string(),
28414
- fromVersion: string().nullable()
28415
- });
28416
- object({
28417
- jobId: string(),
28418
- taskId: string(),
28419
- packages: array(frameworkSwapPackageSchema),
28420
- requestedAtMs: number(),
28421
- schemaVersion: literal(1)
28422
- });
28423
- object({
28424
- jobId: string(),
28425
- taskId: string(),
28426
- backups: array(object({
28427
- name: string(),
28428
- backupPath: string(),
28429
- livePath: string()
28430
- })),
28431
- appliedAtMs: number(),
28432
- bootAttempts: number(),
28433
- schemaVersion: literal(1)
28434
- });
28435
28542
  //#endregion
28436
28543
  //#region src/config.ts
28437
28544
  /**
package/dist/addon.mjs CHANGED
@@ -7137,6 +7137,17 @@ var ModelCatalogEntrySchema = object({
7137
7137
  "imagenet",
7138
7138
  "none"
7139
7139
  ]).optional(),
7140
+ /**
7141
+ * The model already applies softmax IN-GRAPH — its raw output is a
7142
+ * probability distribution, not logits. When set, the `softmax`
7143
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7144
+ * probability vector collapses it toward uniform (top-1 score craters far
7145
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7146
+ * the output is raw logits and the postprocessor applies softmax (the normal
7147
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7148
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7149
+ */
7150
+ outputProbabilities: boolean().optional(),
7140
7151
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7141
7152
  /**
7142
7153
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12544,10 +12555,7 @@ var ConfigUISchemaNullableBridge = custom();
12544
12555
  var InferenceCapabilitiesBridge = custom();
12545
12556
  var ModelAvailabilityListBridge = custom();
12546
12557
  var PipelineRunResultBridge = custom();
12547
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12548
- kind: "mutation",
12549
- auth: "admin"
12550
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12558
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12551
12559
  modelId: string(),
12552
12560
  settings: record(string(), unknown()).readonly()
12553
12561
  }))), method(object({ steps: record(string(), object({
@@ -12607,13 +12615,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12607
12615
  * (inputClasses ≠ null) are skipped and served per-track via
12608
12616
  * pipelineRunner.runDetailSubtree (two-plane design).
12609
12617
  */
12610
- plane: _enum(["full", "frame"]).optional()
12618
+ plane: _enum(["full", "frame"]).optional(),
12619
+ /**
12620
+ * Inference-device selector (Phase 2 multi-device). Format
12621
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12622
+ * Omitted ⇒ the runner's default device (current single-engine
12623
+ * behaviour). Selects WHICH device pool of the node runs the call.
12624
+ */
12625
+ deviceKey: string().optional()
12611
12626
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12612
12627
  engine: PipelineEngineChoiceSchema.optional(),
12613
12628
  steps: array(PipelineStepInputSchema).min(1),
12614
12629
  frames: array(FrameInputSchema).min(1).max(255),
12615
12630
  deviceId: number().optional(),
12616
- sessionId: string().optional()
12631
+ sessionId: string().optional(),
12632
+ /**
12633
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12634
+ * the batch to the Python pool's bench preprocess cache
12635
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12636
+ * preprocessed ONCE and every later inference is a pure-inference cache
12637
+ * hit — the sustained-throughput run measures inference, not
12638
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12639
+ * full preprocess every call, correct). Fresh per sustained run;
12640
+ * released via `uncacheFrame`.
12641
+ */
12642
+ frameId: number().int().nonnegative().optional(),
12643
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12644
+ deviceKey: string().optional()
12617
12645
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12618
12646
  data: _instanceof(Uint8Array),
12619
12647
  width: number().int().positive(),
@@ -12645,8 +12673,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12645
12673
  * - `runtime` — main camera-serving engine (no idle TTL).
12646
12674
  * - `warm-override` — benchmark/test override held in the warm
12647
12675
  * cache; auto-disposed after the idle TTL.
12676
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12677
+ * multi-device, keyed by `deviceKey`) resolved
12678
+ * via `resolveDeviceFactory`. Runs alongside the
12679
+ * `runtime` engine on a DIFFERENT accelerator
12680
+ * (NPU / iGPU / Coral) — this is how the
12681
+ * Engines tab shows all pools running at once.
12648
12682
  */
12649
- kind: _enum(["runtime", "warm-override"]),
12683
+ kind: _enum([
12684
+ "runtime",
12685
+ "warm-override",
12686
+ "device-pool"
12687
+ ]),
12650
12688
  /** Native pid of the underlying Python pool (null when no pool). */
12651
12689
  poolPid: number().nullable(),
12652
12690
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12781,7 +12819,21 @@ var NativeCropResultSchema = object({
12781
12819
  /** Packed rgb (24-bit) pixels of the crop. */
12782
12820
  bytes: _instanceof(Uint8Array),
12783
12821
  width: number().int().positive(),
12784
- height: number().int().positive()
12822
+ height: number().int().positive(),
12823
+ /**
12824
+ * Which source served this crop, so a quality-sensitive consumer (the native
12825
+ * `keyFrame`) can reject a degraded fallback:
12826
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
12827
+ * quality path).
12828
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
12829
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
12830
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
12831
+ *
12832
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
12833
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
12834
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
12835
+ */
12836
+ tier: _enum(["native", "ram-fullframe"]).optional()
12785
12837
  });
12786
12838
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12787
12839
  * originating detection, in FRAME-space coordinates. Reuses
@@ -13021,7 +13073,14 @@ var RunnerCameraConfigSchema = object({
13021
13073
  * camera's detect node differs from its source-owner (P2d, gated by the
13022
13074
  * `remoteSourcingNodes` rollout setting).
13023
13075
  */
13024
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13076
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13077
+ /**
13078
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13079
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13080
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13081
+ * this only selects WHICH device pool of that node runs the session.
13082
+ */
13083
+ deviceKey: string().optional()
13025
13084
  });
13026
13085
  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;
13027
13086
  /**
@@ -13042,6 +13101,19 @@ var RunnerLocalLoadSchema = object({
13042
13101
  avgInferenceTimeMs: number(),
13043
13102
  /** Total queue depth across motion + detection queues. */
13044
13103
  queueDepthTotal: number(),
13104
+ /**
13105
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13106
+ * this runner currently has attached cameras on, so the orchestrator's second
13107
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13108
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13109
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13110
+ */
13111
+ devices: array(object({
13112
+ deviceKey: string(),
13113
+ backend: string(),
13114
+ attachedCameras: number(),
13115
+ queueDepthTotal: number()
13116
+ })).default([]),
13045
13117
  /** Hardware capability flags reported by this node. */
13046
13118
  hardware: object({
13047
13119
  hasGpu: boolean(),
@@ -16190,6 +16262,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16190
16262
  return toDeviceSummary(device, this.addonId);
16191
16263
  }
16192
16264
  };
16265
+ 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;
16266
+ new Set(Object.values(DeviceType));
16193
16267
  /**
16194
16268
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16195
16269
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17848,7 +17922,8 @@ var LinkedDeviceSchema = object({
17848
17922
  deviceId: number(),
17849
17923
  name: string(),
17850
17924
  location: string().nullable(),
17851
- features: array(string())
17925
+ features: array(string()),
17926
+ producesTrackedEvents: boolean().optional()
17852
17927
  });
17853
17928
  var SavedDeviceRowSchema = object({
17854
17929
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19478,6 +19553,7 @@ var TrackSchema = object({
19478
19553
  deviceId: number(),
19479
19554
  className: string(),
19480
19555
  label: string().optional(),
19556
+ producingDeviceName: string().optional(),
19481
19557
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19482
19558
  source: TrackSourceSchema.optional(),
19483
19559
  firstSeen: number(),
@@ -19615,7 +19691,8 @@ var MediaFileKindEnum = _enum([
19615
19691
  "fullFrameBoxed",
19616
19692
  "faceCrop",
19617
19693
  "plateCrop",
19618
- "keyFrame"
19694
+ "keyFrame",
19695
+ "keyFrameSmall"
19619
19696
  ]);
19620
19697
  var MediaFileSchema = object({
19621
19698
  key: string(),
@@ -19944,13 +20021,11 @@ var PipelineTemplateSchema = object({
19944
20021
  createdAt: string(),
19945
20022
  updatedAt: string()
19946
20023
  });
19947
- var AgentAddonConfigSchema = object({
19948
- enabled: boolean(),
20024
+ var DeviceStepConfigSchema = object({
19949
20025
  modelId: string().optional(),
19950
- settings: record(string(), unknown()).readonly()
20026
+ settings: record(string(), unknown()).optional()
19951
20027
  });
19952
20028
  var AgentPipelineSettingsSchema = object({
19953
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19954
20029
  maxCameras: number().int().nonnegative().nullable().default(null),
19955
20030
  /** Per-node detection weight (relative share for the quota balancer). */
19956
20031
  detectWeight: number().positive().optional(),
@@ -19974,7 +20049,22 @@ var AgentPipelineSettingsSchema = object({
19974
20049
  * it already uses to reach the hub). Set this only when the auto-detected
19975
20050
  * address is wrong (multi-homed host, NAT, custom interface).
19976
20051
  */
19977
- reachableHost: string().optional()
20052
+ reachableHost: string().optional(),
20053
+ /**
20054
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20055
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20056
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20057
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20058
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20059
+ * the default model/settings for every camera landing on that accelerator;
20060
+ * a stepId absent ⇒ the step uses that device's format default.
20061
+ */
20062
+ inferenceDevices: record(string(), object({
20063
+ enabled: boolean(),
20064
+ weight: number().positive().optional(),
20065
+ maxSessions: number().int().positive().optional(),
20066
+ steps: record(string(), DeviceStepConfigSchema).optional()
20067
+ })).optional()
19978
20068
  });
19979
20069
  var CameraPipelineForAgentSchema = object({
19980
20070
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19984,14 +20074,13 @@ var CameraPipelineForAgentSchema = object({
19984
20074
  }).nullable()
19985
20075
  });
19986
20076
  var CameraStepOverridePatchSchema = object({
19987
- enabled: boolean().optional(),
19988
20077
  modelId: string().optional(),
19989
20078
  settings: record(string(), unknown()).readonly().optional()
19990
20079
  });
19991
20080
  var CameraPipelineSettingsSchema = object({
19992
20081
  pinnedAgentNodeId: string().optional(),
19993
20082
  stepToggles: record(string(), boolean()).optional(),
19994
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20083
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19995
20084
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19996
20085
  });
19997
20086
  /**
@@ -20205,6 +20294,44 @@ var CameraStatusSchema = object({
20205
20294
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20206
20295
  fetchedAt: number()
20207
20296
  });
20297
+ var NodeInferenceDeviceSchema = object({
20298
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20299
+ key: string(),
20300
+ backend: string(),
20301
+ device: string(),
20302
+ format: _enum(MODEL_FORMATS),
20303
+ /** Whether the node's live probe reports the device as usable right now. */
20304
+ available: boolean(),
20305
+ /**
20306
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20307
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20308
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20309
+ * not a balanced target). An explicit stored value always wins; a stored-only
20310
+ * (unavailable) key keeps its stored value.
20311
+ */
20312
+ enabled: boolean(),
20313
+ /** Relative balancer weight for the enabled device (default 1). */
20314
+ weight: number(),
20315
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20316
+ maxSessions: number().nullable(),
20317
+ /** Object-detection model the executor defaults to for this deviceKey. */
20318
+ defaultModelId: string(),
20319
+ /**
20320
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20321
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20322
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20323
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20324
+ * available per format; this is the stored selection that becomes the
20325
+ * default for EVERY camera landing on this accelerator.
20326
+ */
20327
+ steps: record(string(), DeviceStepConfigSchema).optional()
20328
+ });
20329
+ var NodeInferenceDevicesSchema = object({
20330
+ nodeId: string(),
20331
+ /** False when the node's platform-probe was unreachable (no live device set). */
20332
+ reachable: boolean(),
20333
+ devices: array(NodeInferenceDeviceSchema).readonly()
20334
+ });
20208
20335
  method(object({
20209
20336
  deviceId: number(),
20210
20337
  agentNodeId: string()
@@ -20214,7 +20341,13 @@ method(object({
20214
20341
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20215
20342
  kind: "mutation",
20216
20343
  auth: "admin"
20217
- }), method(_void(), object({ migrated: number() }), {
20344
+ }), method(object({
20345
+ deviceId: number(),
20346
+ deviceKey: string()
20347
+ }), object({ success: literal(true) }), {
20348
+ kind: "mutation",
20349
+ auth: "admin"
20350
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20218
20351
  kind: "mutation",
20219
20352
  auth: "admin"
20220
20353
  }), 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({
@@ -20248,13 +20381,7 @@ method(object({
20248
20381
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20249
20382
  nodeId: string(),
20250
20383
  settings: AgentPipelineSettingsSchema
20251
- })).readonly()), method(object({
20252
- agentNodeId: string(),
20253
- defaults: record(string(), AgentAddonConfigSchema)
20254
- }), object({ success: literal(true) }), {
20255
- kind: "mutation",
20256
- auth: "admin"
20257
- }), method(object({ agentNodeId: string() }), object({
20384
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20258
20385
  success: boolean(),
20259
20386
  removed: boolean()
20260
20387
  }), {
@@ -20286,7 +20413,18 @@ method(object({
20286
20413
  }), object({ success: literal(true) }), {
20287
20414
  kind: "mutation",
20288
20415
  auth: "admin"
20289
- }), method(object({ agentNodeId: string() }), object({
20416
+ }), method(object({
20417
+ agentNodeId: string(),
20418
+ inferenceDevices: record(string(), object({
20419
+ enabled: boolean(),
20420
+ weight: number().positive().optional(),
20421
+ maxSessions: number().int().positive().optional(),
20422
+ steps: record(string(), DeviceStepConfigSchema).optional()
20423
+ }))
20424
+ }), object({ success: literal(true) }), {
20425
+ kind: "mutation",
20426
+ auth: "admin"
20427
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20290
20428
  success: literal(true),
20291
20429
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20292
20430
  effectiveModelId: string().nullable(),
@@ -20302,9 +20440,10 @@ method(object({
20302
20440
  }), object({ success: literal(true) }), {
20303
20441
  kind: "mutation",
20304
20442
  auth: "admin"
20305
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20443
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20306
20444
  deviceId: number(),
20307
20445
  agentNodeId: string(),
20446
+ deviceKey: string(),
20308
20447
  addonId: string(),
20309
20448
  patch: CameraStepOverridePatchSchema.nullable()
20310
20449
  }), object({ success: literal(true) }), {
@@ -20341,14 +20480,13 @@ method(object({
20341
20480
  });
20342
20481
  /**
20343
20482
  * server-management — per-NODE singleton capability for a node's ROOT
20344
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20345
- * agents).
20483
+ * package lifecycle (runtime-updatable node packages).
20346
20484
  *
20347
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20348
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20349
- * version describes the node. Updates install into
20350
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20351
- * starter (probation boot + auto-rollback to N-1).
20485
+ * Every node role runs the SAME root package (`@camstack/server`), which
20486
+ * carries the whole software stack in its npm dep tree, so ONE version
20487
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20488
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20489
+ * no auto-rollback).
20352
20490
  *
20353
20491
  * Providers:
20354
20492
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20456,7 +20594,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20456
20594
  /** Explicit target version; omitted = latest from the registry. */
20457
20595
  version: string().optional() }), ServerUpdateActionResultSchema, {
20458
20596
  kind: "mutation",
20459
- auth: "admin"
20597
+ auth: "admin",
20598
+ timeoutMs: 16 * 6e4
20460
20599
  }), method(_void(), ServerUpdateActionResultSchema, {
20461
20600
  kind: "mutation",
20462
20601
  auth: "admin"
@@ -21500,22 +21639,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21500
21639
  var RestartAddonResultSchema = unknown();
21501
21640
  var InstallPackageResultSchema = unknown();
21502
21641
  var ReloadPackagesResultSchema = unknown();
21503
- /**
21504
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21505
- * server restarts so the admin UI can react to the `restartingAt`
21506
- * timestamp (shows reconnect overlay). The transition from
21507
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21508
- * `system.restart-completed` event after the new process boots.
21509
- *
21510
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21511
- */
21512
- var UpdateFrameworkPackageResultSchema = object({
21513
- packageName: string(),
21514
- fromVersion: string(),
21515
- toVersion: string(),
21516
- /** Ms-epoch the server scheduled its self-restart. */
21517
- restartingAt: number()
21518
- });
21519
21642
  var BulkUpdateItemStatusSchema = _enum([
21520
21643
  "queued",
21521
21644
  "updating",
@@ -21643,13 +21766,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21643
21766
  }), object({ success: literal(true) }), {
21644
21767
  kind: "mutation",
21645
21768
  auth: "admin"
21646
- }), method(object({
21647
- packageName: string().min(1),
21648
- version: string().optional(),
21649
- deferRestart: boolean().optional()
21650
- }), UpdateFrameworkPackageResultSchema, {
21651
- kind: "mutation",
21652
- auth: "admin"
21653
21769
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21654
21770
  kind: "mutation",
21655
21771
  auth: "admin"
@@ -22532,10 +22648,10 @@ var TopologyCategorySchema = object({
22532
22648
  addons: array(TopologyCategoryAddonSchema).readonly()
22533
22649
  });
22534
22650
  /**
22535
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22536
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22537
- * version visibility for the Server management surface. Nullable: offline
22538
- * rows and pre-phase-2 nodes report none.
22651
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22652
+ * root package for every node role) as reported by its `registerNode`
22653
+ * manifest — version visibility for the Server management surface. Nullable:
22654
+ * offline rows and nodes that never reported one.
22539
22655
  */
22540
22656
  var TopologyRootPackageSchema = object({
22541
22657
  name: string(),
@@ -22923,17 +23039,28 @@ var PlatformScoreSchema = object({
22923
23039
  format: _enum([
22924
23040
  "onnx",
22925
23041
  "coreml",
22926
- "openvino"
23042
+ "openvino",
23043
+ "tflite"
22927
23044
  ]),
22928
23045
  score: number(),
22929
23046
  reason: string(),
22930
23047
  available: boolean()
22931
23048
  });
23049
+ var InferenceDeviceDescriptorSchema = object({
23050
+ key: string(),
23051
+ backend: string(),
23052
+ device: string(),
23053
+ format: ModelFormatSchema,
23054
+ runtime: literal("python"),
23055
+ score: number(),
23056
+ available: boolean()
23057
+ });
22932
23058
  var PlatformCapabilitiesSchema = object({
22933
23059
  hardware: HardwareInfoSchema,
22934
23060
  scores: array(PlatformScoreSchema).readonly(),
22935
23061
  bestScore: PlatformScoreSchema,
22936
- pythonPath: string().nullable()
23062
+ pythonPath: string().nullable(),
23063
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22937
23064
  });
22938
23065
  var ModelRequirementSchema = object({
22939
23066
  modelId: string(),
@@ -23812,12 +23939,6 @@ Object.freeze({
23812
23939
  addonId: null,
23813
23940
  access: "delete"
23814
23941
  },
23815
- "addons.updateFrameworkPackage": {
23816
- capName: "addons",
23817
- capScope: "system",
23818
- addonId: null,
23819
- access: "create"
23820
- },
23821
23942
  "addons.updatePackage": {
23822
23943
  capName: "addons",
23823
23944
  capScope: "system",
@@ -26590,12 +26711,6 @@ Object.freeze({
26590
26711
  addonId: null,
26591
26712
  access: "view"
26592
26713
  },
26593
- "pipelineExecutor.reprobeEngine": {
26594
- capName: "pipeline-executor",
26595
- capScope: "system",
26596
- addonId: null,
26597
- access: "create"
26598
- },
26599
26714
  "pipelineExecutor.runAudioTest": {
26600
26715
  capName: "pipeline-executor",
26601
26716
  capScope: "system",
@@ -26746,6 +26861,12 @@ Object.freeze({
26746
26861
  addonId: null,
26747
26862
  access: "view"
26748
26863
  },
26864
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26865
+ capName: "pipeline-orchestrator",
26866
+ capScope: "system",
26867
+ addonId: null,
26868
+ access: "view"
26869
+ },
26749
26870
  "pipelineOrchestrator.getPipelineAssignment": {
26750
26871
  capName: "pipeline-orchestrator",
26751
26872
  capScope: "system",
@@ -26758,6 +26879,12 @@ Object.freeze({
26758
26879
  addonId: null,
26759
26880
  access: "view"
26760
26881
  },
26882
+ "pipelineOrchestrator.getPipelineDevicePin": {
26883
+ capName: "pipeline-orchestrator",
26884
+ capScope: "system",
26885
+ addonId: null,
26886
+ access: "view"
26887
+ },
26761
26888
  "pipelineOrchestrator.listAgentSettings": {
26762
26889
  capName: "pipeline-orchestrator",
26763
26890
  capScope: "system",
@@ -26800,19 +26927,19 @@ Object.freeze({
26800
26927
  addonId: null,
26801
26928
  access: "create"
26802
26929
  },
26803
- "pipelineOrchestrator.setAgentAddonDefaults": {
26930
+ "pipelineOrchestrator.setAgentCapabilities": {
26804
26931
  capName: "pipeline-orchestrator",
26805
26932
  capScope: "system",
26806
26933
  addonId: null,
26807
26934
  access: "create"
26808
26935
  },
26809
- "pipelineOrchestrator.setAgentCapabilities": {
26936
+ "pipelineOrchestrator.setAgentDetectWeight": {
26810
26937
  capName: "pipeline-orchestrator",
26811
26938
  capScope: "system",
26812
26939
  addonId: null,
26813
26940
  access: "create"
26814
26941
  },
26815
- "pipelineOrchestrator.setAgentDetectWeight": {
26942
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26816
26943
  capName: "pipeline-orchestrator",
26817
26944
  capScope: "system",
26818
26945
  addonId: null,
@@ -26854,6 +26981,12 @@ Object.freeze({
26854
26981
  addonId: null,
26855
26982
  access: "create"
26856
26983
  },
26984
+ "pipelineOrchestrator.setPipelineDevicePin": {
26985
+ capName: "pipeline-orchestrator",
26986
+ capScope: "system",
26987
+ addonId: null,
26988
+ access: "create"
26989
+ },
26857
26990
  "pipelineOrchestrator.unassignAudio": {
26858
26991
  capName: "pipeline-orchestrator",
26859
26992
  capScope: "system",
@@ -28406,32 +28539,6 @@ Object.freeze({
28406
28539
  "network-access": "ingress",
28407
28540
  "smtp-provider": "email"
28408
28541
  });
28409
- var frameworkSwapPackageSchema = object({
28410
- name: string(),
28411
- stagedPath: string(),
28412
- backupPath: string(),
28413
- toVersion: string(),
28414
- fromVersion: string().nullable()
28415
- });
28416
- object({
28417
- jobId: string(),
28418
- taskId: string(),
28419
- packages: array(frameworkSwapPackageSchema),
28420
- requestedAtMs: number(),
28421
- schemaVersion: literal(1)
28422
- });
28423
- object({
28424
- jobId: string(),
28425
- taskId: string(),
28426
- backups: array(object({
28427
- name: string(),
28428
- backupPath: string(),
28429
- livePath: string()
28430
- })),
28431
- appliedAtMs: number(),
28432
- bootAttempts: number(),
28433
- schemaVersion: literal(1)
28434
- });
28435
28542
  //#endregion
28436
28543
  //#region src/config.ts
28437
28544
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-import-alexa",
3
- "version": "0.1.25",
3
+ "version": "0.2.1",
4
4
  "description": "Alexa device-import provider for CamStack — imports the smart-home devices in a user's Alexa account via the unofficial alexa-remote2 cookie/token client (the inverse of the Alexa exporter)",
5
5
  "keywords": [
6
6
  "camstack",