@camstack/addon-provider-wyze 0.1.24 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +560 -104
  2. package/dist/addon.mjs +560 -104
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -7094,6 +7094,17 @@ var ModelCatalogEntrySchema = object({
7094
7094
  "imagenet",
7095
7095
  "none"
7096
7096
  ]).optional(),
7097
+ /**
7098
+ * The model already applies softmax IN-GRAPH — its raw output is a
7099
+ * probability distribution, not logits. When set, the `softmax`
7100
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7101
+ * probability vector collapses it toward uniform (top-1 score craters far
7102
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7103
+ * the output is raw logits and the postprocessor applies softmax (the normal
7104
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7105
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7106
+ */
7107
+ outputProbabilities: boolean().optional(),
7097
7108
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7098
7109
  /**
7099
7110
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -7653,6 +7664,160 @@ var EncodeProfileSchema = object({
7653
7664
  */
7654
7665
  outputArgs: array(string()).optional()
7655
7666
  });
7667
+ var COCO_TO_MACRO = {
7668
+ mapping: {
7669
+ person: "person",
7670
+ bicycle: "vehicle",
7671
+ car: "vehicle",
7672
+ motorcycle: "vehicle",
7673
+ airplane: "vehicle",
7674
+ bus: "vehicle",
7675
+ train: "vehicle",
7676
+ truck: "vehicle",
7677
+ boat: "vehicle",
7678
+ bird: "animal",
7679
+ cat: "animal",
7680
+ dog: "animal",
7681
+ horse: "animal",
7682
+ sheep: "animal",
7683
+ cow: "animal",
7684
+ elephant: "animal",
7685
+ bear: "animal",
7686
+ zebra: "animal",
7687
+ giraffe: "animal",
7688
+ suitcase: "package",
7689
+ backpack: "package",
7690
+ handbag: "package"
7691
+ },
7692
+ preserveOriginal: false
7693
+ };
7694
+ var AUDIO_MACRO_LABELS = [
7695
+ {
7696
+ id: "speech",
7697
+ name: "Speech",
7698
+ icon: "🗣️"
7699
+ },
7700
+ {
7701
+ id: "scream",
7702
+ name: "Scream / Shout",
7703
+ icon: "😱"
7704
+ },
7705
+ {
7706
+ id: "crying",
7707
+ name: "Crying / Baby",
7708
+ icon: "😢"
7709
+ },
7710
+ {
7711
+ id: "laughter",
7712
+ name: "Laughter",
7713
+ icon: "😂"
7714
+ },
7715
+ {
7716
+ id: "music",
7717
+ name: "Music",
7718
+ icon: "🎵"
7719
+ },
7720
+ {
7721
+ id: "dog",
7722
+ name: "Dog",
7723
+ icon: "🐕"
7724
+ },
7725
+ {
7726
+ id: "cat",
7727
+ name: "Cat",
7728
+ icon: "🐈"
7729
+ },
7730
+ {
7731
+ id: "bird",
7732
+ name: "Bird",
7733
+ icon: "🐦"
7734
+ },
7735
+ {
7736
+ id: "animal",
7737
+ name: "Animal (other)",
7738
+ icon: "🐾"
7739
+ },
7740
+ {
7741
+ id: "alarm",
7742
+ name: "Alarm / Siren",
7743
+ icon: "🚨"
7744
+ },
7745
+ {
7746
+ id: "doorbell",
7747
+ name: "Doorbell / Knock",
7748
+ icon: "🔔"
7749
+ },
7750
+ {
7751
+ id: "glass_breaking",
7752
+ name: "Glass Breaking",
7753
+ icon: "💥"
7754
+ },
7755
+ {
7756
+ id: "gunshot",
7757
+ name: "Gunshot / Explosion",
7758
+ icon: "💣"
7759
+ },
7760
+ {
7761
+ id: "vehicle",
7762
+ name: "Vehicle",
7763
+ icon: "🚗"
7764
+ },
7765
+ {
7766
+ id: "siren",
7767
+ name: "Emergency Siren",
7768
+ icon: "🚑"
7769
+ },
7770
+ {
7771
+ id: "fire",
7772
+ name: "Fire / Smoke",
7773
+ icon: "🔥"
7774
+ },
7775
+ {
7776
+ id: "water",
7777
+ name: "Water",
7778
+ icon: "💧"
7779
+ },
7780
+ {
7781
+ id: "wind",
7782
+ name: "Wind / Weather",
7783
+ icon: "🌬️"
7784
+ },
7785
+ {
7786
+ id: "door",
7787
+ name: "Door",
7788
+ icon: "🚪"
7789
+ },
7790
+ {
7791
+ id: "footsteps",
7792
+ name: "Footsteps",
7793
+ icon: "👣"
7794
+ },
7795
+ {
7796
+ id: "crowd",
7797
+ name: "Crowd / Chatter",
7798
+ icon: "👥"
7799
+ },
7800
+ {
7801
+ id: "telephone",
7802
+ name: "Telephone",
7803
+ icon: "📞"
7804
+ },
7805
+ {
7806
+ id: "engine",
7807
+ name: "Engine / Motor",
7808
+ icon: "⚙️"
7809
+ },
7810
+ {
7811
+ id: "tools",
7812
+ name: "Tools / Construction",
7813
+ icon: "🔨"
7814
+ },
7815
+ {
7816
+ id: "silence",
7817
+ name: "Silence",
7818
+ icon: "🤫"
7819
+ }
7820
+ ];
7656
7821
  var YAMNET_TO_MACRO = {
7657
7822
  mapping: {
7658
7823
  Speech: "speech",
@@ -7919,6 +8084,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7919
8084
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7920
8085
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7921
8086
  /**
8087
+ * Unified event-kind taxonomy — THE single source of truth for
8088
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8089
+ * icon }`.
8090
+ *
8091
+ * This dictionary folds together what used to be scattered across four
8092
+ * copies:
8093
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8094
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8095
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8096
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8097
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8098
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8099
+ *
8100
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8101
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8102
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8103
+ * color or an icon: they read this dictionary (server descriptors carry the
8104
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8105
+ *
8106
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8107
+ */
8108
+ var TAXONOMY_COLORS = {
8109
+ motion: "#f59e0b",
8110
+ audio: "#06b6d4",
8111
+ person: "#22c55e",
8112
+ vehicle: "#3b82f6",
8113
+ animal: "#f97316",
8114
+ package: "#a855f7",
8115
+ sensor: "#8b5cf6",
8116
+ control: "#10b981",
8117
+ genericDetection: "#64748b"
8118
+ };
8119
+ var DETECTION_SUB_COLORS = {
8120
+ car: "#f59e0b",
8121
+ truck: "#d97706",
8122
+ bus: "#b45309",
8123
+ motorcycle: "#eab308",
8124
+ bicycle: "#ca8a04",
8125
+ airplane: "#60a5fa",
8126
+ boat: "#2563eb",
8127
+ train: "#1d4ed8",
8128
+ bird: "#14b8a6",
8129
+ dog: "#84cc16",
8130
+ cat: "#f97316",
8131
+ horse: "#a16207",
8132
+ sheep: "#a3a3a3",
8133
+ cow: "#78716c",
8134
+ elephant: "#6b7280",
8135
+ bear: "#7c2d12",
8136
+ zebra: "#404040",
8137
+ giraffe: "#d4a373"
8138
+ };
8139
+ function titleCase(id) {
8140
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8141
+ }
8142
+ var entries = /* @__PURE__ */ new Map();
8143
+ function macro(kind, category, color, iconId, label) {
8144
+ entries.set(kind, {
8145
+ kind,
8146
+ parentKind: null,
8147
+ level: "macro",
8148
+ category,
8149
+ color,
8150
+ iconId,
8151
+ labelKey: `eventKind.${kind}`,
8152
+ label
8153
+ });
8154
+ }
8155
+ function sub(kind, parentKind, category, color, iconId, label) {
8156
+ entries.set(kind, {
8157
+ kind,
8158
+ parentKind,
8159
+ level: "sub",
8160
+ category,
8161
+ color,
8162
+ iconId,
8163
+ labelKey: `eventKind.${kind}`,
8164
+ label
8165
+ });
8166
+ }
8167
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8168
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8169
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8170
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8171
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8172
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8173
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8174
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8175
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8176
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8177
+ if (entries.has(cocoClass)) continue;
8178
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8179
+ }
8180
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8181
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8182
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8183
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8184
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8185
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8186
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8187
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8188
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8189
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8190
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8191
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8192
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8193
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8194
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8195
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8196
+ sub("button", "control", "control", "#10b981", "button", "Button");
8197
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8198
+ for (const l of AUDIO_MACRO_LABELS) {
8199
+ const kind = `audio-${l.id}`;
8200
+ if (entries.has(kind)) continue;
8201
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8202
+ }
8203
+ /** The complete taxonomy dictionary, keyed by kind. */
8204
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8205
+ /**
7922
8206
  * Error types for the safe expression engine. Two distinct classes so callers
7923
8207
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7924
8208
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -12111,10 +12395,7 @@ var ConfigUISchemaNullableBridge = custom();
12111
12395
  var InferenceCapabilitiesBridge = custom();
12112
12396
  var ModelAvailabilityListBridge = custom();
12113
12397
  var PipelineRunResultBridge = custom();
12114
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12115
- kind: "mutation",
12116
- auth: "admin"
12117
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12398
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12118
12399
  modelId: string(),
12119
12400
  settings: record(string(), unknown()).readonly()
12120
12401
  }))), method(object({ steps: record(string(), object({
@@ -12174,13 +12455,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12174
12455
  * (inputClasses ≠ null) are skipped and served per-track via
12175
12456
  * pipelineRunner.runDetailSubtree (two-plane design).
12176
12457
  */
12177
- plane: _enum(["full", "frame"]).optional()
12458
+ plane: _enum(["full", "frame"]).optional(),
12459
+ /**
12460
+ * Inference-device selector (Phase 2 multi-device). Format
12461
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12462
+ * Omitted ⇒ the runner's default device (current single-engine
12463
+ * behaviour). Selects WHICH device pool of the node runs the call.
12464
+ */
12465
+ deviceKey: string().optional()
12178
12466
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12179
12467
  engine: PipelineEngineChoiceSchema.optional(),
12180
12468
  steps: array(PipelineStepInputSchema).min(1),
12181
12469
  frames: array(FrameInputSchema).min(1).max(255),
12182
12470
  deviceId: number().optional(),
12183
- sessionId: string().optional()
12471
+ sessionId: string().optional(),
12472
+ /**
12473
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12474
+ * the batch to the Python pool's bench preprocess cache
12475
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12476
+ * preprocessed ONCE and every later inference is a pure-inference cache
12477
+ * hit — the sustained-throughput run measures inference, not
12478
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12479
+ * full preprocess every call, correct). Fresh per sustained run;
12480
+ * released via `uncacheFrame`.
12481
+ */
12482
+ frameId: number().int().nonnegative().optional(),
12483
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12484
+ deviceKey: string().optional()
12184
12485
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12185
12486
  data: _instanceof(Uint8Array),
12186
12487
  width: number().int().positive(),
@@ -12212,8 +12513,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12212
12513
  * - `runtime` — main camera-serving engine (no idle TTL).
12213
12514
  * - `warm-override` — benchmark/test override held in the warm
12214
12515
  * cache; auto-disposed after the idle TTL.
12516
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12517
+ * multi-device, keyed by `deviceKey`) resolved
12518
+ * via `resolveDeviceFactory`. Runs alongside the
12519
+ * `runtime` engine on a DIFFERENT accelerator
12520
+ * (NPU / iGPU / Coral) — this is how the
12521
+ * Engines tab shows all pools running at once.
12215
12522
  */
