@camstack/addon-pipeline 1.1.27 → 1.1.29

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 (49) hide show
  1. package/dist/audio-analyzer/index.js +7 -6
  2. package/dist/audio-analyzer/index.mjs +7 -6
  3. package/dist/audio-codec-ffmpeg/index.js +1 -5
  4. package/dist/audio-codec-ffmpeg/index.mjs +1 -5
  5. package/dist/decoder-ffmpeg/index.js +138 -356
  6. package/dist/decoder-ffmpeg/index.mjs +118 -336
  7. package/dist/decoder-nodeav/index.js +17 -10
  8. package/dist/decoder-nodeav/index.mjs +17 -10
  9. package/dist/detection-pipeline/index.js +341 -379
  10. package/dist/detection-pipeline/index.mjs +341 -379
  11. package/dist/{dist-DAIlCdAx.js → dist-Cwc0TUQr.js} +378 -76
  12. package/dist/{dist-CgEP_0OL.mjs → dist-DjuGmyG9.mjs} +379 -65
  13. package/dist/ffmpeg-args-C5GPp8Cw.mjs +323 -0
  14. package/dist/ffmpeg-args-D6h1edXK.js +418 -0
  15. package/dist/frame-dropper-AjheBGMG.mjs +22 -0
  16. package/dist/frame-dropper-DKLM6pMz.js +27 -0
  17. package/dist/{frame-handle-plane-Dq20KtKL.mjs → frame-handle-plane-Bkxz-TTD.mjs} +13 -5
  18. package/dist/{frame-handle-plane-DtTRX_0n.js → frame-handle-plane-DQNCTrpC.js} +13 -5
  19. package/dist/{frame-ring-sink-BbzrqTWj.js → frame-ring-sink-8LLV-cvH.js} +128 -23
  20. package/dist/{frame-ring-sink-B_NvPTJZ.mjs → frame-ring-sink-ClEWjiRU.mjs} +117 -24
  21. package/dist/motion-wasm/index.js +55 -3
  22. package/dist/motion-wasm/index.mjs +55 -3
  23. package/dist/node-topology-platform-BkR_k6WT.mjs +15 -0
  24. package/dist/node-topology-platform-CFZ7F4xW.js +20 -0
  25. package/dist/pipeline-runner/index.js +50 -16
  26. package/dist/pipeline-runner/index.mjs +50 -16
  27. package/dist/recorder/index.js +36 -40
  28. package/dist/recorder/index.mjs +36 -40
  29. package/dist/stream-broker/_stub.js +2 -2
  30. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-5tQlh9h4.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D567z31g.mjs} +3 -3
  31. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DhY3MZ2C.mjs +26 -0
  32. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BJK0-svt.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DtoYGpTp.mjs} +1 -1
  33. package/dist/stream-broker/{hostInit-DyLqyJaS.mjs → hostInit-B_b3PIZB.mjs} +3 -3
  34. package/dist/stream-broker/index.js +198 -115
  35. package/dist/stream-broker/index.mjs +189 -106
  36. package/dist/stream-broker/remoteEntry.js +1 -1
  37. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-BDLNwJ_F.js → MaskShapeCanvas-DI4BY7W2-x-DZOuQL.js} +1 -1
  38. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CoLjNiUN.js → MotionZonesSettings-NcxxQN8r-DTeuDGCU.js} +1 -1
  39. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DJE3OU-q.js → PrivacyMaskSettings-APgPLF7p-CirtxO3e.js} +1 -1
  40. package/embed-dist/assets/index-C5UpuPr8.css +2 -0
  41. package/embed-dist/assets/{index-C-pL8ETk.js → index-CpUy8OIE.js} +10 -10
  42. package/embed-dist/index.html +2 -2
  43. package/package.json +1 -1
  44. package/python/inference_pool.py +93 -102
  45. package/python/test_inference_pool_device_selection.py +43 -38
  46. package/dist/frame-dropper-7RTo_YyG.js +0 -68
  47. package/dist/frame-dropper-CwkBTPGV.mjs +0 -51
  48. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CkOPfV8r.mjs +0 -26
  49. package/embed-dist/assets/index-DrJ0ee3f.css +0 -2
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-BO1nweKv.mjs
4630
+ //#region ../types/dist/sleep-BiDFW0E7.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5440,6 +5440,100 @@ function createDurableState(deps) {
5440
5440
  };