12216
- kind: _enum(["runtime", "warm-override"]),
12523
+ kind: _enum([
12524
+ "runtime",
12525
+ "warm-override",
12526
+ "device-pool"
12527
+ ]),
12217
12528
  /** Native pid of the underlying Python pool (null when no pool). */
12218
12529
  poolPid: number().nullable(),
12219
12530
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12588,7 +12899,14 @@ var RunnerCameraConfigSchema = object({
12588
12899
  * camera's detect node differs from its source-owner (P2d, gated by the
12589
12900
  * `remoteSourcingNodes` rollout setting).
12590
12901
  */
12591
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
12902
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
12903
+ /**
12904
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
12905
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
12906
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
12907
+ * this only selects WHICH device pool of that node runs the session.
12908
+ */
12909
+ deviceKey: string().optional()
12592
12910
  });
12593
12911
  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;
12594
12912
  /**
@@ -12609,6 +12927,19 @@ var RunnerLocalLoadSchema = object({
12609
12927
  avgInferenceTimeMs: number(),
12610
12928
  /** Total queue depth across motion + detection queues. */
12611
12929
  queueDepthTotal: number(),
12930
+ /**
12931
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
12932
+ * this runner currently has attached cameras on, so the orchestrator's second
12933
+ * `balance()` pass (over a node's devices) weights on real per-pool session
12934
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
12935
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
12936
+ */
12937
+ devices: array(object({
12938
+ deviceKey: string(),
12939
+ backend: string(),
12940
+ attachedCameras: number(),
12941
+ queueDepthTotal: number()
12942
+ })).default([]),
12612
12943
  /** Hardware capability flags reported by this node. */