5441
5441
  }
5442
5442
  /**
5443
+ * Per-node scoping for the shared addon-settings blob.
5444
+ *
5445
+ * An addon's settings store is hub-central (the `addon-settings` cap is
5446
+ * hub-routed — the hub instance answers for every node), so fields whose
5447
+ * value is a NODE fact (decoder backend, engine pick, probed hardware
5448
+ * capabilities) persist under the node-scoped key `<key>@<nodeId>` inside
5449
+ * the single shared blob. The SCHEMA field key stays bare: the write path
5450
+ * ({@link scopePatch}) maps the bare field onto the target node's scoped
5451
+ * key; the read/hydrate path ({@link projectStore}) maps THIS node's value
5452
+ * back, so UI forms and `resolveConfig` only ever see the bare key.
5453
+ *
5454
+ * Fields opt in via the `perNode: true` schema marker on `ConfigFieldBase`
5455
+ * (`interfaces/config-ui.ts`); `BaseAddon` derives the key set from the
5456
+ * schema and routes reads/writes through these helpers.
5457
+ *
5458
+ * ## No bare-key fallback — deliberate
5459
+ *
5460
+ * {@link readNodeValue} reads the node-scoped key ONLY. A node with no
5461
+ * scoped key resolves to `undefined` so the field's schema `default` wins —
5462
+ * NEVER `store[base]` and never another node's value. A bare legacy key in
5463
+ * the store is invisible to every node, hub included, so one node's
5464
+ * selection can never leak onto another. (This generalizes the
5465
+ * `decoder-backend-keys.ts` semantics — the no-fallback variant — over an
5466
+ * arbitrary set of per-node field keys.)
5467
+ *
5468
+ * Pure functions only — no I/O, no imports beyond the language. This is a
5469
+ * LEAF module: import it via its deep path, never from the root barrel.
5470
+ */
5471
+ /**
5472
+ * Normalize a raw kernel node id to the bare node id used for scoping.
5473
+ * `localNodeId` can carry a `<node>/<addon>` suffix on forked child
5474
+ * processes; per-node settings are per-NODE, so strip the addon segment.
5475
+ * `undefined` / `null` / empty falls back to `'hub'`.
5476
+ */
5477
+ function normalizeNodeId(raw) {
5478
+ if (raw === void 0 || raw === null || raw === "") return "hub";
5479
+ const slashIdx = raw.indexOf("/");
5480
+ if (slashIdx < 0) return raw;
5481
+ const bare = raw.slice(0, slashIdx);
5482
+ return bare === "" ? "hub" : bare;
5483
+ }
5484
+ /** The node-scoped store key for a per-node field: `<base>@<nodeId>`. */
5485
+ function nodeScopedKey(base, nodeId) {
5486
+ return `${base}@${normalizeNodeId(nodeId)}`;
5487
+ }
5488
+ /**
5489
+ * Read a node's value for a per-node field from the raw shared store:
5490
+ * the node-scoped key when present, otherwise `undefined`.
5491
+ * Deliberately NO bare-key fallback (see module doc) — the caller lets the
5492
+ * schema `default` win on `undefined`.
5493
+ */
5494
+ function readNodeValue(store, base, nodeId) {
5495
+ return store[nodeScopedKey(base, nodeId)];
5496
+ }
5497
+ /**
5498
+ * Re-map a UI/settings patch so every bare perNode field persists under the
5499
+ * TARGET node's scoped key; all other keys pass through unchanged. Used on
5500
+ * the write path so a save for one node never clobbers another node's value
5501
+ * (and the bare key is never written). Returns a new object — the input
5502
+ * patch is not mutated.
5503
+ */
5504
+ function scopePatch(patch, perNodeKeys, nodeId) {
5505
+ const out = {};
5506
+ for (const [key, value] of Object.entries(patch)) out[perNodeKeys.has(key) ? nodeScopedKey(key, nodeId) : key] = value;
5507
+ return out;
5508
+ }
5509
+ /**
5510
+ * Project the raw shared store onto the bare schema keys for ONE node so a
5511
+ * UI schema (whose field keys are bare) hydrates from that node's own
5512
+ * values:
5513
+ *
5514
+ * - EVERY `@`-scoped key (any node's) is dropped from the pass-through.
5515
+ * - Every bare perNode key is dropped from the pass-through (a stray bare
5516
+ * legacy key must never hydrate any node — no bare fallback).
5517
+ * - THIS node's effective value ({@link readNodeValue}) is then laid onto
5518
+ * each bare perNode key; when the node has no scoped key the bare key is
5519
+ * left ABSENT so the field's schema `default` wins.
5520
+ *
5521
+ * Returns a new object — the input store is not mutated.
5522
+ */
5523
+ function projectStore(store, perNodeKeys, nodeId) {
5524
+ const out = {};
5525
+ for (const [key, value] of Object.entries(store)) {
5526
+ if (key.includes("@")) continue;
5527
+ if (perNodeKeys.has(key)) continue;
5528
+ out[key] = value;
5529
+ }
5530
+ for (const base of perNodeKeys) {
5531
+ const value = readNodeValue(store, base, nodeId);
5532
+ if (value !== void 0) out[base] = value;
5533
+ }
5534
+ return out;
5535
+ }
5536
+ /**
5443
5537
  * Base class for CamStack addons. Eliminates settings boilerplate:
5444
5538
  *
5445
5539
  * - Typed `config` property with automatic resolution from store + defaults
@@ -5607,23 +5701,63 @@ var BaseAddon = class {
5607
5701
  deviceSettingsSchema() {
5608
5702
  return null;
5609
5703
  }
5610
- async getGlobalSettings(overlay, cap, _nodeId) {
5704
+ async getGlobalSettings(overlay, cap, nodeId) {
5611
5705
  const schema = this.globalSettingsSchema(cap);
5612
5706
  if (!schema) return { sections: [] };
5613
- const raw = await this._ctx?.settings?.readAddonStore() ?? {};
5707
+ const projected = await this.resolveGlobalStore(nodeId, cap);
5614
5708
  return hydrateSchema(schema, overlay ? {
5615
- ...raw,
5709
+ ...projected,
5616
5710
  ...overlay
5617
- } : raw);
5711
+ } : projected);
5712
+ }
5713
+ /**
5714
+ * The raw addon store PROJECTED onto the target node's bare per-node keys:
5715
+ * every `perNode: true` field carries THAT node's scoped value on its bare
5716
+ * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
5717
+ * bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
5718
+ * A no-op passthrough when the schema declares no `perNode` field.
5719
+ *
5720
+ * This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
5721
+ * the store for custom option logic (option narrowing, value snapping) to
5722
+ * read it per-node — never `ctx.settings.readAddonStore()` directly.
5723
+ * `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
5724
+ */
5725
+ async resolveGlobalStore(nodeId, cap) {
5726
+ const raw = await this._ctx?.settings?.readAddonStore() ?? {};
5727
+ const keys = this.perNodeKeys(cap);
5728
+ const node = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
5729
+ return keys.size > 0 ? projectStore(raw, keys, node) : raw;
5618
5730
  }