12613
12944
  hardware: object({
12614
12945
  hasGpu: boolean(),
@@ -15757,6 +16088,8 @@ var BaseDeviceProvider = class extends BaseAddon {
15757
16088
  return toDeviceSummary(device, this.addonId);
15758
16089
  }
15759
16090
  };
16091
+ 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;
16092
+ new Set(Object.values(DeviceType));
15760
16093
  /**
15761
16094
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
15762
16095
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -18868,17 +19201,30 @@ var EventKindCategorySchema = _enum([
18868
19201
  "audio",
18869
19202
  "detection",
18870
19203
  "sensor",
19204
+ "control",
18871
19205
  "custom",
18872
19206
  "package"
18873
19207
  ]);
19208
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19209
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
18874
19210
  var EventKindDescriptorSchema = object({
18875
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19211
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
18876
19212
  kind: string(),
19213
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19214
+ labelKey: string(),
19215
+ /** English fallback label (kept for clients that don't translate). */
18877
19216
  label: string(),
18878
19217
  /** Hex color for timeline/legend rendering. */
18879
19218
  color: string(),
19219
+ /** Dictionary id → lucide component on the UI side. */
19220
+ iconId: string(),
19221
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
18880
19222
  icon: EventKindIconSchema,
18881
19223
  category: EventKindCategorySchema,
19224
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19225
+ parentKind: string().nullable(),
19226
+ /** Derived from `parentKind`, explicit for the client tree. */
19227
+ level: EventKindLevelSchema,
18882
19228
  /** Which cap + device contributes this kind. For built-ins the camera
18883
19229
  * itself; for sensor kinds the LINKED source device. */
18884
19230
  source: object({
@@ -18951,11 +19297,21 @@ var TrackAudioLabelSchema = object({
18951
19297
  firstAt: number(),
18952
19298
  lastAt: number()
18953
19299
  });
19300
+ /**
19301
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19302
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19303
+ * linked sensor/control state change (no positions; carries a snapshot). The
19304
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19305
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19306
+ */
19307
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
18954
19308
  var TrackSchema = object({
18955
19309
  trackId: string(),
18956
19310
  deviceId: number(),
18957
19311
  className: string(),
18958
19312
  label: string().optional(),
19313
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19314
+ source: TrackSourceSchema.optional(),
18959
19315
  firstSeen: number(),
18960
19316
  lastSeen: number(),
18961
19317
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19332,6 +19688,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19332
19688
  eventId: string(),
19333
19689
  timestamp: number()
19334
19690
  });
19691
+ /**
19692
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19693
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19694
+ * caps into per-camera event-kind descriptors.
19695
+ *
19696
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19697
+ * is NOT duplicated here — every entry is derived from the single
19698
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19699
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19700
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19701
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19702
+ * eventful cap is missing.
19703
+ */
19704
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19705
+ var LEGACY_ICON = {
19706
+ motion: "motion",
19707
+ audio: "audio",
19708
+ person: "person",
19709
+ vehicle: "vehicle",
19710
+ animal: "animal",
19711
+ package: "package",
19712
+ door: "door",
19713
+ pir: "pir",
19714
+ smoke: "smoke",
19715
+ water: "water",
19716
+ button: "button",
19717
+ generic: "generic",
19718
+ gas: "smoke",
19719
+ vibration: "generic",
19720
+ tamper: "generic",
19721
+ presence: "person",
19722
+ lock: "generic",
19723
+ siren: "generic",
19724
+ switch: "generic",
19725
+ doorbell: "button"
19726
+ };
19727
+ function legacyIcon(iconId) {
19728
+ return LEGACY_ICON[iconId] ?? "generic";
19729
+ }
19730
+ /**
19731
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19732
+ * The anti-drift guard cross-checks this against the eventful caps declared
19733
+ * in `packages/types/src/capabilities/*.cap.ts`.
19734
+ */
19735
+ var CAP_TO_KIND = {
19736
+ contact: "contact",
19737
+ motion: "motion-sensor",
19738
+ smoke: "smoke",
19739
+ flood: "flood",
19740
+ gas: "gas",
19741
+ "carbon-monoxide": "carbon-monoxide",
19742
+ vibration: "vibration",
19743
+ tamper: "tamper",
19744
+ presence: "presence",
19745
+ "enum-sensor": "enum-sensor",
19746
+ "event-emitter": "device-event",
19747
+ "lock-control": "lock",
19748
+ switch: "switch",
19749
+ button: "button",
19750
+ doorbell: "doorbell"
19751
+ };
19752
+ function buildDescriptor(capName, kind) {
19753
+ const t = EVENT_TAXONOMY[kind];
19754
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19755
+ return {
19756
+ ...t,
19757
+ icon: legacyIcon(t.iconId)
19758
+ };
19759
+ }
19760
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19335
19761
  var CameraPipelineConfigSchema = object({
19336
19762
  engine: PipelineEngineChoiceSchema.optional(),
19337
19763
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19350,13 +19776,11 @@ var PipelineTemplateSchema = object({
19350
19776
  createdAt: string(),
19351
19777
  updatedAt: string()
19352
19778
  });
19353
- var AgentAddonConfigSchema = object({
19354
- enabled: boolean(),
19779
+ var DeviceStepConfigSchema = object({
19355
19780
  modelId: string().optional(),
19356
- settings: record(string(), unknown()).readonly()
19781
+ settings: record(string(), unknown()).optional()
19357
19782
  });
19358
19783
  var AgentPipelineSettingsSchema = object({
19359
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19360
19784
  maxCameras: number().int().nonnegative().nullable().default(null),
19361
19785
  /** Per-node detection weight (relative share for the quota balancer). */
19362
19786
  detectWeight: number().positive().optional(),
@@ -19380,7 +19804,22 @@ var AgentPipelineSettingsSchema = object({
19380
19804
  * it already uses to reach the hub). Set this only when the auto-detected
19381
19805
  * address is wrong (multi-homed host, NAT, custom interface).
19382
19806
  */
19383
- reachableHost: string().optional()
19807
+ reachableHost: string().optional(),
19808
+ /**
19809
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
19810
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
19811
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
19812
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
19813
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
19814
+ * the default model/settings for every camera landing on that accelerator;
19815
+ * a stepId absent ⇒ the step uses that device's format default.
19816
+ */
19817
+ inferenceDevices: record(string(), object({
19818
+ enabled: boolean(),
19819
+ weight: number().positive().optional(),
19820
+ maxSessions: number().int().positive().optional(),
19821
+ steps: record(string(), DeviceStepConfigSchema).optional()
19822
+ })).optional()
19384
19823
  });
19385
19824
  var CameraPipelineForAgentSchema = object({
19386
19825
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19390,14 +19829,13 @@ var CameraPipelineForAgentSchema = object({
19390
19829
  }).nullable()
19391
19830
  });
19392
19831
  var CameraStepOverridePatchSchema = object({
19393
- enabled: boolean().optional(),
19394
19832
  modelId: string().optional(),
19395
19833
  settings: record(string(), unknown()).readonly().optional()
19396
19834
  });
19397
19835
  var CameraPipelineSettingsSchema = object({
19398
19836
  pinnedAgentNodeId: string().optional(),
19399
19837
  stepToggles: record(string(), boolean()).optional(),
19400
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
19838
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19401
19839
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19402
19840
  });
19403
19841
  /**
@@ -19611,6 +20049,44 @@ var CameraStatusSchema = object({
19611
20049
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19612
20050
  fetchedAt: number()
19613
20051
  });
20052
+ var NodeInferenceDeviceSchema = object({
20053
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20054
+ key: string(),
20055
+ backend: string(),
20056
+ device: string(),
20057
+ format: _enum(MODEL_FORMATS),
20058
+ /** Whether the node's live probe reports the device as usable right now. */
20059
+ available: boolean(),
20060
+ /**
20061
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20062
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20063
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20064
+ * not a balanced target). An explicit stored value always wins; a stored-only
20065
+ * (unavailable) key keeps its stored value.
20066
+ */
20067
+ enabled: boolean(),
20068
+ /** Relative balancer weight for the enabled device (default 1). */
20069
+ weight: number(),
20070
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20071
+ maxSessions: number().nullable(),
20072
+ /** Object-detection model the executor defaults to for this deviceKey. */
20073
+ defaultModelId: string(),
20074
+ /**
20075
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20076
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20077
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20078
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20079
+ * available per format; this is the stored selection that becomes the
20080
+ * default for EVERY camera landing on this accelerator.
20081
+ */
20082
+ steps: record(string(), DeviceStepConfigSchema).optional()
20083
+ });
20084
+ var NodeInferenceDevicesSchema = object({
20085
+ nodeId: string(),
20086
+ /** False when the node's platform-probe was unreachable (no live device set). */
20087
+ reachable: boolean(),
20088
+ devices: array(NodeInferenceDeviceSchema).readonly()
20089
+ });
19614
20090
  method(object({
19615
20091
  deviceId: number(),
19616
20092
  agentNodeId: string()
@@ -19620,7 +20096,13 @@ method(object({
19620
20096
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19621
20097
  kind: "mutation",
19622
20098
  auth: "admin"
19623
- }), method(_void(), object({ migrated: number() }), {
20099
+ }), method(object({
20100
+ deviceId: number(),
20101
+ deviceKey: string()
20102
+ }), object({ success: literal(true) }), {
20103
+ kind: "mutation",
20104
+ auth: "admin"
20105
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19624
20106
  kind: "mutation",
19625
20107
  auth: "admin"
19626
20108
  }), 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({
@@ -19654,13 +20136,7 @@ method(object({
19654
20136
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
19655
20137
  nodeId: string(),
19656
20138
  settings: AgentPipelineSettingsSchema
19657
- })).readonly()), method(object({
19658
- agentNodeId: string(),
19659
- defaults: record(string(), AgentAddonConfigSchema)
19660
- }), object({ success: literal(true) }), {
19661
- kind: "mutation",
19662
- auth: "admin"
19663
- }), method(object({ agentNodeId: string() }), object({
20139
+ })).readonly()), method(object({ agentNodeId: string() }), object({
19664
20140
  success: boolean(),
19665
20141
  removed: boolean()
19666
20142
  }), {
@@ -19692,7 +20168,18 @@ method(object({
19692
20168
  }), object({ success: literal(true) }), {
19693
20169
  kind: "mutation",
19694
20170
  auth: "admin"
19695
- }), method(object({ agentNodeId: string() }), object({
20171
+ }), method(object({
20172
+ agentNodeId: string(),
20173
+ inferenceDevices: record(string(), object({
20174
+ enabled: boolean(),
20175
+ weight: number().positive().optional(),
20176
+ maxSessions: number().int().positive().optional(),
20177
+ steps: record(string(), DeviceStepConfigSchema).optional()
20178
+ }))
20179
+ }), object({ success: literal(true) }), {
20180
+ kind: "mutation",
20181
+ auth: "admin"
20182
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
19696
20183
  success: literal(true),
19697
20184
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
19698
20185
  effectiveModelId: string().nullable(),
@@ -19708,9 +20195,10 @@ method(object({
19708
20195
  }), object({ success: literal(true) }), {
19709
20196
  kind: "mutation",
19710
20197
  auth: "admin"
19711
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20198
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
19712
20199
  deviceId: number(),
19713
20200
  agentNodeId: string(),
20201
+ deviceKey: string(),
19714
20202
  addonId: string(),
19715
20203
  patch: CameraStepOverridePatchSchema.nullable()
19716
20204
  }), object({ success: literal(true) }), {
@@ -19747,14 +20235,13 @@ method(object({
19747
20235
  });
19748
20236
  /**
19749
20237
  * server-management — per-NODE singleton capability for a node's ROOT
19750
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
19751
- * agents).
20238
+ * package lifecycle (runtime-updatable node packages).
19752
20239
  *
19753
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
19754
- * on agents) carries the whole software stack in its npm dep tree, so ONE
19755
- * version describes the node. Updates install into
19756
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
19757
- * starter (probation boot + auto-rollback to N-1).
20240
+ * Every node role runs the SAME root package (`@camstack/server`), which
20241
+ * carries the whole software stack in its npm dep tree, so ONE version
20242
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20243
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20244
+ * no auto-rollback).
19758
20245
  *
19759
20246
  * Providers:
19760
20247
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -19862,7 +20349,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
19862
20349
  /** Explicit target version; omitted = latest from the registry. */
19863
20350
  version: string().optional() }), ServerUpdateActionResultSchema, {
19864
20351
  kind: "mutation",
19865
- auth: "admin"
20352
+ auth: "admin",
20353
+ timeoutMs: 16 * 6e4
19866
20354
  }), method(_void(), ServerUpdateActionResultSchema, {
19867
20355
  kind: "mutation",
19868
20356
  auth: "admin"
@@ -20957,22 +21445,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
20957
21445
  var RestartAddonResultSchema = unknown();
20958
21446
  var InstallPackageResultSchema = unknown();
20959
21447
  var ReloadPackagesResultSchema = unknown();
20960
- /**
20961
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
20962
- * server restarts so the admin UI can react to the `restartingAt`
20963
- * timestamp (shows reconnect overlay). The transition from
20964
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
20965
- * `system.restart-completed` event after the new process boots.
20966
- *
20967
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
20968
- */
20969
- var UpdateFrameworkPackageResultSchema = object({
20970
- packageName: string(),
20971
- fromVersion: string(),
20972
- toVersion: string(),
20973
- /** Ms-epoch the server scheduled its self-restart. */
20974
- restartingAt: number()
20975
- });
20976
21448
  var BulkUpdateItemStatusSchema = _enum([
20977
21449
  "queued",
20978
21450
  "updating",
@@ -21100,13 +21572,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21100
21572
  }), object({ success: literal(true) }), {
21101
21573
  kind: "mutation",
21102
21574
  auth: "admin"
21103
- }), method(object({
21104
- packageName: string().min(1),
21105
- version: string().optional(),
21106
- deferRestart: boolean().optional()
21107
- }), UpdateFrameworkPackageResultSchema, {
21108
- kind: "mutation",
21109
- auth: "admin"
21110
21575
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21111
21576
  kind: "mutation",
21112
21577
  auth: "admin"
@@ -22023,10 +22488,10 @@ var TopologyCategorySchema = object({
22023
22488
  addons: array(TopologyCategoryAddonSchema).readonly()
22024
22489
  });
22025
22490
  /**
22026
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22027
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22028
- * version visibility for the Server management surface. Nullable: offline
22029
- * rows and pre-phase-2 nodes report none.
22491
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22492
+ * root package for every node role) as reported by its `registerNode`
22493
+ * manifest — version visibility for the Server management surface. Nullable:
22494
+ * offline rows and nodes that never reported one.
22030
22495
  */
22031
22496
  var TopologyRootPackageSchema = object({
22032
22497
  name: string(),
@@ -22414,17 +22879,28 @@ var PlatformScoreSchema = object({
22414
22879
  format: _enum([
22415
22880
  "onnx",
22416
22881
  "coreml",
22417
- "openvino"
22882
+ "openvino",
22883
+ "tflite"
22418
22884
  ]),
22419
22885
  score: number(),
22420
22886
  reason: string(),
22421
22887
  available: boolean()
22422
22888
  });
22889
+ var InferenceDeviceDescriptorSchema = object({
22890
+ key: string(),
22891
+ backend: string(),
22892
+ device: string(),
22893
+ format: ModelFormatSchema,
22894
+ runtime: literal("python"),
22895
+ score: number(),
22896
+ available: boolean()
22897
+ });
22423
22898
  var PlatformCapabilitiesSchema = object({
22424
22899
  hardware: HardwareInfoSchema,
22425
22900
  scores: array(PlatformScoreSchema).readonly(),
22426
22901
  bestScore: PlatformScoreSchema,
22427
- pythonPath: string().nullable()
22902
+ pythonPath: string().nullable(),
22903
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22428
22904
  });
22429
22905
  var ModelRequirementSchema = object({
22430
22906
  modelId: string(),
@@ -23316,12 +23792,6 @@ Object.freeze({
23316
23792
  addonId: null,
23317
23793
  access: "delete"
23318
23794
  },
23319
- "addons.updateFrameworkPackage": {
23320
- capName: "addons",
23321
- capScope: "system",
23322
- addonId: null,
23323
- access: "create"
23324
- },
23325
23795
  "addons.updatePackage": {
23326
23796
  capName: "addons",
23327
23797
  capScope: "system",
@@ -26094,12 +26564,6 @@ Object.freeze({
26094
26564
  addonId: null,
26095
26565
  access: "view"
26096
26566
  },
26097
- "pipelineExecutor.reprobeEngine": {
26098
- capName: "pipeline-executor",
26099
- capScope: "system",
26100
- addonId: null,
26101
- access: "create"
26102
- },
26103
26567
  "pipelineExecutor.runAudioTest": {
26104
26568
  capName: "pipeline-executor",
26105
26569
  capScope: "system",
@@ -26250,6 +26714,12 @@ Object.freeze({
26250
26714
  addonId: null,
26251
26715
  access: "view"
26252
26716
  },
26717
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26718
+ capName: "pipeline-orchestrator",
26719
+ capScope: "system",
26720
+ addonId: null,
26721
+ access: "view"
26722
+ },
26253
26723
  "pipelineOrchestrator.getPipelineAssignment": {
26254
26724
  capName: "pipeline-orchestrator",
26255
26725
  capScope: "system",
@@ -26262,6 +26732,12 @@ Object.freeze({
26262
26732
  addonId: null,
26263
26733
  access: "view"
26264
26734
  },
26735
+ "pipelineOrchestrator.getPipelineDevicePin": {
26736
+ capName: "pipeline-orchestrator",
26737
+ capScope: "system",
26738
+ addonId: null,
26739
+ access: "view"
26740
+ },
26265
26741
  "pipelineOrchestrator.listAgentSettings": {
26266
26742
  capName: "pipeline-orchestrator",
26267
26743
  capScope: "system",
@@ -26304,19 +26780,19 @@ Object.freeze({
26304
26780
  addonId: null,
26305
26781
  access: "create"
26306
26782
  },
26307
- "pipelineOrchestrator.setAgentAddonDefaults": {
26783
+ "pipelineOrchestrator.setAgentCapabilities": {
26308
26784
  capName: "pipeline-orchestrator",
26309
26785
  capScope: "system",
26310
26786
  addonId: null,
26311
26787
  access: "create"
26312
26788
  },
26313
- "pipelineOrchestrator.setAgentCapabilities": {
26789
+ "pipelineOrchestrator.setAgentDetectWeight": {
26314
26790
  capName: "pipeline-orchestrator",
26315
26791
  capScope: "system",
26316
26792
  addonId: null,
26317
26793
  access: "create"
26318
26794
  },
26319
- "pipelineOrchestrator.setAgentDetectWeight": {
26795
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26320
26796
  capName: "pipeline-orchestrator",
26321
26797
  capScope: "system",
26322
26798
  addonId: null,
@@ -26358,6 +26834,12 @@ Object.freeze({
26358
26834
  addonId: null,
26359
26835
  access: "create"
26360
26836
  },
26837
+ "pipelineOrchestrator.setPipelineDevicePin": {
26838
+ capName: "pipeline-orchestrator",
26839
+ capScope: "system",
26840
+ addonId: null,
26841
+ access: "create"
26842
+ },
26361
26843
  "pipelineOrchestrator.unassignAudio": {
26362
26844
  capName: "pipeline-orchestrator",
26363
26845
  capScope: "system",
@@ -27910,32 +28392,6 @@ Object.freeze({
27910
28392
  "network-access": "ingress",
27911
28393
  "smtp-provider": "email"
27912
28394
  });
27913
- var frameworkSwapPackageSchema = object({
27914
- name: string(),
27915
- stagedPath: string(),
27916
- backupPath: string(),
27917
- toVersion: string(),
27918
- fromVersion: string().nullable()
27919
- });
27920
- object({
27921
- jobId: string(),
27922
- taskId: string(),
27923
- packages: array(frameworkSwapPackageSchema),
27924
- requestedAtMs: number(),
27925
- schemaVersion: literal(1)
27926
- });
27927
- object({
27928
- jobId: string(),
27929
- taskId: string(),
27930
- backups: array(object({
27931
- name: string(),
27932
- backupPath: string(),
27933
- livePath: string()
27934
- })),
27935
- appliedAtMs: number(),
27936
- bootAttempts: number(),
27937
- schemaVersion: literal(1)
27938
- });
27939
28395
  //#endregion
27940
28396
  //#region ../../node_modules/@apocaliss92/wyze-bridge-js/dist/index.js
27941
28397
  var __require = /* @__PURE__ */ ((x) => typeof __require$1 !== "undefined" ? __require$1 : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof __require$1 !== "undefined" ? __require$1 : a)[b] }) : x)(function(x) {