5619
- async updateGlobalSettings(patch, _nodeId) {
5620
- await this._ctx?.settings?.writeAddonStore(patch);
5731
+ async updateGlobalSettings(patch, nodeId) {
5732
+ const keys = this.perNodeKeys();
5733
+ const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
5734
+ const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
5735
+ const barePatch = patch;
5736
+ const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
5737
+ await this._ctx?.settings?.writeAddonStore(scoped);
5738
+ if (target !== localNode) return;
5621
5739
  await this.resolveConfig();
5622
5740
  await this.onConfigChanged();
5623
5741
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
5624
5742
  this.maybeAutoRestart(patch, this.globalSettingsSchema());
5625
5743
  }
5626
5744
  /**
5745
+ * The set of field keys the global settings schema declares `perNode: true`
5746
+ * — derived once per `cap` argument and memoized (schemas are static
5747
+ * declarations). Empty set ⇒ every per-node code path is bypassed and the
5748
+ * settings API behaves exactly like the legacy node-agnostic one.
5749
+ */
5750
+ _perNodeKeysCache = /* @__PURE__ */ new Map();
5751
+ perNodeKeys(cap) {
5752
+ const cacheKey = cap ?? "";
5753
+ const cached = this._perNodeKeysCache.get(cacheKey);
5754
+ if (cached) return cached;
5755
+ const schema = this.globalSettingsSchema(cap);
5756
+ const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
5757
+ this._perNodeKeysCache.set(cacheKey, keys);
5758
+ return keys;
5759
+ }
5760
+ /**
5627
5761
  * If any field in `patch` is marked `requiresRestart` in `schema`,
5628
5762
  * schedule an addon restart for the next tick. Deferred via
5629
5763
  * `setImmediate` so the tRPC mutation that triggered the write has
@@ -5776,12 +5910,19 @@ var BaseAddon = class {
5776
5910
  * The merge is shallow: each key in `defaults` is checked against the store.
5777
5911
  * Only keys present in defaults are read — the store can contain extra keys
5778
5912
  * (e.g. from older versions) without polluting the typed config.
5913
+ *
5914
+ * Keys the global settings schema declares `perNode: true` resolve from
5915
+ * THIS node's scoped key (`<key>@<localNode>`) via `readNodeValue` — never
5916
+ * from the bare key — so a per-node field resolves to this node's own
5917
+ * selection at boot (absent scoped key ⇒ the constructor default wins).
5779
5918
  */
5780
5919
  async resolveConfig() {
5781
5920
  const stored = await this.readAddonStoreWithRetry();
5921
+ const perNode = this.perNodeKeys();
5922
+ const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
5782
5923
  const resolved = { ...this.defaults };
5783
5924
  for (const key of Object.keys(this.defaults)) {
5784
- const storedValue = stored[key];
5925
+ const storedValue = perNode.has(key) ? readNodeValue(stored, key, localNode) : stored[key];
5785
5926
  if (storedValue !== void 0 && storedValue !== null) {
5786
5927
  const defaultType = typeof this.defaults[key];
5787
5928
  if (typeof storedValue === defaultType) resolved[key] = storedValue;
@@ -5865,6 +6006,27 @@ var BaseAddon = class {
5865
6006
  }
5866
6007
  };
5867
6008
  /**
6009
+ * Collect the keys of every field marked `perNode: true`, recursing into
6010
+ * layout containers (`group` fields and `sub-tabs` tabs) the same way
6011
+ * `hydrateSchema` does. Valueless structural fields (separator/info/…)
6012
+ * don't declare `perNode` and are excluded by the `in` narrowing.
6013
+ */
6014
+ function collectPerNodeFieldKeys(fields) {
6015
+ const collected = [];
6016
+ for (const field of fields) {
6017
+ if (field.type === "group") {
6018
+ collected.push(...collectPerNodeFieldKeys(field.fields));
6019
+ continue;
6020
+ }
6021
+ if (field.type === "sub-tabs") {
6022
+ for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
6023
+ continue;
6024
+ }
6025
+ if ("perNode" in field && field.perNode === true) collected.push(field.key);
6026
+ }
6027
+ return collected;
6028
+ }
6029
+ /**
5868
6030
  * Normalize an `ICamstackAddon.initialize()` return value into the
5869
6031
  * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
5870
6032
  * envelopes pass through; void stays void.
@@ -6272,11 +6434,6 @@ var ProfileRtspEntrySchema = object({
6272
6434
  function parseJsonUnknown(text) {
6273
6435
  return JSON.parse(text);
6274
6436
  }
6275
- /** Narrow an unknown value to a plain `Record<string, unknown>` or return null. */
6276
- function asJsonObject(value) {
6277
- if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
6278
- return { ...value };
6279
- }
6280
6437
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
6281
6438
  DeviceType["Camera"] = "camera";
6282
6439
  DeviceType["Hub"] = "hub";
@@ -6361,6 +6518,13 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
6361
6518
  /** Single still-image entity (HA `image.*`). Read-only display of an
6362
6519
  * `entity_picture` signed URL the browser loads directly. `image` cap. */
6363
6520
  DeviceType["Image"] = "image";
6521
+ /** Smart pet feeder — cloud-connected food dispenser with a bowl food
6522
+ * level, battery, desiccant life, feeding state and manual-feed /
6523
+ * call-pet / maintenance actions. Installed with the `pet-feeder` cap;
6524
+ * dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
6525
+ * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
6526
+ * integrations sharing the same food/desiccant/hopper surface. */
6527
+ DeviceType["PetFeeder"] = "pet-feeder";
6364
6528
  return DeviceType;
6365
6529
  }({});
6366
6530
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10840,7 +11004,8 @@ var motionDetectionCapability = {
10840
11004
  methods: {
10841
11005
  analyze: method(object({
10842
11006
  deviceId: number(),
10843
- frame: FrameInputSchema
11007
+ frame: FrameInputSchema.optional(),
11008
+ frameHandle: FrameHandleSchema.optional()
10844
11009
  }), MotionAnalysisResultSchema, { kind: "mutation" }),
10845
11010
  removeCamera: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
10846
11011
  reset: method(_void(), _void(), { kind: "mutation" })
@@ -11152,11 +11317,20 @@ var pipelineExecutorCapability = {
11152
11317
  * legacy call shape used by existing benchmark code; once all
11153
11318
  * callers pass it explicitly we make it required.
11154
11319
  *
11155
- * Exactly one of `frame`, `imageBase64`, `referenceImage` must be
11156
- * provided:
11320
+ * Exactly one of `frame`, `frameHandle`, `imageBase64`,
11321
+ * `referenceImage` must be provided:
11157
11322
  * - `frame`: runtime dispatch path (runner → decoded broker frame).
11158
11323
  * Carries the raw buffer, dimensions, and format; the executor
11159
11324
  * uses it directly without base64 round-tripping.
11325
+ * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
11326
+ * decoded frame. Both runner and executor are hub-local processes
11327
+ * sharing `/dev/shm`, so the executor maps the named segment and
11328
+ * reads the pixels back zero-copy — eliminating the ~1.2MB
11329
+ * re-serialisation over UDS/MsgPack the `frame` path pays per call.
11330
+ * High-risk: the FrameRing is a latest-wins seqlock with no
11331
+ * refcount, so a recycled slot yields a null read; the executor
11332
+ * then degrades to an empty result and the runner ships pixels via
11333
+ * `frame` as the fallback (queue-depth gated on the runner side).
11160
11334
  * - `imageBase64`: one-shot test path (benchmark ImageTab).
11161
11335
  * - `referenceImage`: named file from the reference-image store.
11162
11336
  */
@@ -11164,6 +11338,12 @@ var pipelineExecutorCapability = {
11164
11338
  engine: PipelineEngineChoiceSchema.optional(),
11165
11339
  steps: array(PipelineStepInputSchema).min(1),
11166
11340
  frame: FrameInputSchema.optional(),
11341
+ /**
11342
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
11343
+ * the decoded pixels live in. One more member of the one-of
11344
+ * frame/frameHandle/image/imageBase64/referenceImage group.
11345
+ */
11346
+ frameHandle: FrameHandleSchema.optional(),
11167
11347
  imageBase64: string().optional(),
11168
11348
  /**
11169
11349
  * Binary JPEG bytes — preferred over `imageBase64` on internal
@@ -11972,6 +12152,113 @@ object({
11972
12152
  lastFetchedAt: number()
11973
12153
  });
11974
12154
  DeviceType.Sensor;
12155
+ /**
12156
+ * Feeder connectivity / power status — mirrors the HA petkit device-status
12157
+ * enum: `normal` (online, mains), `offline` (not reaching PetKit cloud),
12158
+ * `on_batteries` (running on battery backup). `null` until first reported.
12159
+ */
12160
+ var PetFeederDeviceStatusSchema = _enum([
12161
+ "normal",
12162
+ "offline",
12163
+ "on_batteries"
12164
+ ]);
12165
+ var gramsPortion = number().int().min(4).max(200);
12166
+ object({
12167
+ /** Food currently in the bowl (grams). Null when the device has not
12168
+ * reported a reading yet. On dual-hopper models this is the combined
12169
+ * bowl reading; per-hopper levels live in `food1`/`food2`. */
12170
+ foodLevel: number().nullable(),
12171
+ /** Hopper-1 food level (grams) on dual-hopper feeders; null on
12172
+ * single-hopper models. */
12173
+ food1: number().nullable(),
12174
+ /** Hopper-2 food level (grams) on dual-hopper feeders; null on
12175
+ * single-hopper models. */
12176
+ food2: number().nullable(),
12177
+ /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
12178
+ * (`device_class: problem`, on = low). True when the bowl is empty /
12179
+ * below the feeder's low threshold. */
12180
+ lowFood: boolean(),
12181
+ /** Battery charge 0..100 (%). Null on mains-powered models or when the
12182
+ * device has no battery reading. */
12183
+ batteryPower: number().min(0).max(100).nullable(),
12184
+ /** Days of desiccant life remaining. Null when the model has no
12185
+ * desiccant sensor. */
12186
+ desiccantLeftDays: number().nullable(),
12187
+ /** True while a feed is in progress. */
12188
+ feeding: boolean(),
12189
+ /** Decoded connectivity / power status (HA petkit device-status enum).
12190
+ * Null until the device has reported a status. */
12191
+ status: PetFeederDeviceStatusSchema.nullable(),
12192
+ /** Decoded human-readable fault message. Null (or the device's `no_error`
12193
+ * sentinel) means healthy; a non-null string is an active fault. Pairs
12194
+ * with `errorCode` for consumers that want the raw integer. */
12195
+ error: string().nullable(),
12196
+ /** Raw device error code (0 / null = no error). */
12197
+ errorCode: number().nullable(),
12198
+ /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
12199
+ isDualHopper: boolean(),
12200
+ /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
12201
+ childLock: boolean(),
12202
+ /** Front indicator-light setting. */
12203
+ indicatorLight: boolean(),
12204
+ /** Play a chime when dispensing. */
12205
+ feedSound: boolean(),
12206
+ /** Speaker / prompt volume level (device-scaled integer). */
12207
+ volume: number(),
12208
+ /** Ms epoch when the slice was last refreshed from the cloud. */
12209
+ lastFetchedAt: number()
12210
+ });
12211
+ DeviceType.PetFeeder, method(object({
12212
+ deviceId: number().int().nonnegative(),
12213
+ grams: gramsPortion.optional(),
12214
+ hopper1: gramsPortion.optional(),
12215
+ hopper2: gramsPortion.optional()
12216
+ }), _void(), {
12217
+ kind: "mutation",
12218
+ auth: "admin"
12219
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
12220
+ kind: "mutation",
12221
+ auth: "admin"
12222
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
12223
+ kind: "mutation",
12224
+ auth: "admin"
12225
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
12226
+ kind: "mutation",
12227
+ auth: "admin"
12228
+ }), method(object({ deviceId: number().int().nonnegative() }), _void(), {
12229
+ kind: "mutation",
12230
+ auth: "admin"
12231
+ }), method(object({
12232
+ deviceId: number().int().nonnegative(),
12233
+ soundId: number().int().nonnegative()
12234
+ }), _void(), {
12235
+ kind: "mutation",
12236
+ auth: "admin"
12237
+ }), method(object({
12238
+ deviceId: number().int().nonnegative(),
12239
+ on: boolean()
12240
+ }), _void(), {
12241
+ kind: "mutation",
12242
+ auth: "admin"
12243
+ }), method(object({
12244
+ deviceId: number().int().nonnegative(),
12245
+ on: boolean()
12246
+ }), _void(), {
12247
+ kind: "mutation",
12248
+ auth: "admin"
12249
+ }), method(object({
12250
+ deviceId: number().int().nonnegative(),
12251
+ on: boolean()
12252
+ }), _void(), {
12253
+ kind: "mutation",
12254
+ auth: "admin"
12255
+ }), method(object({
12256
+ deviceId: number().int().nonnegative(),
12257
+ level: number().int().nonnegative()
12258
+ }), _void(), {
12259
+ kind: "mutation",
12260
+ auth: "admin"
12261
+ });
11975
12262
  object({
11976
12263
  /** Instantaneous power draw in watts. */
11977
12264
  watts: number().optional(),
@@ -14087,11 +14374,13 @@ var decoderCapability = {
14087
14374
  }), _void()),
14088
14375
  pullFrames: method(object({
14089
14376
  sessionId: string(),
14090
- maxCount: number().default(1)
14377
+ maxCount: number().default(1),
14378
+ waitMs: number().optional()
14091
14379
  }), array(DecodedFrameSchema)),
14092
14380
  pullHandles: method(object({
14093
14381
  sessionId: string(),
14094
- maxCount: number().default(1)
14382
+ maxCount: number().default(1),
14383
+ waitMs: number().optional()
14095
14384
  }), array(FrameHandleSchema)),
14096
14385
  getFrame: method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()),
14097
14386
  getShmStats: method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()),
@@ -16114,7 +16403,10 @@ var AgentLoadSummarySchema = object({
16114
16403
  online: boolean(),
16115
16404
  load: RunnerLocalLoadSchema,
16116
16405
  /** Computed score used by the L2 capacity balancer (lower = less loaded). */
16117
- score: number()
16406
+ score: number(),
16407
+ /** This node's decode hwaccel backend (per-node `probedBestHwaccel`), or null
16408
+ * when not yet probed — for the cluster Pipeline table UI (P0.2). */
16409
+ decodeHwaccel: string().nullable()
16118
16410
  });
16119
16411
  /**
16120
16412
  * Aggregate metrics across the whole detection cluster. Replaces the legacy
@@ -18727,7 +19019,10 @@ var HwAccelBackendInputSchema = _enum([
18727
19019
  "webgpu",
18728
19020
  "none"
18729
19021
  ]).nullable().optional();
18730
- var HwAccelResolutionSchema = object({ preferred: array(string()).readonly() });
19022
+ var HwAccelResolutionSchema = object({
19023
+ preferred: array(string()).readonly(),
19024
+ rationale: string()
19025
+ });
18731
19026
  var HardwareEncoderIdSchema = _enum([
18732
19027
  "h264_videotoolbox",
18733
19028
  "hevc_videotoolbox",
@@ -18742,7 +19037,7 @@ var HardwareEncoderIdSchema = _enum([
18742
19037
  "libx264",
18743
19038
  "libx265"
18744
19039
  ]);
18745
- var HardwareEncodersSchema = object({
19040
+ object({
18746
19041
  encoders: array(object({
18747
19042
  encoder: HardwareEncoderIdSchema,
18748
19043
  codec: _enum(["H264", "H265"]),
@@ -18761,15 +19056,7 @@ var HardwareEncodersSchema = object({
18761
19056
  defaultH265: HardwareEncoderIdSchema,
18762
19057
  probedAt: number()
18763
19058
  });
18764
- /**
18765
- * Decode-side companion to {@link HardwareEncodersSchema}: the `-hwaccel`
18766
- * methods the configured ffmpeg binary actually supports (parsed from
18767
- * `ffmpeg -hwaccels`). Used to gate decode-hwaccel attempts so the transcode
18768
- * egress never spends a spawn on a backend this build cannot offer. Per-stream
18769
- * decode fragility (e.g. VideoToolbox HEVC) is still handled by the egress's
18770
- * software fallback — this only filters out wholly-unsupported backends.
18771
- */
18772
- var HardwareDecodeAccelsSchema = object({
19059
+ object({
18773
19060
  methods: array(string()).readonly(),
18774
19061
  probedAt: number()
18775
19062
  });
@@ -18832,16 +19119,7 @@ var ResolvedInferenceConfigSchema = object({
18832
19119
  format: ModelFormatSchema,
18833
19120
  reason: string()
18834
19121
  });
18835
- method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema), method(object({ requirements: array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema), method(object({
18836
- prefer: HwAccelBackendInputSchema,
18837
- nodeId: string().optional()
18838
- }), HwAccelResolutionSchema), method(_void(), HardwareEncodersSchema), method(_void(), HardwareEncodersSchema, {
18839
- kind: "mutation",
18840
- auth: "admin"
18841
- }), method(_void(), HardwareDecodeAccelsSchema), method(_void(), HardwareDecodeAccelsSchema, {
18842
- kind: "mutation",
18843
- auth: "admin"
18844
- });
19122
+ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema), method(object({ requirements: array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema), method(object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema);
18845
19123
  var PtzPresetSchema = object({
18846
19124
  id: string(),
18847
19125
  name: string()
@@ -21833,6 +22111,66 @@ Object.freeze({
21833
22111
  addonId: null,
21834
22112
  access: "create"
21835
22113
  },
22114
+ "petFeeder.callPet": {
22115
+ capName: "pet-feeder",
22116
+ capScope: "device",
22117
+ addonId: null,
22118
+ access: "create"
22119
+ },
22120
+ "petFeeder.cancelFeed": {
22121
+ capName: "pet-feeder",
22122
+ capScope: "device",
22123
+ addonId: null,
22124
+ access: "create"
22125
+ },
22126
+ "petFeeder.feed": {
22127
+ capName: "pet-feeder",
22128
+ capScope: "device",
22129
+ addonId: null,
22130
+ access: "create"
22131
+ },
22132
+ "petFeeder.markFoodReplenished": {
22133
+ capName: "pet-feeder",
22134
+ capScope: "device",
22135
+ addonId: null,
22136
+ access: "create"
22137
+ },
22138
+ "petFeeder.playSound": {
22139
+ capName: "pet-feeder",
22140
+ capScope: "device",
22141
+ addonId: null,
22142
+ access: "create"
22143
+ },
22144
+ "petFeeder.resetDesiccant": {
22145
+ capName: "pet-feeder",
22146
+ capScope: "device",
22147
+ addonId: null,
22148
+ access: "delete"
22149
+ },
22150
+ "petFeeder.setChildLock": {
22151
+ capName: "pet-feeder",
22152
+ capScope: "device",
22153
+ addonId: null,
22154
+ access: "create"
22155
+ },
22156
+ "petFeeder.setFeedSound": {
22157
+ capName: "pet-feeder",
22158
+ capScope: "device",
22159
+ addonId: null,
22160
+ access: "create"
22161
+ },
22162
+ "petFeeder.setIndicatorLight": {
22163
+ capName: "pet-feeder",
22164
+ capScope: "device",
22165
+ addonId: null,
22166
+ access: "create"
22167
+ },
22168
+ "petFeeder.setVolume": {
22169
+ capName: "pet-feeder",
22170
+ capScope: "device",
22171
+ addonId: null,
22172
+ access: "create"
22173
+ },
21836
22174
  "pipelineAnalytics.clearTracks": {
21837
22175
  capName: "pipeline-analytics",
21838
22176
  capScope: "device",
@@ -22439,30 +22777,6 @@ Object.freeze({
22439
22777
  addonId: null,
22440
22778
  access: "view"
22441
22779
  },
22442
- "platformProbe.getHardwareDecodeAccels": {
22443
- capName: "platform-probe",
22444
- capScope: "system",
22445
- addonId: null,
22446
- access: "view"
22447
- },
22448
- "platformProbe.getHardwareEncoders": {
22449
- capName: "platform-probe",
22450
- capScope: "system",
22451
- addonId: null,
22452
- access: "view"
22453
- },
22454
- "platformProbe.refreshHardwareDecodeAccels": {
22455
- capName: "platform-probe",
22456
- capScope: "system",
22457
- addonId: null,
22458
- access: "create"
22459
- },
22460
- "platformProbe.refreshHardwareEncoders": {
22461
- capName: "platform-probe",
22462
- capScope: "system",
22463
- addonId: null,
22464
- access: "create"
22465
- },
22466
22780
  "platformProbe.resolveHwAccel": {
22467
22781
  capName: "platform-probe",
22468
22782
  capScope: "system",
@@ -24113,12 +24427,6 @@ Object.defineProperty(exports, "array", {
24113
24427
  return array;
24114
24428
  }
24115
24429
  });
24116
- Object.defineProperty(exports, "asJsonObject", {
24117
- enumerable: true,
24118
- get: function() {
24119
- return asJsonObject;
24120
- }
24121
- });
24122
24430
  Object.defineProperty(exports, "audioAnalysisCapability", {
24123
24431
  enumerable: true,
24124
24432
  get: function() {
@@ -24149,12 +24457,6 @@ Object.defineProperty(exports, "cameraStreamsCapability", {
24149
24457
  return cameraStreamsCapability;
24150
24458
  }
24151
24459
  });
24152
- Object.defineProperty(exports, "createDurableState", {
24153
- enumerable: true,
24154
- get: function() {
24155
- return createDurableState;
24156
- }
24157
- });
24158
24460
  Object.defineProperty(exports, "createEvent", {
24159
24461
  enumerable: true,
24160
24462
  get: function() {