@camstack/types 1.0.4 → 1.0.6

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 (59) hide show
  1. package/dist/addon.d.ts +34 -0
  2. package/dist/addon.js +22 -0
  3. package/dist/addon.mjs +3 -0
  4. package/dist/capabilities/addons.cap.d.ts +10 -10
  5. package/dist/capabilities/advanced-notifier.cap.d.ts +4 -4
  6. package/dist/capabilities/alerts.cap.d.ts +10 -10
  7. package/dist/capabilities/audio-codec.cap.d.ts +2 -2
  8. package/dist/capabilities/camera-streams.cap.d.ts +15 -15
  9. package/dist/capabilities/consumables.cap.d.ts +4 -4
  10. package/dist/capabilities/cover.cap.d.ts +4 -4
  11. package/dist/capabilities/decoder.cap.d.ts +1 -1
  12. package/dist/capabilities/index.d.ts +2 -1
  13. package/dist/capabilities/local-network.cap.d.ts +6 -6
  14. package/dist/capabilities/log-destination.cap.d.ts +5 -5
  15. package/dist/capabilities/media-player.cap.d.ts +4 -4
  16. package/dist/capabilities/mesh-network.cap.d.ts +3 -3
  17. package/dist/capabilities/metrics-provider.cap.d.ts +33 -3
  18. package/dist/capabilities/network-access.cap.d.ts +7 -7
  19. package/dist/capabilities/oauth-integration.cap.d.ts +2 -2
  20. package/dist/capabilities/pipeline-executor.cap.d.ts +48 -1
  21. package/dist/capabilities/pipeline-orchestrator.cap.d.ts +524 -4
  22. package/dist/capabilities/platform-probe.cap.d.ts +1 -1
  23. package/dist/capabilities/restreamer.cap.d.ts +2 -2
  24. package/dist/capabilities/schemas/streaming-shared.d.ts +9 -9
  25. package/dist/capabilities/sso-bridge.cap.d.ts +3 -3
  26. package/dist/capabilities/storage.cap.d.ts +1 -1
  27. package/dist/capabilities/stream-broker.cap.d.ts +28 -28
  28. package/dist/capabilities/stream-params.cap.d.ts +14 -14
  29. package/dist/capabilities/user-management.cap.d.ts +20 -20
  30. package/dist/capabilities/vacuum-control.cap.d.ts +13 -13
  31. package/dist/capabilities/valve.cap.d.ts +4 -4
  32. package/dist/capabilities/webrtc-session.cap.d.ts +12 -12
  33. package/dist/deps/binary-downloader.d.ts +1 -1
  34. package/dist/deps/ffmpeg-downloader.d.ts +1 -1
  35. package/dist/deps/python-downloader.d.ts +1 -1
  36. package/dist/device/base-device-provider.d.ts +4 -1
  37. package/dist/encode-profile.d.ts +2 -2
  38. package/dist/enums/event-category.d.ts +12 -0
  39. package/dist/err-msg-COpsHMw2.js +18 -0
  40. package/dist/err-msg-IQTHeDzc.mjs +13 -0
  41. package/dist/generated/addon-api.d.ts +438 -12
  42. package/dist/generated/device-proxy.d.ts +1 -1
  43. package/dist/generated/method-access-map.d.ts +1 -1
  44. package/dist/generated/system-proxy.d.ts +3 -3
  45. package/dist/health/wiring-health.d.ts +16 -16
  46. package/dist/index.js +1435 -4646
  47. package/dist/index.mjs +485 -3711
  48. package/dist/interfaces/event-bus.d.ts +15 -0
  49. package/dist/interfaces/metrics-provider.d.ts +3 -1
  50. package/dist/interfaces/pipeline-executor-capability.d.ts +8 -0
  51. package/dist/lifecycle/job.d.ts +6 -6
  52. package/dist/node.js +3 -3
  53. package/dist/node.mjs +1 -1
  54. package/dist/schemas/auth-records.d.ts +4 -4
  55. package/dist/sleep-D7JeS58T.mjs +3507 -0
  56. package/dist/sleep-DnS0eJh_.js +3920 -0
  57. package/dist/storage/filesystem-storage-provider.d.ts +2 -1
  58. package/dist/types/agent-pipeline-settings.d.ts +7 -0
  59. package/package.json +6 -1
package/dist/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { $ as SubscribeFramesInputSchema, A as ReadinessTimeoutError, B as CameraStreamSchema, C as asNumber, D as parseJsonUnknown, E as parseJsonObject, F as BrokerStatusSchema, G as FrameHandleSchema, H as DecodedFrameSchema, I as CAM_PROFILE_ORDER, J as ProfileSlotStatusSchema, K as ProfileRtspEntrySchema, L as CamProfileSchema, M as readinessKey, N as scopeKey, O as DATAPLANE_SECRET_HEADER, P as BrokerStatsSchema, Q as SubscribeAudioChunksResultSchema, R as CamStreamKindSchema, S as asJsonObject, T as parseJsonArray, U as EncodedPacketSchema, V as DecodedAudioChunkSchema, W as FrameHandleFormatSchema, X as StreamSourceSchema, Y as StreamSourceEntrySchema, Z as SubscribeAudioChunksInputSchema, _ as DeviceFeature, a as adminUiCapability, at as BaseAddon, b as asBoolean, c as createMirrorSource, ct as createEvent, d as DEVICE_STATUS_METHOD, dt as WELL_KNOWN_TABS, et as SubscribeFramesResultSchema, f as event, ft as WELL_KNOWN_TAB_MAP, g as ChargingStatus, h as method, ht as DisposerChain, i as deviceOpsCapability, it as selectAssignedProfileSlots, j as emitDownForOwnedCaps, k as ReadinessRegistry, l as createSliceHandle, lt as emitReadiness, m as isDeviceConfigCap, mt as EventCategory, n as sleepCancellable, nt as makeSourceBrokerId, o as createDeviceProxy, ot as normalizeAddonInitResult, p as expandCapMethods, pt as hydrateSchema, q as ProfileSlotSchema, r as RawStateResultSchema, rt as parseProfileBrokerId, s as createLazyTrpcSource, st as createDurableState, t as sleep, tt as makeProfileBrokerId, u as DEVICE_SETTINGS_CONTRIBUTION_METHODS, ut as isEvent, v as DeviceRole, w as asString, x as asJsonArray, y as DeviceType, z as CamStreamResolutionSchema } from "./sleep-D7JeS58T.mjs";
2
+ import { t as errMsg } from "./err-msg-IQTHeDzc.mjs";
1
3
  import { z } from "zod";
2
4
  //#region src/health/wiring-health.ts
3
5
  /**
@@ -60,1376 +62,53 @@ var MODEL_FORMATS = [
60
62
  "pt"
61
63
  ];
62
64
  //#endregion
63
- //#region src/disposer-chain.ts
64
- var DisposerChain = class {
65
- disposers = [];
66
- disposed = false;
67
- onError;
68
- constructor(opts = {}) {
69
- this.onError = opts.onError ?? ((err, index) => {
70
- console.error(`[DisposerChain] disposer #${index} threw`, err);
71
- });
72
- }
73
- /**
74
- * Register a teardown callback. Returns an unregister function so
75
- * callers can drop a single disposer without disposing the whole
76
- * chain.
77
- *
78
- * If the chain has already been disposed, the callback runs immediately
79
- * (sync) — this matches the “register-after-shutdown” edge case where
80
- * an addon's late initialization races with kernel restart.
81
- */
82
- add(fn) {
83
- if (this.disposed) {
84
- try {
85
- const result = fn();
86
- if (result && typeof result.then === "function") result.catch((err) => this.onError(err, -1));
87
- } catch (err) {
88
- this.onError(err, -1);
89
- }
90
- return () => void 0;
91
- }
92
- this.disposers.push(fn);
93
- return () => {
94
- const idx = this.disposers.indexOf(fn);
95
- if (idx >= 0) this.disposers.splice(idx, 1);
96
- };
97
- }
98
- /** True after `dispose()` has been called at least once. */
99
- get isDisposed() {
100
- return this.disposed;
101
- }
102
- /** Number of disposers currently registered. */
103
- get size() {
104
- return this.disposers.length;
105
- }
106
- /**
107
- * Run every registered disposer in LIFO order. Idempotent: subsequent
108
- * calls do nothing. Awaits async disposers so callers can sequence
109
- * shutdown → restart correctly.
110
- */
111
- async dispose() {
112
- if (this.disposed) return;
113
- this.disposed = true;
114
- const drain = this.disposers.slice().toReversed();
115
- this.disposers = [];
116
- for (let i = 0; i < drain.length; i++) {
117
- const fn = drain[i];
118
- try {
119
- const result = fn();
120
- if (result && typeof result.then === "function") await result;
121
- } catch (err) {
122
- this.onError(err, drain.length - 1 - i);
123
- }
124
- }
125
- }
126
- };
127
- //#endregion
128
- //#region src/interfaces/addon.ts
129
- var DEFAULT_ADDON_PLACEMENT = "hub-only";
130
- /**
131
- * Resolve the effective `AddonExecution` for an addon. `placement`
132
- * always resolves to a concrete value (defaults to `hub-only`).
133
- * `group` is left as-declared — `undefined` when the addon declares no
134
- * co-location group, which means it runs on its own dedicated runner.
135
- */
136
- function resolveAddonExecution(decl) {
137
- return {
138
- placement: decl.execution?.placement ?? "hub-only",
139
- group: decl.execution?.group
140
- };
141
- }
142
- /** True when the addon may run on a remote agent (deployable from hub). */
143
- function isDeployableToAgent(decl) {
144
- const placement = resolveAddonExecution(decl).placement;
145
- return placement === "any-node" || placement === "agent-only";
146
- }
147
- /** True when the addon is skipped on the hub (agent-only deployment). */
148
- function isAgentOnlyPlacement(decl) {
149
- return resolveAddonExecution(decl).placement === "agent-only";
150
- }
151
- /**
152
- * Convenience accessor — the declared co-location group label, or
153
- * `undefined` when the addon declares no group (→ its own dedicated
154
- * runner). Callers that need the runner id should use `resolveRunnerId`.
155
- */
156
- function resolveAddonGroup(decl) {
157
- return resolveAddonExecution(decl).group;
158
- }
159
- /**
160
- * Resolve the runner id an addon belongs to. This is the single
161
- * authority for the hub↔runner topology:
162
- * - declared `group` present → the runner id is the group name
163
- * (shared with every other addon declaring the same group);
164
- * - no `group` declared → the runner id is the addon id itself
165
- * (its own dedicated runner — one addon, one process).
166
- *
167
- * The runner's Moleculer nodeID is `${parentNodeId}/${runnerId}`.
168
- */
169
- function resolveRunnerId(decl, addonId) {
170
- return decl.execution?.group ?? addonId;
171
- }
172
- /** Convenience accessor — the resolved placement (defaults to `hub-only`). */
173
- function resolveAddonPlacement(decl) {
174
- return resolveAddonExecution(decl).placement;
175
- }
176
- //#endregion
177
- //#region src/enums/event-category.ts
178
- var EventCategory = /* @__PURE__ */ function(EventCategory) {
179
- EventCategory["SystemBoot"] = "system.boot";
180
- EventCategory["SystemAddonsReady"] = "system.addons-ready";
181
- EventCategory["SystemRestarting"] = "system.restarting";
182
- /**
183
- * Fired exactly once after the hub finishes booting following a
184
- * restart that was triggered by `RestartCoordinator` (today: framework
185
- * live-update). Payload is the marker that was on disk at boot
186
- * (`PendingRestartMarkerPayload`); admin UI listens for it to display a
187
- * success toast describing what changed.
188
- *
189
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
190
- */
191
- EventCategory["SystemRestartCompleted"] = "system.restart-completed";
192
- /**
193
- * Readiness transition for a capability provider. Every producer emits
194
- * this event on `onInitialize` completion, `onDestroy`, and
195
- * `$node.reconnect`; every consumer that needs to gate on a cross-process
196
- * cap subscribes via the kernel's readiness module (`awaitReady` /
197
- * `onReadyState`) instead of polling. Payload is
198
- * `SystemReadyStatePayload` — see event-bus.ts.
199
- */
200
- EventCategory["SystemReadyState"] = "system.ready-state";
201
- EventCategory["AddonStarted"] = "addon.started";
202
- EventCategory["AddonStopped"] = "addon.stopped";
203
- EventCategory["AddonRestarted"] = "addon.restarted";
204
- EventCategory["AddonUpdated"] = "addon.updated";
205
- EventCategory["AddonInstalled"] = "addon.installed";
206
- EventCategory["AddonUninstalled"] = "addon.uninstalled";
207
- EventCategory["AddonCrashed"] = "addon.crashed";
208
- EventCategory["AddonError"] = "addon.error";
209
- EventCategory["AddonPageReady"] = "addon.page-ready";
210
- EventCategory["AddonWidgetReady"] = "addon.widget-ready";
211
- /**
212
- * Addon failed to load (import or initialize). Emitted by the kernel's
213
- * AddonHealthMonitor only AFTER the boot grace period ends — failures
214
- * during the first 5 minutes are silently retried without alerting
215
- * (slow-starting addons must have time to come up). Post-grace, this
216
- * event is emitted exactly once per failure-streak; AlertCenter
217
- * consumes it to create a persistent operator-visible alert.
218
- *
219
- * Payload: `{ packageName, addonId?, error: { message, stack }, retryCount, nextRetryAt }`.
220
- */
221
- EventCategory["AddonLoadFailed"] = "addon.load-failed";
222
- /**
223
- * Addon recovered from a previous failure. Emitted when an addon
224
- * transitions from `failed` back to `healthy` (typically via the
225
- * monitor's auto-retry loop, or after manual `addons.retryLoad`).
226
- * AlertCenter dismisses the corresponding `AddonLoadFailed` alert
227
- * on this event.
228
- */
229
- EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
230
- /**
231
- * Monitor scheduled the next retry for a failed addon. Transient —
232
- * surfaced to the UI for live-updating the "next retry in Ns"
233
- * countdown on the Addons page row, NOT persisted as an alert.
234
- */
235
- EventCategory["AddonRetryScheduled"] = "addon.retry-scheduled";
236
- /**
237
- * Monitor is attempting to reload a failed addon NOW. UI uses this
238
- * to show a spinner during the retry attempt. Same transient nature
239
- * as AddonRetryScheduled.
240
- */
241
- EventCategory["AddonRetryAttempting"] = "addon.retry-attempting";
242
- EventCategory["DeviceRegistered"] = "device.registered";
243
- EventCategory["DeviceUnregistered"] = "device.unregistered";
244
- EventCategory["DeviceEnabled"] = "device.enabled";
245
- EventCategory["DeviceDisabled"] = "device.disabled";
246
- EventCategory["DeviceSettingsUpdated"] = "device.settings-updated";
247
- /**
248
- * Emitted when the set of native capability providers bound to a device
249
- * changes — e.g. an addon registers a new native cap via
250
- * `DeviceContext.registerNativeCap`, or all native bindings for a device
251
- * are cleared on removal. Hub consumers re-resolve device-proxy routes
252
- * when this fires.
253
- */
254
- EventCategory["DeviceBindingsChanged"] = "device.bindings-changed";
255
- /**
256
- * Emitted when the operator-organisational meta surface changes
257
- * (`name` / `location` / `disabled`). Payload: `{deviceId, field,
258
- * value}`. Live consumers (UI device list, alert center) react
259
- * without polling. Distinct from `DeviceSettingsUpdated` which
260
- * fires on hardware-config changes (host/port/credentials/etc).
261
- */
262
- EventCategory["DeviceMetaChanged"] = "device.meta-changed";
263
- /**
264
- * Emitted by `BaseDevice.updateSourceInfo()` after a successful patch
265
- * to the device's upstream-system identity / rendering envelope. The
266
- * full new SourceInfo travels in the payload so cross-process listeners
267
- * (UI, export adapters) don't need to re-read the meta blob.
268
- *
269
- * Payload: `{deviceId, sourceInfo}`.
270
- *
271
- * Distinct from `DeviceMetaChanged` which covers the operator-edited
272
- * surface (`name` / `location` / `disabled`). The two share the
273
- * persistence layer (both ride on `device-manager.setMetadata` for
274
- * SourceInfo, or `setName`/`setLocation`/`setDisabled` for the meta
275
- * fields) but consumers care about different subsets.
276
- */
277
- EventCategory["DeviceSourceInfoChanged"] = "device.source-info-changed";
278
- /**
279
- * Emitted by DeviceStreamWiringService after a successful
280
- * `stream-broker.registerDeviceStreams` call. Payload includes
281
- * deviceId — consumers look up the full registered device info via
282
- * `brokerManager.getRegisteredDevice(deviceId)`.
283
- *
284
- * Replaces the legacy `StreamRouterService.onDeviceRegistered` callback.
285
- */
286
- EventCategory["DeviceStreamsRegistered"] = "device.streams-registered";
287
- /**
288
- * Device-level "fully provisioned" signal — the EXPORT trigger. Emitted once
289
- * a device's persisted export-relevant shape (its `DeviceFeature` set) is
290
- * established or changes, carrying `{deviceId, fingerprint, generation}`.
291
- * Export adapters (Alexa / HAP) react to THIS instead of the chatty per-cap
292
- * `DeviceBindingsChanged`, turning a boot's incomplete→complete trickle into
293
- * one clean delta. `fingerprint` is `canonicalDeviceFingerprint(shape)`.
294
- *
295
- * Spec: docs/superpowers/specs/2026-06-01-alexa-hap-export-reconciler-design.md
296
- */
297
- EventCategory["DeviceProvisioned"] = "device.provisioned";
298
- /**
299
- * Fires once when a device's initial feature-probe completes successfully
300
- * (lastProbedAt 0→>0); exported shape is now stable. Telemetry (may be
301
- * dropped) — consumers MUST also gate on the device record's `probed` flag.
302
- */
303
- EventCategory["DeviceReady"] = "device.ready";
304
- EventCategory["IntegrationEnabled"] = "integration.enabled";
305
- EventCategory["IntegrationDisabled"] = "integration.disabled";
306
- EventCategory["IntegrationDeleted"] = "integration.deleted";
307
- /** Emitted when a broker connection's status flips (connected /
308
- * disconnected / error). Carries `{ brokerId, status, error? }`. */
309
- EventCategory["BrokerStatusChanged"] = "broker.status-changed";
310
- /** Emitted for every subscription-matched message routed by a
311
- * broker provider. Carries `{ brokerId, subscriptionId, key, payload }`.
312
- * Consumers filter by `brokerId` + `subscriptionId` in the handler. */
313
- EventCategory["BrokerMessage"] = "broker.message";
314
- EventCategory["ProviderStarted"] = "provider.started";
315
- EventCategory["ProviderStopped"] = "provider.stopped";
316
- EventCategory["ProcessCrashed"] = "process.crashed";
317
- EventCategory["ProcessRestartScheduled"] = "process.restart_scheduled";
318
- EventCategory["ProcessRestarted"] = "process.restarted";
319
- EventCategory["RecordingStarted"] = "recording.started";
320
- EventCategory["RecordingStopped"] = "recording.stopped";
321
- EventCategory["RecordingError"] = "recording.error";
322
- EventCategory["RecordingHealthDegraded"] = "recording.health.degraded";
323
- EventCategory["RecordingStorageCritical"] = "recording.storage.critical";
324
- EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
325
- EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
326
- EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
327
- EventCategory["DetectionEvent"] = "detection.event";
328
- EventCategory["SessionTrackNew"] = "session.track.new";
329
- EventCategory["SessionTrackExpired"] = "session.track.expired";
330
- EventCategory["BenchmarkProgress"] = "benchmark.progress";
331
- EventCategory["PlatformProbePhase"] = "platform-probe.phase";
332
- EventCategory["PipelineProgress"] = "pipeline.progress";
333
- /** Per-frame execution trace emitted by the pipeline executor for live observability. */
334
- EventCategory["PipelineTrace"] = "pipeline.trace";
335
- /**
336
- * Raw inference output emitted by `addon-pipeline-runner` after running the
337
- * detection pipeline on a frame. Carries the `FrameResult` (with
338
- * `detections[]`, `width`/`height`, timing debug) — never the frame
339
- * buffer itself. Hub-side consumers (analysis pipeline, class filters,
340
- * notifications) subscribe to this and re-emit `detection.result` after
341
- * post-processing.
342
- */
343
- EventCategory["PipelineInferenceResult"] = "pipeline.inference-result";
344
- /**
345
- * Camera lifecycle event emitted by `addon-pipeline-orchestrator` when it
346
- * assigns or unassigns a camera to/from an agent. Carries no frame data;
347
- * pure observability for UI dashboards and metrics consumers.
348
- */
349
- EventCategory["PipelineCameraAssigned"] = "pipeline.camera-assigned";
350
- EventCategory["PipelineCameraUnassigned"] = "pipeline.camera-unassigned";
351
- /**
352
- * Per-camera pipeline config was mutated by the orchestrator
353
- * (3-level settings change via `setAgentAddonDefaults` /
354
- * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
355
- * pipeline-scoped `applyDeviceSettingsPatch`). Orchestrator
356
- * subscribes to its own emission to hot-reload the assigned runner
357
- * via `attachCamera` so the next frame executes against the new
358
- * engine/steps without waiting for a rebalance or the next
359
- * `DeviceStreamsRegistered` cycle.
360
- */
361
- EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
362
- /**
363
- * Periodic snapshot of per-node pipeline-runner load
364
- * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
365
- * subscribe instead of polling `pipelineRunner.getLocalLoad`.
366
- * `nodeId` carried in the payload + on `event.source.nodeId`.
367
- */
368
- EventCategory["PipelineRunnerLoadSnapshot"] = "pipeline.runner-load-snapshot";
369
- /**
370
- * Periodic snapshot of per-camera pipeline metrics (`CameraMetrics`
371
- * + `deviceId` + `nodeId`). Emitted ~1Hz by every runner for each
372
- * attached camera. UI subscribes to drive overlay phase / fps /
373
- * inference time without polling `getCameraMetrics`.
374
- */
375
- EventCategory["PipelineCameraMetricsSnapshot"] = "pipeline.camera-metrics-snapshot";
376
- /**
377
- * Periodic snapshot of stream-broker per-broker statistics (input
378
- * fps, decoded fps, bitrate, codec). Emitted ~1Hz by every
379
- * stream-broker process for each active broker so the UI can drive
380
- * the Stream / Cluster dashboards without polling
381
- * `streamBroker.listAllProfileSlots` and friends.
382
- */
383
- EventCategory["StreamBrokerMetricsSnapshot"] = "stream-broker.metrics-snapshot";
384
- /**
385
- * Cap event fired by `stream-broker` when a profile slot enters
386
- * "demanded" state — a cam stream has been assigned and at least one
387
- * consumer (RTSP restream, decoded subscriber, WebRTC session, …) is
388
- * present. Camera-provider addons (Reolink Baichuan push, …)
389
- * subscribe to this category to start their underlying transport
390
- * lazily. Payload: `{ deviceId, camStreamId, profile }`.
391
- */
392
- EventCategory["StreamBrokerOnCamStreamDemand"] = "stream-broker.onCamStreamDemand";
393
- /**
394
- * Cap event fired by `stream-broker` when the last consumer leaves a
395
- * previously-demanded cam stream. Providers tear down their
396
- * underlying transport on receipt. Payload: `{ deviceId, camStreamId }`.
397
- */
398
- EventCategory["StreamBrokerOnCamStreamIdle"] = "stream-broker.onCamStreamIdle";
399
- /**
400
- * Cap event fired by `stream-broker` when a broker fails to dial a
401
- * managed-loopback source (today: `pull-rfc4571`) and the publisher
402
- * needs to refresh the cached URL. The lib's TCP server idle-tears-down
403
- * on its own schedule, so a re-publish with a fresh `host:port` is the
404
- * only way to keep the broker dialable. Camera providers (Reolink
405
- * Baichuan native, …) subscribe and respond by re-running their publish
406
- * pipeline.
407
- * Payload: `{ deviceId, camStreamId, brokerId }`.
408
- */
409
- EventCategory["StreamBrokerOnRequestStreamSourceRefresh"] = "stream-broker.onRequestStreamSourceRefresh";
410
- /**
411
- * A camera provider changed a device's stream parameters (codec /
412
- * resolution / bitrate / a stream added or removed). A LOW-LATENCY NUDGE
413
- * for the stream-broker's catalog reconcile: it carries NO authoritative
414
- * data — the broker re-PULLS that device's `stream-catalog` cap on receipt
415
- * (the 30s reconcile poll is the backstop if this nudge is dropped). Emitted
416
- * by providers from `stream-params.setProfile`. Payload: `{ deviceId }`.
417
- */
418
- EventCategory["StreamParamsChanged"] = "stream-params.changed";
419
- /**
420
- * Generic per-device runtime-state change. Fired by `device-manager`
421
- * whenever a persisted slice in any cap's `runtimeState` shape
422
- * mutates. Payload: `{deviceId, capName, slice}`. Subscribers are
423
- * the `deviceState` cap router (cross-process listeners) and the
424
- * deviceProxy reactive bindings (`device.state.<capName>.value`).
425
- * Cap-specific events (`battery.onStatusChanged`, …) still fire
426
- * — they're authoritative for callers that want a typed payload
427
- * without filtering on `capName`.
428
- */
429
- EventCategory["DeviceStateChanged"] = "device.state-changed";
430
- /**
431
- * Cap event fired by every device that registers the `battery`
432
- * capability. Mirrors the cap definition's `onStatusChanged`. Carries
433
- * `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,
434
- * snapshot wrapper, UI) react to charge/sleep transitions without
435
- * polling `batteryCapability.getStatus`.
436
- */
437
- EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
438
- /**
439
- * Emitted by the battery cap provider WHEN `wakeForStream` enters the
440
- * "wake in progress" window — between the Baichuan wake-up issue and
441
- * the camera's first dialed-back RTP packet. The stream-broker
442
- * manager subscribes to flip the per-broker placeholder reason to
443
- * `'waking'` so viewers see a labelled "WAKING UP" tile instead of
444
- * the generic `'reconnecting'` frame. Carries `{ deviceId }`. The
445
- * complementary "wake complete" signal is the existing
446
- * `BatteryOnStatusChanged { sleeping: false }` event.
447
- */
448
- EventCategory["BatteryOnWakeStarted"] = "battery.onWakeStarted";
449
- /**
450
- * Cap event fired by every device that registers the `doorbell`
451
- * capability. Mirrors `doorbellCapability.events.onPressed`. Carries
452
- * `{ deviceId, timestamp }`. Operators consuming the UI subscribe
453
- * here to render transient ring toasts and a "Recent presses" row
454
- * on the device detail page.
455
- */
456
- EventCategory["DoorbellOnPressed"] = "doorbell.onPressed";
457
- /**
458
- * Cap event fired by every device that registers the `event-emitter`
459
- * capability. Mirrors `eventEmitterCapability.events.onEvent`. Carries
460
- * `{ deviceId, eventType, data, timestamp, seq }` — the device's EXACT
461
- * declared event verbatim (NO normalization). Subscribers (UI event
462
- * stream, advanced-notifier rules) react to fired events without
463
- * holding a cap reference.
464
- */
465
- EventCategory["EventEmitted"] = "event-emitter.event";
466
- /**
467
- * Periodic snapshot of the per-node detection-pipeline engine
468
- * registry (loaded engines, models resident, in-use cameras, idle
469
- * TTL). Emitted ~0.2Hz (every 5 s) by every detection-pipeline
470
- * process. The Engines tab subscribes to drive its inventory view
471
- * without polling `pipelineExecutor.listLoadedEngines`.
472
- */
473
- EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
474
- /**
475
- * Cluster topology snapshot. Carries the same payload returned by
476
- * `nodes.topology` (every reachable node + addons + processes).
477
- * Emitted by the hub on any agent / addon lifecycle change
478
- * (debounced) plus a periodic safety net. Replaces UI polling on
479
- * `nodes.topology` — admin-ui dashboards subscribe to drive the
480
- * cluster view directly from the event payload.
481
- */
482
- EventCategory["ClusterTopologySnapshot"] = "cluster.topology-snapshot";
483
- /**
484
- * Periodic per-node system metrics snapshot (CPU / memory / GPU /
485
- * disk / network). Emitted ~0.2 Hz by the metrics-provider addon
486
- * for each node. Drives the dashboard SystemStatus / ProcessResources
487
- * widgets without polling `metricsProvider.getCurrent`.
488
- */
489
- EventCategory["MetricsNodeResourcesSnapshot"] = "metrics.node-resources-snapshot";
490
- /**
491
- * Periodic per-node process-tree snapshot (camstack-related pids
492
- * with ghost / managed / root classification). Emitted ~0.2 Hz by
493
- * the metrics-provider addon. Drives the Cluster → Processes tab
494
- * without polling `metricsProvider.listNodeProcesses`.
495
- */
496
- EventCategory["MetricsNodeProcessesSnapshot"] = "metrics.node-processes-snapshot";
497
- /**
498
- * Capability binding change event emitted by `addon-pipeline-orchestrator`
499
- * when a user changes which addon implements a cap on a node. Subscribed
500
- * by every kernel process to update its local `preferredProviderRegistry`
501
- * so future capability lookups respect the new binding.
502
- */
503
- /**
504
- * A capability binding was changed for a node — addon X now provides
505
- * capability `cap` on node `nodeId`. Lives under the generic
506
- * `capability.*` namespace because capability bindings are a kernel-
507
- * level concept used by many addons, not strictly pipeline-scoped.
508
- */
509
- EventCategory["CapabilityBindingChanged"] = "capability.binding-changed";
510
- EventCategory["ModelDownloadProgress"] = "model.download.progress";
511
- EventCategory["AgentRegistered"] = "agent.registered";
512
- EventCategory["AgentUnregistered"] = "agent.unregistered";
513
- EventCategory["AgentOnline"] = "agent.online";
514
- EventCategory["AgentOffline"] = "agent.offline";
515
- /** Forked worker process (e.g. hub/pipeline) connected to the broker. */
516
- EventCategory["WorkerOnline"] = "worker.online";
517
- /** Forked worker process disconnected from the broker. */
518
- EventCategory["WorkerOffline"] = "worker.offline";
519
- EventCategory["AgentTaskDispatched"] = "agent.task.dispatched";
520
- EventCategory["AgentTaskAssigned"] = "agent.task.assigned";
521
- EventCategory["AgentTrpcConnected"] = "agent.trpc.connected";
522
- EventCategory["AgentWsConnected"] = "agent.ws.connected";
523
- EventCategory["AgentWsDisconnected"] = "agent.ws.disconnected";
524
- EventCategory["AgentBackupActivated"] = "agent.backup.activated";
525
- EventCategory["OrchestrationSettingsUpdated"] = "orchestration.settings-updated";
526
- /**
527
- * Per-agent hwaccel preference changed (user override set, cleared, or
528
- * re-probed). Observability event — decoders pull the current pref at
529
- * `createSession`, so running sessions keep their current backend until
530
- * they rotate naturally (camera add/remove, stream restart). Future
531
- * work can wire a listener in stream-broker that force-rotates live
532
- * sessions; for now this event powers logs + admin-UI toast feedback.
533
- */
534
- EventCategory["PipelineAgentHwaccelChanged"] = "pipeline.agent-hwaccel-changed";
535
- EventCategory["MotionAnalysis"] = "detection.motion-analysis";
536
- /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */
537
- EventCategory["MotionZonesRaw"] = "detection.motion-zones-raw";
538
- /**
539
- * Per-camera motion phase transition (`watching ↔ active`) emitted
540
- * by the runner. Mirrors the `motion.onMotionChanged` cap event
541
- * surface — payload `MotionOnMotionChangedPayload` carries
542
- * `{deviceId, detected, timestamp, source, regions?}`. Subscribers
543
- * include addons that need to react to motion state without
544
- * polling the runtime-state mirror.
545
- */
546
- EventCategory["MotionOnMotionChanged"] = "motion.on-motion-changed";
547
- EventCategory["DetectionResult"] = "detection.result";
548
- EventCategory["DetectionRaw"] = "detection.raw";
549
- EventCategory["DetectionCameraNative"] = "detection.camera-native";
550
- /**
551
- * Canonical per-chunk live audio pipeline output. Payload is
552
- * `PipelineAudioInferenceResultPayload` carrying a full `AudioResult`
553
- * (level + detections + debug). Lives on the pipeline.* namespace
554
- * alongside `pipeline.inference-result` (video) for symmetry.
555
- */
556
- EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
557
- EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
558
- EventCategory["ProviderMotion"] = "provider.motion";
559
- EventCategory["ProviderDetection"] = "provider.detection";
560
- EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
561
- EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
562
- EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
563
- EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
564
- EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
565
- EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
566
- EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
567
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
568
- EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
569
- /**
570
- * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
571
- * `stream.offline` fires after STREAM_STALE_TIMEOUT_MS without an encoded
572
- * packet on an active broker. `stream.online` fires on first packet
573
- * after a stale gap (or on initial first packet). Payload includes
574
- * the assigned camStreamId as `profileKey`.
575
- */
576
- EventCategory["StreamOnline"] = "stream.online";
577
- EventCategory["StreamOffline"] = "stream.offline";
578
- EventCategory["NetworkTunnelStarted"] = "network.tunnel.started";
579
- EventCategory["NetworkTunnelStopped"] = "network.tunnel.stopped";
580
- /** Fired by the `local-network` cap when the host's interface set
581
- * changes (new IP from DHCP, VPN connect, docker bridge added). */
582
- EventCategory["LocalNetworkChanged"] = "network.local.changed";
583
- /**
584
- * Fired by a `mesh-network` provider (Tailscale, …) when its
585
- * mesh-reachable host changes (join / leave / MagicDNS or 100.x IP
586
- * change). Lets `local-network` fold the mesh endpoint into its
587
- * connection-endpoint list without a cross-package import. Payload
588
- * carries the preferred host (`host: ''` = no longer reachable),
589
- * the hub port, and the scheme. Telemetry-grade (D8): consumers
590
- * pull-reconcile from the provider's `getStatus` on reconnect. */
591
- EventCategory["MeshNetworkChanged"] = "network.mesh.changed";
592
- EventCategory["BackupCompleted"] = "backup.completed";
593
- EventCategory["BackupRestored"] = "backup.restored";
594
- EventCategory["NotificationDispatched"] = "notification.dispatched";
595
- EventCategory["NotificationFailed"] = "notification.failed";
596
- EventCategory["DeviceUpdated"] = "device.updated";
597
- /**
598
- * Transport-level connectivity. Emitted by the device driver when
599
- * the underlying control socket actually connects / disconnects
600
- * (Baichuan TCP, ONVIF probe response, RTSP DESCRIBE, …) — NOT for
601
- * power-state transitions on a battery camera. For battery wake /
602
- * doze cycles see `DeviceAwake` / `DeviceSleeping`.
603
- */
604
- EventCategory["DeviceOnline"] = "device.online";
605
- /**
606
- * Stream-broker watchdog — emitted when no encoded packet has been
607
- * received for STREAM_STALE_TIMEOUT_MS on an active broker (rtsp or
608
- * push). Paired with DeviceOnline which fires on first packet after
609
- * a stale gap. Payload: DeviceStreamHealthPayload.
610
- */
611
- EventCategory["DeviceOffline"] = "device.offline";
612
- /**
613
- * Battery cam woke up — physical power-state transition reported
614
- * by the device firmware. Distinct from `DeviceOnline` so a UI panel
615
- * watching power state doesn't flap on every UDP socket reconnect.
616
- */
617
- EventCategory["DeviceAwake"] = "device.awake";
618
- /**
619
- * Battery cam went to sleep. See `DeviceAwake`.
620
- */
621
- EventCategory["DeviceSleeping"] = "device.sleeping";
622
- EventCategory["RetentionCleanup"] = "retention.cleanup";
623
- /**
624
- * Legacy bulk-update progress snapshot (payload `BulkUpdateState`). No longer
625
- * emitted — F3 removed the coordinator that produced it; "Update all" now runs
626
- * as one lifecycle engine job (`AddonsJobProgress`/`AddonsJobLog`). Retained
627
- * (with `BulkUpdateState`) only to avoid regenerating the event maps; removed
628
- * in F4 once live bulk progress is re-implemented over the engine events.
629
- */
630
- EventCategory["AddonsBulkUpdateProgress"] = "addons.bulk-update-progress";
631
- EventCategory["AddonsJobProgress"] = "addons.job-progress";
632
- EventCategory["AddonsJobLog"] = "addons.job-log";
633
- /**
634
- * A container's child visibility toggled (hidden/shown). Emitted by the
635
- * `accessories` cap when a child device is hidden or revealed.
636
- * Payload: `{ deviceId, childDeviceId, hidden }`.
637
- */
638
- EventCategory["AccessoriesChildVisibilityChanged"] = "accessories.onChildVisibilityChanged";
639
- /**
640
- * A container's child set changed (children added/removed/reordered).
641
- * Payload: `{ deviceId, childDeviceIds, hiddenChildIds }`.
642
- */
643
- EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
644
- return EventCategory;
645
- }({});
646
- //#endregion
647
- //#region src/interfaces/config-ui.ts
648
- /** Predefined tabs with standard label, icon, and sort order.
649
- *
650
- * Pipeline (renamed Orchestrator in the UI) holds the four
651
- * pipeline-orchestrator sections: General, Object Detection, Audio,
652
- * and Cluster Assignment. Motion stays its own tab.
653
- * `streaming` remains for older payloads that haven't been retagged. */
654
- var WELL_KNOWN_TABS = [
655
- {
656
- id: "overview",
657
- label: "Overview",
658
- icon: "layout-dashboard",
659
- order: -10
660
- },
661
- {
662
- id: "general",
663
- label: "General",
664
- icon: "settings",
665
- order: 0
666
- },
667
- {
668
- id: "image",
669
- label: "Image",
670
- icon: "image",
671
- order: 5
672
- },
673
- {
674
- id: "light",
675
- label: "Light",
676
- icon: "lightbulb",
677
- order: 7
678
- },
679
- {
680
- id: "motion",
681
- label: "Motion",
682
- icon: "activity",
683
- order: 10
684
- },
685
- {
686
- id: "audio",
687
- label: "Audio",
688
- icon: "mic",
689
- order: 12
690
- },
691
- {
692
- id: "alarms",
693
- label: "Alarms",
694
- icon: "bell-ring",
695
- order: 13
696
- },
697
- {
698
- id: "snapshot",
699
- label: "Snapshot",
700
- icon: "camera",
701
- order: 15
702
- },
703
- {
704
- id: "osd",
705
- label: "OSD",
706
- icon: "type",
707
- order: 18
708
- },
709
- {
710
- id: "stream-broker",
711
- label: "Stream Broker",
712
- icon: "radio",
713
- order: 20
714
- },
715
- {
716
- id: "streaming",
717
- label: "Streaming",
718
- icon: "video",
719
- order: 35
720
- },
721
- {
722
- id: "ptz",
723
- label: "PTZ",
724
- icon: "move",
725
- order: 40
726
- },
727
- {
728
- id: "consumables",
729
- label: "Consumables",
730
- icon: "recycle",
731
- order: 44
732
- },
733
- {
734
- id: "pipeline",
735
- label: "Detection Pipeline",
736
- icon: "cpu",
737
- order: 39
738
- },
739
- {
740
- id: "detection-pipeline",
741
- label: "Detection pipeline",
742
- icon: "cpu",
743
- order: 39
744
- },
745
- {
746
- id: "zones",
747
- label: "Detection",
748
- icon: "shapes",
749
- order: 38
750
- },
751
- {
752
- id: "analytics",
753
- label: "Analytics",
754
- icon: "activity",
755
- order: 37
756
- },
757
- {
758
- id: "live-stats",
759
- label: "Live Stats",
760
- icon: "activity",
761
- order: 39
762
- },
763
- {
764
- id: "recording",
765
- label: "Recording",
766
- icon: "circle-dot",
767
- order: 40
768
- },
769
- {
770
- id: "engine",
771
- label: "Inference Engine",
772
- icon: "zap",
773
- order: 41
774
- },
775
- {
776
- id: "scheduler",
777
- label: "Scheduler",
778
- icon: "list-checks",
779
- order: 42
780
- },
781
- {
782
- id: "decoder",
783
- label: "Decoder",
784
- icon: "film",
785
- order: 43
786
- },
787
- {
788
- id: "notifications",
789
- label: "Notifications",
790
- icon: "bell",
791
- order: 50
792
- },
793
- {
794
- id: "network",
795
- label: "Network",
796
- icon: "globe",
797
- order: 60
798
- },
799
- {
800
- id: "storage",
801
- label: "Storage",
802
- icon: "hard-drive",
803
- order: 70
804
- },
805
- {
806
- id: "advanced",
807
- label: "Advanced",
808
- icon: "wrench",
809
- order: 100
810
- }
811
- ];
812
- /** Lookup map for well-known tabs by ID. */
813
- var WELL_KNOWN_TAB_MAP = Object.fromEntries(WELL_KNOWN_TABS.map((t) => [t.id, t]));
814
- /**
815
- * Field types that never carry a value (separator, info, button). Used by
816
- * `hydrateSchema` to skip the `value` injection for structural-only fields.
817
- */
818
- function isValuelessField(field) {
819
- return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button";
820
- }
821
- /**
822
- * Merge a `ConfigUISchema` with a raw `values` record into a
823
- * `ConfigUISchemaWithValues`. Used by every `get*Settings` backend endpoint
824
- * before returning to the admin UI. Groups are walked recursively.
825
- *
826
- * For each leaf field:
827
- * - structural fields (`separator`, `info`, `button`) pass through unchanged
828
- * - other fields get `value = values[key] ?? field.default ?? null`
829
- *
830
- * Unknown keys in `values` (keys not declared in the schema) are silently
831
- * ignored — they don't contribute to the output. Missing keys fall back to
832
- * the schema default or `null`. This mirrors the old backend behaviour where
833
- * `FormBuilder` rendered `values[key] ?? field.default`.
834
- */
835
- function hydrateSchema(schema, values) {
836
- return {
837
- ...schema.tabs ? { tabs: [...schema.tabs] } : {},
838
- sections: schema.sections.map((section) => ({
839
- ...section,
840
- fields: section.fields.map((field) => hydrateField(field, values))
841
- }))
842
- };
843
- }
844
- function hydrateField(field, values) {
845
- if (isValuelessField(field)) return field;
846
- if (field.type === "group") {
847
- const hydratedChildren = field.fields.map((child) => hydrateField(child, values));
848
- return {
849
- ...field,
850
- fields: hydratedChildren
851
- };
852
- }
853
- if (field.type === "sub-tabs") {
854
- const hydratedTabs = field.tabs.map((tab) => ({
855
- ...tab,
856
- fields: tab.fields.map((child) => hydrateField(child, values))
857
- }));
858
- return {
859
- ...field,
860
- tabs: hydratedTabs
861
- };
862
- }
863
- const key = field.key;
864
- const storedValue = Object.prototype.hasOwnProperty.call(values, key) ? values[key] : void 0;
865
- const defaultValue = field.default;
866
- if (field.multiple) {
867
- const stored = Array.isArray(storedValue) ? storedValue : storedValue !== void 0 && storedValue !== null ? [storedValue] : [];
868
- const itemFallback = field.multiple.itemDefault !== void 0 ? field.multiple.itemDefault : defaultValue !== void 0 ? defaultValue : typeOf(field) === "string" ? "" : null;
869
- const minCount = Math.max(field.multiple.min, stored.length);
870
- const items = [];
871
- for (let i = 0; i < minCount; i++) items.push(i < stored.length ? stored[i] : itemFallback);
872
- return {
873
- ...field,
874
- value: items
875
- };
876
- }
877
- const rawValue = storedValue !== void 0 ? storedValue : defaultValue !== void 0 ? defaultValue : null;
878
- if (field.type === "password") return {
879
- ...field,
880
- value: ""
881
- };
882
- const value = field.type === "textarea" && field.isJson && rawValue !== null && typeof rawValue === "object" ? JSON.stringify(rawValue, null, 2) : rawValue;
883
- return {
884
- ...field,
885
- value
886
- };
887
- }
888
- /**
889
- * Rough "value family" classifier used by `hydrateField`'s multiple
890
- * fallback to pick a sensible zero-value when no `itemDefault` / no
891
- * field `default` / no stored value is available.
892
- */
893
- function typeOf(field) {
894
- switch (field.type) {
895
- case "text":
896
- case "textarea":
897
- case "password":
898
- case "color":
899
- case "probe":
900
- case "timezone":
901
- case "datetime": return "string";
902
- case "number":
903
- case "slider": return "number";
904
- case "boolean": return "boolean";
905
- default: return "other";
906
- }
907
- }
908
- //#endregion
909
- //#region src/interfaces/event-bus.ts
910
- /**
911
- * Narrow a SystemEvent to a typed event by checking its category.
912
- * Returns `true` (and narrows the type) if the category matches.
913
- *
914
- * @example
915
- * ```typescript
916
- * eventBus.subscribe({ category: 'addon.started' }, (event) => {
917
- * if (isEvent(event, 'addon.started')) {
918
- * event.data.addonId // ✓ typed as string
919
- * }
920
- * })
921
- * ```
922
- */
923
- function isEvent(event, category) {
924
- return event.category === category;
925
- }
926
- /**
927
- * Create a typed event with less boilerplate.
928
- *
929
- * @example
930
- * ```typescript
931
- * eventBus.emit(createEvent('addon.started', { type: 'addon', id: 'pipeline' }, {
932
- * addonId: 'pipeline',
933
- * packageVersion: '0.1.8',
934
- * }))
935
- * ```
936
- */
937
- function createEvent(category, source, data) {
938
- return {
939
- id: typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2),
940
- timestamp: /* @__PURE__ */ new Date(),
941
- source,
942
- category,
943
- data
944
- };
945
- }
946
- /**
947
- * Emit a `system.ready-state` event for a capability. Caller supplies a
948
- * per-process `generation` string — stable across a single process
949
- * lifetime, changes on restart — which consumer-side registries use to
950
- * derive a monotonic `epoch` without requiring the emitter to
951
- * coordinate.
952
- */
953
- function emitReadiness(bus, params) {
954
- const ts = params.ts ?? Date.now();
955
- bus.emit(createEvent("system.ready-state", {
956
- type: "capability",
957
- id: params.capName,
958
- nodeId: params.sourceNodeId
959
- }, {
960
- capName: params.capName,
961
- scope: params.scope,
962
- state: params.state,
963
- generation: params.generation,
964
- sourceNodeId: params.sourceNodeId,
965
- ts
966
- }));
967
- }
968
- //#endregion
969
- //#region src/addon/durable-state.ts
970
- /**
971
- * Build a {@link DurableState} over a single store key. Transport-agnostic:
972
- * `read`/`write` are the addon-store or device-store accessors. The whole
973
- * validated value round-trips on every read/write — the schema is the
974
- * single source of truth for what is persisted, so no hand-listed field
975
- * set can drop a key on save.
976
- */
977
- function createDurableState(deps) {
978
- const get = async () => {
979
- const raw = (await deps.read())[deps.key];
980
- if (raw === void 0) return deps.fallback;
981
- const parsed = deps.schema.safeParse(raw);
982
- if (!parsed.success) {
983
- deps.onParseError?.(deps.key, parsed.error);
984
- return deps.fallback;
985
- }
986
- return parsed.data;
987
- };
988
- const set = async (next) => {
989
- const validated = deps.schema.parse(next);
990
- await deps.write({ [deps.key]: validated });
991
- };
992
- const update = async (fn) => {
993
- await set(fn(await get()));
994
- };
995
- return {
996
- get,
997
- set,
998
- update
999
- };
1000
- }
1001
- //#endregion
1002
- //#region src/addon/base-addon.ts
1003
- /**
1004
- * Base class for CamStack addons. Eliminates settings boilerplate:
1005
- *
1006
- * - Typed `config` property with automatic resolution from store + defaults
1007
- * - `getGlobalSettings()` / `updateGlobalSettings()` auto-implemented
1008
- * - `getAddonSettings()` / `updateAddonSettings()` auto-implemented
1009
- * - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented
1010
- * - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)
1011
- * - `field()` helper that constrains field keys to TConfig keys
1012
- * - `schema()` helper that validates the full schema structure
1013
- *
1014
- * Subclasses override:
1015
- * - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]
1016
- * - `onShutdown()` — cleanup
1017
- * - `globalSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
1018
- *
1019
- * @example
1020
- * ```ts
1021
- * interface MyConfig {
1022
- * maxRetries: number
1023
- * endpoint: string
1024
- * }
1025
- *
1026
- * export default class MyAddon extends BaseAddon<MyConfig> {
1027
- * constructor() {
1028
- * super({ maxRetries: 3, endpoint: 'http://localhost' })
1029
- * }
1030
- *
1031
- * protected globalSettingsSchema() {
1032
- * return this.schema({
1033
- * sections: [{
1034
- * id: 'main', title: 'Settings',
1035
- * fields: [
1036
- * this.field({ type: 'number', key: 'maxRetries', label: 'Max Retries', default: 3 }),
1037
- * this.field({ type: 'text', key: 'endpoint', label: 'Endpoint' }),
1038
- * ],
1039
- * }],
1040
- * })
1041
- * }
1042
- *
1043
- * protected async onInitialize(): Promise<ProviderRegistration[]> {
1044
- * const client = new Client(this.config.endpoint, this.config.maxRetries)
1045
- * return [{ capability: 'my-feature', provider: client }]
1046
- * }
1047
- * }
1048
- * ```
1049
- */
1050
- var BaseAddon = class {
1051
- _ctx = null;
1052
- _config;
1053
- /**
1054
- * Per-process random id used as the `generation` stamp on every
1055
- * `system.ready-state` event this addon emits. Constant for the
1056
- * lifetime of this addon instance (== one process boot); consumer-
1057
- * side registries derive a monotonic `epoch` by watching for
1058
- * generation transitions.
1059
- */
1060
- _readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
1061
- /** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
1062
- _registeredCapNames = [];
1063
- /** Default config values. Provided via constructor. */
1064
- defaults;
1065
- constructor(defaults) {
1066
- this.defaults = defaults;
1067
- this._config = { ...defaults };
1068
- }
1069
- /**
1070
- * Override to opt out of the automatic `system.ready-state` emission.
1071
- * Returns `true` by default — addons that don't map cleanly to the
1072
- * readiness protocol (e.g. pure collection providers whose readiness
1073
- * is already reported per-device by upstream) can override to `false`
1074
- * and emit manually.
1075
- */
1076
- get autoEmitReadiness() {
1077
- return true;
1078
- }
1079
- /** The AddonContext, available after initialize(). */
1080
- get ctx() {
1081
- if (!this._ctx) throw new Error(`${this.constructor.name}: ctx accessed before initialize()`);
1082
- return this._ctx;
1083
- }
1084
- /**
1085
- * Non-throwing ctx accessor for code paths that can legitimately run
1086
- * before `initialize()` has resolved — most commonly cap handlers the
1087
- * hub invokes eagerly at page load (device-details aggregator pings
1088
- * every addon's `getDeviceSettingsContribution` as soon as the page
1089
- * mounts). Prefer `ctx` for the normal case; reach for this only in
1090
- * capability methods that may be queried before the addon is wired up.
1091
- */
1092
- get ctxIfReady() {
1093
- return this._ctx;
1094
- }
1095
- /** Current resolved config (defaults merged with persisted store values). */
1096
- get config() {
1097
- return this._config;
1098
- }
1099
- async initialize(context) {
1100
- this._ctx = context;
1101
- await this.resolveConfig();
1102
- const result = await this.onInitialize();
1103
- this.emitLifecycle(EventCategory.AddonStarted);
1104
- const normalized = normalizeAddonInitResult(result);
1105
- const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
1106
- this._registeredCapNames = providers.map((p) => p.capability.name);
1107
- return normalized;
1108
- }
1109
- /**
1110
- * Called by the isolated-process runner AFTER `broker.start()`.
1111
- * In-process addons never need this because the hub broker is already
1112
- * running when `initialize()` fires. For forked children the broker
1113
- * starts AFTER `initialize()`, so the readiness emit inside initialize()
1114
- * fires before the broker can broadcast it. This method re-emits the
1115
- * ready state once the transport is live.
1116
- */
1117
- postBrokerStart() {
1118
- if (this.autoEmitReadiness && this._registeredCapNames.length > 0) this.emitReadinessForProviders("ready");
1119
- }
1120
- /**
1121
- * Called by the isolated-process runner / agent bootstrap once the broker
1122
- * has started AND the hub node is connected — the moment `ctx.api.*` calls
1123
- * to hub-provided capabilities become safe. Wrap any bootstrap work that
1124
- * must query the hub (device restore, settings fetch, initial sync) in
1125
- * `onHubConnected()` rather than `onInitialize()` — during `initialize()`
1126
- * on a worker the broker is not yet connected and remote cap calls either
1127
- * time out or throw "Service not found".
1128
- *
1129
- * Default implementation is a no-op. Override in subclasses that need it.
1130
- * Never fires on hub-local in-process addons (the hub is its own node).
1131
- */
1132
- async onHubReachable() {}
1133
- async shutdown() {
1134
- this.emitLifecycle(EventCategory.AddonStopped);
1135
- if (this.autoEmitReadiness) this.emitReadinessForProviders("down");
1136
- await this.onShutdown();
1137
- for (const unsub of this._subscriptions) unsub();
1138
- this._subscriptions = [];
1139
- this._ctx = null;
1140
- }
1141
- /** Addon-specific cleanup. Override if needed. */
1142
- async onShutdown() {}
1143
- /**
1144
- * Called after config is resolved during updateGlobalSettings/updateAddonSettings.
1145
- * Override to react to config changes (e.g. restart a sampler, reconnect a service).
1146
- * Not called during initialize() — use onInitialize() for initial setup.
1147
- */
1148
- async onConfigChanged() {}
1149
- /**
1150
- * Create a ConfigField with `key` constrained to keys of TConfig.
1151
- * Provides autocomplete and compile-time validation.
1152
- */
1153
- field(field) {
1154
- return field;
1155
- }
1156
- /**
1157
- * Create a full ConfigUISchema with typed sections.
1158
- * Fields created via `this.field()` get key validation automatically.
1159
- */
1160
- schema(schema) {
1161
- return schema;
1162
- }
1163
- /** Override to provide global-level settings UI schema. */
1164
- globalSettingsSchema(_cap) {
1165
- return null;
1166
- }
1167
- /** Override to provide device-level settings UI schema. */
1168
- deviceSettingsSchema() {
1169
- return null;
1170
- }
1171
- async getGlobalSettings(overlay, cap) {
1172
- const schema = this.globalSettingsSchema(cap);
1173
- if (!schema) return { sections: [] };
1174
- const raw = await this._ctx?.settings?.readAddonStore() ?? {};
1175
- return hydrateSchema(schema, overlay ? {
1176
- ...raw,
1177
- ...overlay
1178
- } : raw);
1179
- }
1180
- async updateGlobalSettings(patch) {
1181
- await this._ctx?.settings?.writeAddonStore(patch);
1182
- await this.resolveConfig();
1183
- await this.onConfigChanged();
1184
- this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
1185
- this.maybeAutoRestart(patch, this.globalSettingsSchema());
1186
- }
1187
- /**
1188
- * If any field in `patch` is marked `requiresRestart` in `schema`,
1189
- * schedule an addon restart for the next tick. Deferred via
1190
- * `setImmediate` so the tRPC mutation that triggered the write has
1191
- * time to return its response before the addon is torn down and
1192
- * re-initialised by `AddonRegistryService.restartAddon`.
1193
- */
1194
- maybeAutoRestart(patch, schema) {
1195
- if (!schema) return;
1196
- const restartKeys = /* @__PURE__ */ new Set();
1197
- for (const section of schema.sections) for (const field of section.fields) {
1198
- if (field.type === "separator" || field.type === "info") continue;
1199
- if (field.requiresRestart) restartKeys.add(field.key);
1200
- }
1201
- if (restartKeys.size === 0) return;
1202
- if (!Object.keys(patch).some((k) => restartKeys.has(k))) return;
1203
- const ctx = this._ctx;
1204
- if (!ctx) return;
1205
- const addonId = ctx.id;
1206
- setImmediate(() => {
1207
- ctx.api.addons?.restartAddon?.mutate({ addonId }).then(() => {
1208
- ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: Object.keys(patch).filter((k) => restartKeys.has(k)) } });
1209
- }).catch((err) => {
1210
- ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
1211
- });
1212
- });
1213
- }
1214
- async getDeviceSettings(deviceId) {
1215
- const schema = this.deviceSettingsSchema();
1216
- if (!schema) return { sections: [] };
1217
- return hydrateSchema(schema, await this._ctx?.settings?.readDeviceStore(deviceId) ?? {});
1218
- }
1219
- async updateDeviceSettings(deviceId, patch) {
1220
- await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
1221
- }
1222
- _subscriptions = [];
1223
- /**
1224
- * Subscribe to an event bus category. The subscription is automatically
1225
- * unsubscribed on shutdown — no manual cleanup needed.
1226
- *
1227
- * @example
1228
- * ```ts
1229
- * this.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
1230
- * void this.handleDevice(event)
1231
- * })
1232
- * ```
1233
- */
1234
- /**
1235
- * Subscribe to `system.ready-state` events for one or more capabilities.
1236
- * Abstracts the boilerplate type-narrowing + nodeId extraction that every
1237
- * resilience subscription duplicates. Cleanup is automatic (registered on
1238
- * `_subscriptions` like a normal `subscribe` call).
1239
- */
1240
- watchCapability(capNames, handlers) {
1241
- const nameSet = new Set(Array.isArray(capNames) ? capNames : [capNames]);
1242
- this.subscribe({ category: "system.ready-state" }, (event) => {
1243
- const data = event.data;
1244
- if (typeof data.capName !== "string") return;
1245
- if (!nameSet.has(data.capName)) return;
1246
- if (data.scope?.type !== "node") return;
1247
- const nodeId = data.scope.nodeId;
1248
- if (typeof nodeId !== "string" || nodeId.length === 0) return;
1249
- const capName = data.capName;
1250
- if (data.state === "down") handlers.onDown?.(nodeId, capName);
1251
- else if (data.state === "ready") handlers.onReady?.(nodeId, capName);
1252
- });
1253
- }
1254
- subscribe(filter, handler) {
1255
- const unsub = this.ctx.eventBus.subscribe(filter, handler);
1256
- this._subscriptions.push(unsub);
1257
- }
1258
- emitLifecycle(category, data) {
1259
- try {
1260
- const ctx = this._ctx;
1261
- if (!ctx) return;
1262
- ctx.eventBus.emit({
1263
- id: `${ctx.id}-${Date.now()}`,
1264
- timestamp: /* @__PURE__ */ new Date(),
1265
- source: {
1266
- type: "addon",
1267
- id: ctx.id,
1268
- nodeId: ctx.kernel.localNodeId ?? "hub"
1269
- },
1270
- category,
1271
- data: {
1272
- addonId: ctx.id,
1273
- ...data
1274
- }
1275
- });
1276
- } catch {}
1277
- }
1278
- /**
1279
- * Emit a `system.ready-state` event for every capability this addon
1280
- * registered at init. Scope is `{type:'node', nodeId}` — readiness is
1281
- * tied to the node that hosts the provider. Consumers that care about
1282
- * per-device readiness can subscribe with `{type:'device', ...}` if
1283
- * the provider emits at finer granularity (collection addons may
1284
- * opt-out via `autoEmitReadiness` and emit manually).
1285
- */
1286
- emitReadinessForProviders(state) {
1287
- const ctx = this._ctx;
1288
- if (!ctx) return;
1289
- if (this._registeredCapNames.length === 0) return;
1290
- const rawNodeId = ctx.kernel?.localNodeId ?? "hub";
1291
- const nodeId = rawNodeId.includes("/") ? rawNodeId.split("/")[0] : rawNodeId;
1292
- for (const capName of this._registeredCapNames) try {
1293
- emitReadiness(ctx.eventBus, {
1294
- capName,
1295
- scope: {
1296
- type: "node",
1297
- nodeId
1298
- },
1299
- state,
1300
- generation: this._readinessGeneration,
1301
- sourceNodeId: nodeId
1302
- });
1303
- } catch {}
1304
- }
1305
- /**
1306
- * Resolve the shared models directory path via the storage capability.
1307
- * Falls back to `camstack-data/models` if storage is unavailable.
1308
- * Used by inference addons (detection-pipeline, audio-classifier, embedding-encoder).
1309
- */
1310
- async resolveModelsDir() {
1311
- return this.ctx.api.storage.resolve.query({
1312
- location: "models",
1313
- relativePath: ""
1314
- }).catch(() => "camstack-data/models");
1315
- }
1316
- /**
1317
- * Access the runtime capability registry for in-process provider lookups.
1318
- * Returns null if the registry is not available (e.g. on agents).
1319
- * Used by addons that consume other capabilities directly (snapshot, stream-broker, enrichment).
1320
- */
1321
- get capabilities() {
1322
- return this.ctx.capabilities ?? null;
1323
- }
1324
- /**
1325
- * Resolve config by merging defaults with persisted store values.
1326
- * Called automatically during initialize() and after every updateSettings().
1327
- *
1328
- * The merge is shallow: each key in `defaults` is checked against the store.
1329
- * Only keys present in defaults are read — the store can contain extra keys
1330
- * (e.g. from older versions) without polluting the typed config.
1331
- */
1332
- async resolveConfig() {
1333
- const stored = await this.readAddonStoreWithRetry();
1334
- const resolved = { ...this.defaults };
1335
- for (const key of Object.keys(this.defaults)) {
1336
- const storedValue = stored[key];
1337
- if (storedValue !== void 0 && storedValue !== null) {
1338
- const defaultType = typeof this.defaults[key];
1339
- if (typeof storedValue === defaultType) resolved[key] = storedValue;
1340
- }
1341
- }
1342
- this._config = resolved;
1343
- }
1344
- /**
1345
- * Typed durable handle over ONE key of this addon's store. The whole
1346
- * Zod-validated value round-trips on every read/write — no hand-listed
1347
- * fields, so a field can never be silently dropped on persist. Reads use
1348
- * the same retry budget as config resolution; a corrupt/legacy blob logs
1349
- * a warning and falls back rather than crashing boot.
1350
- */
1351
- state(key, schema, fallback) {
1352
- return createDurableState({
1353
- key,
1354
- schema,
1355
- fallback,
1356
- read: () => this.readAddonStoreWithRetry(),
1357
- write: async (patch) => {
1358
- await this._ctx?.settings?.writeAddonStore(patch);
1359
- },
1360
- onParseError: (k, e) => this._ctx?.logger?.warn?.(`durable-state: stored "${k}" failed validation — using fallback`, { meta: {
1361
- addonId: this._ctx?.id,
1362
- key: k,
1363
- error: String(e)
1364
- } })
1365
- });
1366
- }
1367
- /** Per-device variant of {@link state}, backed by the per-device store. */
1368
- deviceState(deviceId, key, schema, fallback) {
1369
- return createDurableState({
1370
- key,
1371
- schema,
1372
- fallback,
1373
- read: async () => await this._ctx?.settings?.readDeviceStore(deviceId) ?? {},
1374
- write: async (patch) => {
1375
- await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
1376
- },
1377
- onParseError: (k, e) => this._ctx?.logger?.warn?.(`durable-state: stored device ${deviceId} "${k}" failed validation — using fallback`, { meta: {
1378
- addonId: this._ctx?.id,
1379
- deviceId,
1380
- key: k,
1381
- error: String(e)
1382
- } })
1383
- });
1384
- }
1385
- /**
1386
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
1387
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
1388
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
1389
- * re-initialize) doesn't propagate up into `initialize()` and leave the
1390
- * addon permanently broken.
1391
- *
1392
- * Retry only the two known infra fingerprints. Anything else propagates so
1393
- * real bugs surface immediately. After the budget expires we fall back to
1394
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
1395
- */
1396
- async readAddonStoreWithRetry() {
1397
- const settings = this._ctx?.settings;
1398
- if (!settings) return {};
1399
- const delaysMs = [
1400
- 150,
1401
- 350,
1402
- 600,
1403
- 900
1404
- ];
1405
- let lastErr;
1406
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
1407
- return await settings.readAddonStore() ?? {};
1408
- } catch (err) {
1409
- lastErr = err;
1410
- const msg = err instanceof Error ? err.message : String(err);
1411
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
1412
- if (attempt === delaysMs.length) break;
1413
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
1414
- }
1415
- this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
1416
- return {};
1417
- }
1418
- };
65
+ //#region src/interfaces/addon.ts
66
+ var DEFAULT_ADDON_PLACEMENT = "hub-only";
67
+ /**
68
+ * Resolve the effective `AddonExecution` for an addon. `placement`
69
+ * always resolves to a concrete value (defaults to `hub-only`).
70
+ * `group` is left as-declared — `undefined` when the addon declares no
71
+ * co-location group, which means it runs on its own dedicated runner.
72
+ */
73
+ function resolveAddonExecution(decl) {
74
+ return {
75
+ placement: decl.execution?.placement ?? "hub-only",
76
+ group: decl.execution?.group
77
+ };
78
+ }
79
+ /** True when the addon may run on a remote agent (deployable from hub). */
80
+ function isDeployableToAgent(decl) {
81
+ const placement = resolveAddonExecution(decl).placement;
82
+ return placement === "any-node" || placement === "agent-only";
83
+ }
84
+ /** True when the addon is skipped on the hub (agent-only deployment). */
85
+ function isAgentOnlyPlacement(decl) {
86
+ return resolveAddonExecution(decl).placement === "agent-only";
87
+ }
88
+ /**
89
+ * Convenience accessor — the declared co-location group label, or
90
+ * `undefined` when the addon declares no group ( its own dedicated
91
+ * runner). Callers that need the runner id should use `resolveRunnerId`.
92
+ */
93
+ function resolveAddonGroup(decl) {
94
+ return resolveAddonExecution(decl).group;
95
+ }
1419
96
  /**
1420
- * Normalize an `ICamstackAddon.initialize()` return value into the
1421
- * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
1422
- * envelopes pass through; void stays void.
97
+ * Resolve the runner id an addon belongs to. This is the single
98
+ * authority for the hub↔runner topology:
99
+ * - declared `group` present the runner id is the group name
100
+ * (shared with every other addon declaring the same group);
101
+ * - no `group` declared → the runner id is the addon id itself
102
+ * (its own dedicated runner — one addon, one process).
1423
103
  *
1424
- * Exported so the kernel boot path and the backend addon registry can
1425
- * share the same normalizer instead of duplicating the shim. Addons that
1426
- * extend `BaseAddon` already emit the envelope, so this helper is only a
1427
- * safety net for direct `ICamstackAddon` implementations (mostly tests).
104
+ * The runner's Moleculer nodeID is `${parentNodeId}/${runnerId}`.
1428
105
  */
1429
- function normalizeAddonInitResult(result) {
1430
- if (result == null) return;
1431
- if (Array.isArray(result)) return { providers: result };
1432
- return result;
106
+ function resolveRunnerId(decl, addonId) {
107
+ return decl.execution?.group ?? addonId;
108
+ }
109
+ /** Convenience accessor — the resolved placement (defaults to `hub-only`). */
110
+ function resolveAddonPlacement(decl) {
111
+ return resolveAddonExecution(decl).placement;
1433
112
  }
1434
113
  //#endregion
1435
114
  //#region src/addon/build-addon-route-provider.ts
@@ -1667,441 +346,66 @@ var TIMEZONES = [
1667
346
  id: "America/Sao_Paulo",
1668
347
  label: "America/Sao Paulo (UTC−3)",
1669
348
  region: "Americas",
1670
- stdOffsetMinutes: -180,
1671
- dst: null
1672
- },
1673
- {
1674
- id: "Asia/Dubai",
1675
- label: "Asia/Dubai (UTC+4)",
1676
- region: "Asia",
1677
- stdOffsetMinutes: 240,
1678
- dst: null
1679
- },
1680
- {
1681
- id: "Asia/Kolkata",
1682
- label: "Asia/Kolkata (UTC+5:30)",
1683
- region: "Asia",
1684
- stdOffsetMinutes: 330,
1685
- dst: null
1686
- },
1687
- {
1688
- id: "Asia/Singapore",
1689
- label: "Asia/Singapore (UTC+8)",
1690
- region: "Asia",
1691
- stdOffsetMinutes: 480,
1692
- dst: null
1693
- },
1694
- {
1695
- id: "Asia/Shanghai",
1696
- label: "Asia/Shanghai (UTC+8)",
1697
- region: "Asia",
1698
- stdOffsetMinutes: 480,
1699
- dst: null
1700
- },
1701
- {
1702
- id: "Asia/Tokyo",
1703
- label: "Asia/Tokyo (UTC+9)",
1704
- region: "Asia",
1705
- stdOffsetMinutes: 540,
1706
- dst: null
1707
- },
1708
- {
1709
- id: "Australia/Sydney",
1710
- label: "Australia/Sydney (UTC+10 / AEDT)",
1711
- region: "Oceania",
1712
- stdOffsetMinutes: 600,
1713
- dst: {
1714
- offsetHours: 1,
1715
- startMonth: 10,
1716
- startWeekIndex: 1,
1717
- startWeekday: "Sunday",
1718
- startHour: 2,
1719
- endMonth: 4,
1720
- endWeekIndex: 1,
1721
- endWeekday: "Sunday",
1722
- endHour: 3
1723
- }
1724
- }
1725
- ];
1726
- /** Resolve an IANA id to its `Timezone`, or `undefined` if unknown. */
1727
- function findTimezone(id) {
1728
- return TIMEZONES.find((tz) => tz.id === id);
1729
- }
1730
- //#endregion
1731
- //#region src/capabilities/schemas/streaming-shared.ts
1732
- /** Shared Zod schemas used across streaming capabilities. */
1733
- var CamProfileSchema = z.enum([
1734
- "high",
1735
- "mid",
1736
- "low"
1737
- ]);
1738
- /** Canonical ordering. Hard-coded; never sorted. */
1739
- var CAM_PROFILE_ORDER = [
1740
- "high",
1741
- "mid",
1742
- "low"
1743
- ];
1744
- var CamStreamKindSchema = z.enum([
1745
- "pull-rtsp",
1746
- "pull-rtmp",
1747
- "pull-http",
1748
- "pull-rfc4571",
1749
- "push-annexb",
1750
- "derived"
1751
- ]);
1752
- var CamStreamResolutionSchema = z.object({
1753
- width: z.number().int().positive(),
1754
- height: z.number().int().positive()
1755
- });
1756
- var CameraStreamSchema = z.object({
1757
- /** Stable, provider-assigned id unique within the (deviceId) scope. */
1758
- camStreamId: z.string().min(1),
1759
- deviceId: z.number().int().nonnegative(),
1760
- kind: CamStreamKindSchema,
1761
- /** Required for pull-* kinds. Ignored for push-annexb. */
1762
- url: z.string().optional(),
1763
- codec: z.string().optional(),
1764
- resolution: CamStreamResolutionSchema.optional(),
1765
- fps: z.number().positive().optional(),
1766
- /** Human label surfaced in the Admin UI "Camera Stream" dropdown. */
1767
- label: z.string().optional(),
1768
- /**
1769
- * Device-level features the publisher advertised (e.g. `battery-operated`).
1770
- * The broker, snapshot orchestrator, and prebuffer manager all consult
1771
- * this list to derive policy — relaxed stall watchdog for battery
1772
- * cams, prebuffer off by default, longer snapshot rate-limit, etc.
1773
- *
1774
- * Single source of truth replacing per-stream flags like the
1775
- * historical `allowStall`: if the publisher knows the camera is
1776
- * battery-powered, every downstream service derives the right policy
1777
- * from this list.
1778
- */
1779
- deviceFeatures: z.array(z.string()).optional(),
1780
- /**
1781
- * Whether this stream participates in the broker's automatic profile
1782
- * assignment (`computeInitialAssignment`). Defaults to `true`. Publishers
1783
- * use `false` when they want a stream to be SELECTABLE in the UI but not
1784
- * picked by default — e.g. Reolink publishes its native Baichuan streams
1785
- * as `autoEligible: true` (the recommended path) and its RTSP / RTMP
1786
- * mirrors as `autoEligible: false` (still pickable per slot, just not
1787
- * the auto choice). Manual `assignProfile` calls remain valid for
1788
- * non-eligible streams.
1789
- */
1790
- autoEligible: z.boolean().optional(),
1791
- /**
1792
- * Transport-specific opaque metadata. The broker passes it through to
1793
- * the source reader without inspecting it. Currently used by
1794
- * `pull-rfc4571` streams to carry the upstream SDP (so the reader can
1795
- * route RTP packets to the right depacketizer without an in-band
1796
- * DESCRIBE phase). Other kinds typically leave it undefined.
1797
- */
1798
- metadata: z.record(z.string(), z.unknown()).optional()
1799
- });
1800
- var ProfileSlotStatusSchema = z.enum([
1801
- "unassigned",
1802
- "idle",
1803
- "connecting",
1804
- "streaming",
1805
- "error"
1806
- ]);
1807
- var ProfileSlotSchema = z.object({
1808
- deviceId: z.number().int().nonnegative(),
1809
- profile: CamProfileSchema,
1810
- /** Broker id the rest of the system addresses: `${deviceId}/${profile}`. */
1811
- brokerId: z.string(),
1812
- /** `null` when the profile is unassigned. */
1813
- sourceCamStreamId: z.string().nullable(),
1814
- status: ProfileSlotStatusSchema,
1815
- resolution: CamStreamResolutionSchema.optional(),
1816
- codec: z.string().optional(),
1817
- preBufferSec: z.number().nonnegative().optional(),
1818
- errorMessage: z.string().optional()
1819
- });
1820
- /** The canonical consumer-facing broker id for a device profile. */
1821
- function makeProfileBrokerId(deviceId, profile) {
1822
- return `${deviceId}/${profile}`;
1823
- }
1824
- /**
1825
- * The broker id keying a physical SOURCE stream: `${deviceId}/${camStreamId}`.
1826
- * This is the broker's internal source key (`brokerIdFor` in the manager) — a
1827
- * `camStreamId` like `native:main` — as opposed to the public profile alias
1828
- * {@link makeProfileBrokerId}. Used to resolve a source's restream endpoint
1829
- * (e.g. routing a transcode's ffmpeg input through the broker so it rides the
1830
- * single source dial instead of opening a second camera connection).
1831
- */
1832
- function makeSourceBrokerId(deviceId, camStreamId) {
1833
- return `${deviceId}/${camStreamId}`;
1834
- }
1835
- /**
1836
- * Inverse of {@link makeProfileBrokerId}. Returns `null` when the string is
1837
- * not a canonical `${deviceId}/${profile}` id — e.g. a broker-internal
1838
- * cam-stream id (`5/native:main`), a derived/adaptive ref, or a malformed
1839
- * value. So a caller can safely distinguish "addresses a public profile"
1840
- * from "addresses something broker-internal".
1841
- */
1842
- function parseProfileBrokerId(brokerId) {
1843
- const slash = brokerId.indexOf("/");
1844
- if (slash <= 0) return null;
1845
- const idPart = brokerId.slice(0, slash);
1846
- const profilePart = brokerId.slice(slash + 1);
1847
- if (!/^\d+$/.test(idPart)) return null;
1848
- const parsed = CamProfileSchema.safeParse(profilePart);
1849
- if (!parsed.success) return null;
1850
- return {
1851
- deviceId: Number(idPart),
1852
- profile: parsed.data
1853
- };
1854
- }
1855
- /**
1856
- * The profile slots worth consuming/recording: ASSIGNED only
1857
- * (`sourceCamStreamId != null`), DEDUPED by physical source so the same
1858
- * camera encoder is never subscribed/recorded twice, ordered high→mid→low.
1859
- * Optionally scoped to one `deviceId`. Pure; never mutates the input.
1860
- *
1861
- * Recording `main` and `mid` when both point at the same `sourceCamStreamId`
1862
- * would dial the camera's internal stream twice for no benefit — this is the
1863
- * dedup that prevents that.
1864
- */
1865
- function selectAssignedProfileSlots(slots, deviceId) {
1866
- const ordered = [...slots].filter((s) => deviceId === void 0 || s.deviceId === deviceId).toSorted((a, b) => CAM_PROFILE_ORDER.indexOf(a.profile) - CAM_PROFILE_ORDER.indexOf(b.profile));
1867
- const seenSources = /* @__PURE__ */ new Set();
1868
- const out = [];
1869
- for (const s of ordered) {
1870
- const src = s.sourceCamStreamId;
1871
- if (src === null) continue;
1872
- if (seenSources.has(src)) continue;
1873
- seenSources.add(src);
1874
- out.push(s);
1875
- }
1876
- return out;
1877
- }
1878
- /**
1879
- * Zod schema for StreamSourceEntry — the canonical stream descriptor
1880
- * exposed by ICameraDevice.getStreamSources() and consumed by the broker.
1881
- */
1882
- var StreamSourceEntrySchema = z.object({
1883
- id: z.string(),
1884
- label: z.string(),
1885
- protocol: z.enum([
1886
- "rtsp",
1887
- "rtmp",
1888
- "annexb",
1889
- "http-mjpeg",
1890
- "webrtc",
1891
- "custom"
1892
- ]),
1893
- url: z.string().optional(),
1894
- resolution: z.object({
1895
- width: z.number(),
1896
- height: z.number()
1897
- }).readonly().optional(),
1898
- fps: z.number().optional(),
1899
- bitrate: z.number().optional(),
1900
- codec: z.string().optional(),
1901
- profileHint: CamProfileSchema.optional()
1902
- });
1903
- var StreamSourceSchema = z.object({
1904
- type: z.string(),
1905
- url: z.string(),
1906
- videoCodec: z.string().optional(),
1907
- audioCodec: z.string().optional(),
1908
- metadata: z.record(z.string(), z.unknown()).readonly().optional()
1909
- });
1910
- var EncodedPacketSchema = z.object({
1911
- type: z.enum(["video", "audio"]),
1912
- data: z.instanceof(Uint8Array),
1913
- pts: z.number(),
1914
- dts: z.number(),
1915
- keyframe: z.boolean(),
1916
- codec: z.string()
1917
- });
1918
- var DecodedFrameSchema = z.object({
1919
- data: z.instanceof(Uint8Array),
1920
- width: z.number(),
1921
- height: z.number(),
1922
- format: z.enum([
1923
- "jpeg",
1924
- "rgb",
1925
- "bgr",
1926
- "yuv420",
1927
- "gray"
1928
- ]),
1929
- timestamp: z.number()
1930
- });
1931
- /**
1932
- * Wire schema for `FrameHandle` — a zero-pixel, serialisable reference to a
1933
- * frame in a shared-memory ring (Phase 5 / D9). Mirrors the `FrameHandle` TS
1934
- * interface in `interfaces/frame-handle.ts` field-for-field so a decoder cap
1935
- * method (`pullHandles`) can carry handles over tRPC / Moleculer.
1936
- *
1937
- * The `satisfies` assertion below pins this schema to the interface: if a
1938
- * field is added to / removed from `FrameHandle` without a matching schema
1939
- * edit, `z.infer<typeof FrameHandleSchema>` stops being assignable to
1940
- * `FrameHandle` and the build fails — no silent drift.
1941
- */
1942
- var FrameHandleSchema = z.object({
1943
- shmId: z.string(),
1944
- slot: z.number().int().nonnegative(),
1945
- seq: z.number().int().nonnegative(),
1946
- width: z.number().int().positive(),
1947
- height: z.number().int().positive(),
1948
- format: z.enum([
1949
- "jpeg",
1950
- "rgb",
1951
- "bgr",
1952
- "yuv420",
1953
- "gray"
1954
- ]),
1955
- pts: z.number(),
1956
- byteLength: z.number().int().nonnegative(),
1957
- nodeId: z.string(),
1958
- slotCount: z.number().int().positive()
1959
- });
1960
- /**
1961
- * Pixel format a frame-handle subscriber can request from the broker
1962
- * (Phase 5 / D9). The packed, raster subset of `FrameFormat` — the formats a
1963
- * `frameSink: 'shm'` decoder session can write into a `FrameRing` slot.
1964
- * `jpeg` is excluded: a variable-length stream is not a fixed-stride ring
1965
- * payload, and no decoded-frame consumer requests it over the shm plane.
1966
- */
1967
- var FrameHandleFormatSchema = z.enum([
1968
- "rgb",
1969
- "bgr",
1970
- "yuv420",
1971
- "gray"
1972
- ]);
1973
- /**
1974
- * Input for `stream-broker.subscribeFrames` (Phase 5 / D9). A consumer asks the
1975
- * broker for a `FrameHandle` stream of one `format`; the broker maintains one
1976
- * shared-memory ring per `(brokerId, format)` actually requested, so a `gray`
1977
- * subscriber (motion) and an `rgb` subscriber (detection) each read the format
1978
- * they asked for with no broker-side conversion.
1979
- */
1980
- var SubscribeFramesInputSchema = z.object({
1981
- brokerId: z.string(),
1982
- format: FrameHandleFormatSchema,
1983
- /**
1984
- * Optional reader-side cadence hint in frames per second. The broker does
1985
- * NOT throttle — latest-wins ring reads drop frames implicitly for a slow
1986
- * consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
1987
- * the consumer can pace its own `pullFrameHandles` polling.
1988
- */
1989
- maxFps: z.number().positive().optional(),
1990
- /** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
1991
- tag: z.string().optional()
1992
- });
1993
- /**
1994
- * Result of `stream-broker.subscribeFrames`. The consumer then polls
1995
- * `pullFrameHandles({ subscriptionId, maxCount })` and feeds each returned
1996
- * `FrameHandle` to a `FrameRingReader`.
1997
- */
1998
- var SubscribeFramesResultSchema = z.object({
1999
- /** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
2000
- subscriptionId: z.string(),
2001
- /** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
2002
- maxFps: z.number().nonnegative()
2003
- });
2004
- /**
2005
- * Wire schema for a decoded audio chunk (Phase 5 / D9). Mirrors the
2006
- * `DecodedAudioChunk` TS interface in `interfaces/stream-broker.ts`: PCM
2007
- * sample bytes plus the track parameters a consumer needs to interpret them.
2008
- *
2009
- * Audio chunks are tiny (a ~500ms AudioCodecSession window is a few KB of
2010
- * Float32 PCM), so unlike video frames they ship their bytes INLINE over
2011
- * tRPC / Moleculer — no shared-memory plane. `data` is typed `Uint8Array`
2012
- * (the wire-serialisable supertype of `Buffer`) to match `DecodedFrameSchema`
2013
- * / `EncodedPacketSchema`'s precedent; a `Buffer` is assignable to it.
2014
- */
2015
- var DecodedAudioChunkSchema = z.object({
2016
- data: z.instanceof(Uint8Array),
2017
- sampleRate: z.number().int().positive(),
2018
- channels: z.number().int().positive(),
2019
- timestamp: z.number()
2020
- });
2021
- /**
2022
- * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). The
2023
- * handle-free, ship-bytes-inline analogue of `subscribeFrames`: a consumer
2024
- * asks the broker for the decoded audio-chunk stream of one broker; the
2025
- * broker keeps a per-subscription bounded FIFO queue the consumer drains via
2026
- * `pullAudioChunks`.
2027
- */
2028
- var SubscribeAudioChunksInputSchema = z.object({
2029
- brokerId: z.string(),
2030
- /** Short caller-identity tag (`audio-analyzer`, …) for `listClients`. */
2031
- tag: z.string().optional()
2032
- });
2033
- /** Result of `stream-broker.subscribeAudioChunks`. */
2034
- var SubscribeAudioChunksResultSchema = z.object({
2035
- /** Opaque id passed to `pullAudioChunks` / `unsubscribeAudioChunks`. */
2036
- subscriptionId: z.string() });
2037
- var BrokerStatusSchema = z.enum([
2038
- "idle",
2039
- "connecting",
2040
- "streaming",
2041
- "error",
2042
- "stopped"
2043
- ]);
2044
- var BrokerStatsSchema = z.object({
2045
- status: BrokerStatusSchema,
2046
- inputFps: z.number(),
2047
- decodeFps: z.number(),
2048
- encodedSubscribers: z.number(),
2049
- decodedSubscribers: z.number(),
2050
- uptimeMs: z.number(),
2051
- bitrateKbps: z.number(),
2052
- idrIntervalMs: z.number(),
2053
- codec: z.string().optional(),
2054
- totalBytes: z.number(),
2055
- packetCount: z.number(),
2056
- rtspClients: z.number(),
2057
- pipeClients: z.number(),
2058
- preBufferSec: z.number(),
2059
- preBufferMs: z.number(),
2060
- preBufferPackets: z.number(),
2061
- /**
2062
- * Moleculer node id of the decoder provider currently servicing this
2063
- * stream's decoded subscribers. `null` until the deferred decoder is
2064
- * created (no decoded clients yet, or codec not detected). Surfaces the
2065
- * runtime decoder placement so the UI can show "Decoder: <agent>" without
2066
- * a separate cap call per broker.
2067
- */
2068
- decoderNodeId: z.string().nullable(),
2069
- /**
2070
- * Detected audio track parameters from the RTSP DESCRIBE / SDP. `null`
2071
- * when the stream has no audio track or the broker is in cold start.
2072
- * `supported = false` means the codec was detected but the local
2073
- * decoder pipeline cannot produce PCM chunks (e.g. AAC without the
2074
- * AAC pipeline wired). Surfaced in the UI device-overview so operators
2075
- * can pre-pick the audio-analysis model that matches the codec.
2076
- */
2077
- audio: z.object({
2078
- codec: z.string(),
2079
- sampleRate: z.number(),
2080
- channels: z.number(),
2081
- supported: z.boolean()
2082
- }).nullable().optional()
2083
- });
2084
- /**
2085
- * Exporter-facing "profile restream" entry. Returned by
2086
- * `cameraStreams.getProfileRtspEntries` — one per ASSIGNED profile slot
2087
- * (high/mid/low). `brokerId` is the PROFILE-keyed broker id
2088
- * (`${deviceId}/${profile}`, e.g. `15/high`); its `url` is a broker
2089
- * RTSP restream that aliases the profile's assigned source broker, so
2090
- * every consumer dialling a profile converges on the same single pull.
2091
- * `codec`/`resolution` describe the assigned SOURCE camStream (for the
2092
- * `auto` resolution-closest pick). Distinct from `RtspRestreamEntry`
2093
- * (raw per-camStream, live-view only).
2094
- */
2095
- var ProfileRtspEntrySchema = z.object({
2096
- profile: CamProfileSchema,
2097
- /** Profile-keyed broker id, format `${deviceId}/${profile}` (e.g. `"15/high"`). */
2098
- brokerId: z.string(),
2099
- url: z.string(),
2100
- mutedUrl: z.string(),
2101
- enabled: z.boolean(),
2102
- codec: z.string().optional(),
2103
- resolution: CamStreamResolutionSchema.optional()
2104
- });
349
+ stdOffsetMinutes: -180,
350
+ dst: null
351
+ },
352
+ {
353
+ id: "Asia/Dubai",
354
+ label: "Asia/Dubai (UTC+4)",
355
+ region: "Asia",
356
+ stdOffsetMinutes: 240,
357
+ dst: null
358
+ },
359
+ {
360
+ id: "Asia/Kolkata",
361
+ label: "Asia/Kolkata (UTC+5:30)",
362
+ region: "Asia",
363
+ stdOffsetMinutes: 330,
364
+ dst: null
365
+ },
366
+ {
367
+ id: "Asia/Singapore",
368
+ label: "Asia/Singapore (UTC+8)",
369
+ region: "Asia",
370
+ stdOffsetMinutes: 480,
371
+ dst: null
372
+ },
373
+ {
374
+ id: "Asia/Shanghai",
375
+ label: "Asia/Shanghai (UTC+8)",
376
+ region: "Asia",
377
+ stdOffsetMinutes: 480,
378
+ dst: null
379
+ },
380
+ {
381
+ id: "Asia/Tokyo",
382
+ label: "Asia/Tokyo (UTC+9)",
383
+ region: "Asia",
384
+ stdOffsetMinutes: 540,
385
+ dst: null
386
+ },
387
+ {
388
+ id: "Australia/Sydney",
389
+ label: "Australia/Sydney (UTC+10 / AEDT)",
390
+ region: "Oceania",
391
+ stdOffsetMinutes: 600,
392
+ dst: {
393
+ offsetHours: 1,
394
+ startMonth: 10,
395
+ startWeekIndex: 1,
396
+ startWeekday: "Sunday",
397
+ startHour: 2,
398
+ endMonth: 4,
399
+ endWeekIndex: 1,
400
+ endWeekday: "Sunday",
401
+ endHour: 3
402
+ }
403
+ }
404
+ ];
405
+ /** Resolve an IANA id to its `Timezone`, or `undefined` if unknown. */
406
+ function findTimezone(id) {
407
+ return TIMEZONES.find((tz) => tz.id === id);
408
+ }
2105
409
  //#endregion
2106
410
  //#region src/interfaces/recording-config.ts
2107
411
  /**
@@ -2294,375 +598,6 @@ function migrateConfigToBands(config) {
2294
598
  return schedules.map((schedule) => bandFromSchedule(schedule, bandMode, config));
2295
599
  }
2296
600
  //#endregion
2297
- //#region src/readiness/readiness-registry.ts
2298
- var ReadinessTimeoutError = class extends Error {
2299
- capName;
2300
- scope;
2301
- waitedMs;
2302
- constructor(capName, scope, waitedMs) {
2303
- super(`Timed out waiting for ${capName} (${scopeKey(scope)}) to become ready after ${waitedMs}ms`);
2304
- this.name = "ReadinessTimeoutError";
2305
- this.capName = capName;
2306
- this.scope = scope;
2307
- this.waitedMs = waitedMs;
2308
- }
2309
- };
2310
- /**
2311
- * Build a canonical string key for a `(capName, scope)` pair. Used as
2312
- * the snapshot map key and for log/debug output.
2313
- */
2314
- function readinessKey(capName, scope) {
2315
- return `${capName}|${scopeKey(scope)}`;
2316
- }
2317
- function scopeKey(scope) {
2318
- switch (scope.type) {
2319
- case "global": return "global";
2320
- case "node": return `node:${scope.nodeId}`;
2321
- case "device": return `device:${scope.deviceId}`;
2322
- }
2323
- }
2324
- function scopesEqual(a, b) {
2325
- if (a.type !== b.type) return false;
2326
- if (a.type === "global" || b.type === "global") return true;
2327
- if (a.type === "node" && b.type === "node") return a.nodeId === b.nodeId;
2328
- if (a.type === "device" && b.type === "device") return a.deviceId === b.deviceId;
2329
- return false;
2330
- }
2331
- var ReadinessRegistry = class {
2332
- bus;
2333
- sourceNodeId;
2334
- logger;
2335
- now;
2336
- generation;
2337
- snapshot = /* @__PURE__ */ new Map();
2338
- subscriptions = /* @__PURE__ */ new Set();
2339
- unsubscribeBus;
2340
- unsubscribeAgentOffline;
2341
- constructor(options) {
2342
- this.bus = options.eventBus;
2343
- this.sourceNodeId = options.sourceNodeId;
2344
- this.logger = options.logger;
2345
- this.now = options.now ?? (() => Date.now());
2346
- this.generation = options.generation ?? randomGeneration();
2347
- this.unsubscribeBus = this.bus.subscribe({ category: "system.ready-state" }, (event) => this.ingest(event.data));
2348
- this.unsubscribeAgentOffline = this.bus.subscribe({ category: "agent.offline" }, (event) => this.synthesizeDownForNode(event.data.agentId));
2349
- if (typeof this.bus.getRecent === "function") try {
2350
- const recent = this.bus.getRecent({ category: "system.ready-state" });
2351
- for (const event of recent) this.ingest(event.data);
2352
- } catch {}
2353
- }
2354
- /** Release the event-bus subscription. Idempotent. */
2355
- close() {
2356
- this.unsubscribeBus();
2357
- this.unsubscribeAgentOffline();
2358
- this.subscriptions.clear();
2359
- }
2360
- /** Current snapshot for a `(capName, scope)` pair, or `null` if never seen. */
2361
- get(capName, scope) {
2362
- return this.snapshot.get(readinessKey(capName, scope)) ?? null;
2363
- }
2364
- /**
2365
- * Serializable snapshot for cross-process transport. Returns an array
2366
- * of `ReadinessRecord` plain objects — safe for MsgPack / JSON
2367
- * transport. Used by the hub's `$readiness.getSnapshot` Moleculer
2368
- * action; consumers hydrate their local registry from the result.
2369
- */
2370
- getSnapshotForTransport() {
2371
- return Array.from(this.snapshot.values());
2372
- }
2373
- /**
2374
- * Hydrate the snapshot from an authoritative source. Entries already
2375
- * present locally are skipped — live deltas (received via the event
2376
- * bus subscription) always take precedence over the snapshot. For
2377
- * each newly hydrated entry, a one-shot transition is dispatched to
2378
- * matching subscriptions so pending `awaitReady` callers unblock
2379
- * without having to wait for a fresh event.
2380
- *
2381
- * Local `epoch` is reset to 1 per entry — consumer-side epoch is
2382
- * derived from observed generation transitions, so this mirrors the
2383
- * value that would have been assigned had the consumer observed the
2384
- * first `ready` event directly.
2385
- */
2386
- hydrate(records) {
2387
- const now = this.now();
2388
- for (const record of records) {
2389
- const key = readinessKey(record.capName, record.scope);
2390
- if (this.snapshot.has(key)) continue;
2391
- const hydrated = {
2392
- capName: record.capName,
2393
- scope: record.scope,
2394
- state: record.state,
2395
- generation: record.generation,
2396
- epoch: 1,
2397
- lastChange: now,
2398
- sourceNodeId: record.sourceNodeId
2399
- };
2400
- this.snapshot.set(key, hydrated);
2401
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated, gen=${record.generation.slice(0, 6)})`);
2402
- const transition = {
2403
- capName: record.capName,
2404
- scope: record.scope,
2405
- state: record.state,
2406
- epoch: 1,
2407
- generation: record.generation,
2408
- sourceNodeId: "hydrated",
2409
- ts: now,
2410
- durationInPrevState: 0
2411
- };
2412
- for (const sub of this.subscriptions) {
2413
- if (sub.capName !== record.capName) continue;
2414
- if (!scopesEqual(sub.scope, record.scope)) continue;
2415
- try {
2416
- sub.handler(transition);
2417
- } catch (err) {
2418
- this.logger?.warn(`readiness hydrate handler threw for ${record.capName}: ${err.message ?? String(err)}`);
2419
- }
2420
- }
2421
- }
2422
- }
2423
- /** Shallow copy of the full snapshot — mainly for diagnostics/tests. */
2424
- getAll() {
2425
- return new Map(this.snapshot);
2426
- }
2427
- /**
2428
- * Emit a `ready` transition for a locally-owned capability. The
2429
- * payload carries this registry's `generation` so remote registries
2430
- * can derive their own `epoch`.
2431
- */
2432
- emitReady(capName, scope) {
2433
- this.emitTransition(capName, scope, "ready");
2434
- }
2435
- emitStarting(capName, scope) {
2436
- this.emitTransition(capName, scope, "starting");
2437
- }
2438
- emitDown(capName, scope) {
2439
- this.emitTransition(capName, scope, "down");
2440
- }
2441
- /**
2442
- * One-shot: resolve once the cap is `ready`. Reads the snapshot
2443
- * first — if already ready, resolves synchronously on the next tick.
2444
- *
2445
- * Default behaviour is **wait indefinitely** (timeoutMs = `Infinity`):
2446
- * the orchestrator and other callers never want to attach a camera
2447
- * with empty steps just because a cap took longer than 30s to come
2448
- * up. Pass an explicit finite `timeoutMs` to bound the wait, or an
2449
- * `AbortSignal` to cancel externally. `Infinity` is honoured natively
2450
- * — no `setTimeout` is registered (Node's setTimeout silently clamps
2451
- * values above ~24.8d, so we must skip the timer entirely).
2452
- */
2453
- awaitReady(capName, scope, opts = {}) {
2454
- const timeoutMs = opts.timeoutMs ?? Number.POSITIVE_INFINITY;
2455
- const start = this.now();
2456
- if (this.get(capName, scope)?.state === "ready") return Promise.resolve();
2457
- if (opts.signal?.aborted) return Promise.reject(opts.signal.reason ?? /* @__PURE__ */ new Error("aborted"));
2458
- return new Promise((resolve, reject) => {
2459
- let settled = false;
2460
- const unsubscribe = this.onReadyState(capName, scope, (t) => {
2461
- if (t.state !== "ready") return;
2462
- if (settled) return;
2463
- settled = true;
2464
- cleanup();
2465
- resolve();
2466
- });
2467
- const timer = !Number.isFinite(timeoutMs) ? null : setTimeout(() => {
2468
- if (settled) return;
2469
- settled = true;
2470
- cleanup();
2471
- reject(new ReadinessTimeoutError(capName, scope, this.now() - start));
2472
- }, timeoutMs);
2473
- const pendingLogTimer = setInterval(() => {
2474
- if (settled) return;
2475
- this.logger?.warn(`readiness: still awaiting ${capName} (${scopeKey(scope)}) after ${this.now() - start}ms`);
2476
- }, 3e4);
2477
- if (typeof pendingLogTimer.unref === "function") pendingLogTimer.unref();
2478
- const onAbort = () => {
2479
- if (settled) return;
2480
- settled = true;
2481
- cleanup();
2482
- reject(opts.signal?.reason ?? /* @__PURE__ */ new Error("aborted"));
2483
- };
2484
- opts.signal?.addEventListener("abort", onAbort, { once: true });
2485
- function cleanup() {
2486
- unsubscribe();
2487
- if (timer !== null) clearTimeout(timer);
2488
- clearInterval(pendingLogTimer);
2489
- opts.signal?.removeEventListener("abort", onAbort);
2490
- }
2491
- });
2492
- }
2493
- /**
2494
- * Observable: every transition (starting/ready/down) dispatches to
2495
- * `handler`. On subscription, if the snapshot has a current state,
2496
- * the handler fires ONCE asynchronously with that state as the
2497
- * initial transition (duration = 0) so late subscribers can pick up
2498
- * current state without racing the next transition.
2499
- *
2500
- * Returns an unsubscribe function.
2501
- */
2502
- onReadyState(capName, scope, handler) {
2503
- const sub = {
2504
- capName,
2505
- scope,
2506
- handler
2507
- };
2508
- this.subscriptions.add(sub);
2509
- const current = this.get(capName, scope);
2510
- if (current !== null) queueMicrotask(() => {
2511
- if (!this.subscriptions.has(sub)) return;
2512
- handler({
2513
- capName,
2514
- scope,
2515
- state: current.state,
2516
- epoch: current.epoch,
2517
- generation: current.generation,
2518
- sourceNodeId: this.sourceNodeId,
2519
- ts: current.lastChange,
2520
- durationInPrevState: 0
2521
- });
2522
- });
2523
- return () => {
2524
- this.subscriptions.delete(sub);
2525
- };
2526
- }
2527
- emitTransition(capName, scope, state) {
2528
- const ts = this.now();
2529
- this.bus.emit(createEvent("system.ready-state", {
2530
- type: "capability",
2531
- id: capName,
2532
- nodeId: this.sourceNodeId
2533
- }, {
2534
- capName,
2535
- scope,
2536
- state,
2537
- generation: this.generation,
2538
- sourceNodeId: this.sourceNodeId,
2539
- ts
2540
- }));
2541
- }
2542
- /**
2543
- * Update snapshot + dispatch to subscribers. Idempotent: same
2544
- * `generation + state` replay is a no-op for both snapshot and
2545
- * subscribers.
2546
- *
2547
- * Defensive against malformed payloads (legal reason: a mock bus's
2548
- * `getRecent` may ignore the category filter and replay unrelated
2549
- * events when we hydrate at construction time).
2550
- */
2551
- ingest(payload) {
2552
- if (typeof payload?.capName !== "string") return;
2553
- if (payload.state !== "ready" && payload.state !== "starting" && payload.state !== "down") return;
2554
- if (typeof payload.generation !== "string") return;
2555
- if (payload.scope?.type !== "global" && payload.scope?.type !== "node" && payload.scope?.type !== "device") return;
2556
- const key = readinessKey(payload.capName, payload.scope);
2557
- const prev = this.snapshot.get(key) ?? null;
2558
- const now = this.now();
2559
- let epoch;
2560
- if (prev === null) epoch = payload.state === "ready" ? 1 : 0;
2561
- else if (prev.generation !== payload.generation && payload.state === "ready") epoch = prev.epoch + 1;
2562
- else epoch = prev.epoch;
2563
- if (prev !== null && prev.generation === payload.generation && prev.state === payload.state) return;
2564
- const durationInPrevState = prev === null ? 0 : Math.max(0, now - prev.lastChange);
2565
- const next = {
2566
- capName: payload.capName,
2567
- scope: payload.scope,
2568
- state: payload.state,
2569
- generation: payload.generation,
2570
- epoch,
2571
- lastChange: now,
2572
- sourceNodeId: payload.sourceNodeId
2573
- };
2574
- this.snapshot.set(key, next);
2575
- const transition = {
2576
- capName: payload.capName,
2577
- scope: payload.scope,
2578
- state: payload.state,
2579
- epoch,
2580
- generation: payload.generation,
2581
- sourceNodeId: payload.sourceNodeId,
2582
- ts: payload.ts,
2583
- durationInPrevState
2584
- };
2585
- if (this.logger) this.logger.debug(`readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`);
2586
- for (const sub of this.subscriptions) {
2587
- if (sub.capName !== payload.capName) continue;
2588
- if (!scopesEqual(sub.scope, payload.scope)) continue;
2589
- try {
2590
- sub.handler(transition);
2591
- } catch (err) {
2592
- this.logger?.warn(`readiness handler threw for ${payload.capName}: ${err.message ?? String(err)}`);
2593
- }
2594
- }
2595
- }
2596
- /**
2597
- * `agent.offline` demux. When an agent disconnects ungracefully,
2598
- * its BaseAddon's shutdown hook doesn't fire — consumers would
2599
- * otherwise keep waiting on a stale `ready`. Walk the snapshot and
2600
- * synthesize a `down` transition for every node-scoped record bound
2601
- * to the disconnected `nodeId`, skipping records already `down`.
2602
- *
2603
- * Synthesis is LOCAL: we don't re-emit on the bus. Every registry
2604
- * (one per process) receives the same `agent.offline` and reacts
2605
- * independently, which means no duplicated downs and no central
2606
- * supervisor required.
2607
- */
2608
- synthesizeDownForNode(offlineNodeId) {
2609
- const now = this.now();
2610
- for (const [key, record] of this.snapshot) {
2611
- if (record.state === "down") continue;
2612
- if (!(record.scope.type === "node" && record.scope.nodeId === offlineNodeId || record.scope.type === "device" && record.sourceNodeId === offlineNodeId)) continue;
2613
- const durationInPrevState = Math.max(0, now - record.lastChange);
2614
- const next = {
2615
- ...record,
2616
- state: "down",
2617
- lastChange: now
2618
- };
2619
- this.snapshot.set(key, next);
2620
- const transition = {
2621
- capName: record.capName,
2622
- scope: record.scope,
2623
- state: "down",
2624
- epoch: record.epoch,
2625
- generation: record.generation,
2626
- sourceNodeId: this.sourceNodeId,
2627
- ts: now,
2628
- durationInPrevState
2629
- };
2630
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`);
2631
- for (const sub of this.subscriptions) {
2632
- if (sub.capName !== record.capName) continue;
2633
- if (!scopesEqual(sub.scope, record.scope)) continue;
2634
- try {
2635
- sub.handler(transition);
2636
- } catch (err) {
2637
- this.logger?.warn(`readiness handler threw during agent.offline demux for ${record.capName}: ${err.message ?? String(err)}`);
2638
- }
2639
- }
2640
- }
2641
- }
2642
- };
2643
- function randomGeneration() {
2644
- if (typeof crypto !== "undefined" && crypto.randomUUID) return crypto.randomUUID();
2645
- return Math.random().toString(36).slice(2, 14);
2646
- }
2647
- /**
2648
- * Given a list of `(capName, scope)` pairs that a just-disconnected
2649
- * node owned, emit a `down` transition for each. Callers (kernel
2650
- * supervisor, hub `$node.disconnected` handler) wire this up once they
2651
- * know which caps the node held.
2652
- */
2653
- function emitDownForOwnedCaps(registry, owned) {
2654
- for (const { capName, scope } of owned) registry.emitDown(capName, scope);
2655
- }
2656
- //#endregion
2657
- //#region src/interfaces/addon-data-plane.ts
2658
- /**
2659
- * Per-listener shared-secret header the hub injects on every reverse-proxied
2660
- * data-plane request and the addon's framework wrapper validates — so only the
2661
- * hub can reach the addon's `127.0.0.1` listener. Defined here (shared by the
2662
- * core proxy and the kernel listener) to keep both off a kernel↔core import.
2663
- */
2664
- var DATAPLANE_SECRET_HEADER = "x-camstack-dataplane-secret";
2665
- //#endregion
2666
601
  //#region src/interfaces/storage-location.ts
2667
602
  /**
2668
603
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
@@ -3200,66 +1135,6 @@ var RUNTIME_DEFAULTS = {
3200
1135
  "auth.tokenExpiry": "24h"
3201
1136
  };
3202
1137
  //#endregion
3203
- //#region src/utils/json-safe.ts
3204
- /**
3205
- * Type-safe JSON parsing helpers.
3206
- *
3207
- * `JSON.parse` is typed as `any` in lib.es5.d.ts, which triggers
3208
- * `no-unsafe-*` ESLint rules and destroys downstream inference. These
3209
- * wrappers return `unknown` — callers narrow structurally via type
3210
- * guards, `typeof` checks, or helpers like `asRecord`/`asString`.
3211
- */
3212
- /**
3213
- * Parse JSON and return it as `unknown` — the only entry point for untrusted JSON.
3214
- *
3215
- * The optional generic overload `parseJsonUnknown<T>(text)` returns `T` for
3216
- * call sites that know the shape at parse time (e.g. MQTT payloads with a
3217
- * known protocol schema). This is a **type-level bridge only** — no runtime
3218
- * validation is performed. Callers that need runtime validation should parse
3219
- * as `unknown` and narrow via Zod or structural guards.
3220
- */
3221
- function parseJsonUnknown(text) {
3222
- return JSON.parse(text);
3223
- }
3224
- /** Narrow an unknown value to a plain `Record<string, unknown>` or return null. */
3225
- function asJsonObject(value) {
3226
- if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
3227
- return { ...value };
3228
- }
3229
- /** Narrow an unknown value to a `readonly unknown[]` or return an empty array. */
3230
- function asJsonArray(value) {
3231
- return Array.isArray(value) ? value : [];
3232
- }
3233
- /** Safe string extraction from an unknown record field. */
3234
- function asString(value, fallback = "") {
3235
- return typeof value === "string" ? value : fallback;
3236
- }
3237
- /** Safe number extraction from an unknown record field. */
3238
- function asNumber(value, fallback = 0) {
3239
- return typeof value === "number" ? value : fallback;
3240
- }
3241
- /** Safe boolean extraction from an unknown record field. */
3242
- function asBoolean(value, fallback = false) {
3243
- return typeof value === "boolean" ? value : fallback;
3244
- }
3245
- /** Parse JSON + narrow to object in one step. */
3246
- function parseJsonObject(text) {
3247
- try {
3248
- return asJsonObject(parseJsonUnknown(text));
3249
- } catch {
3250
- return null;
3251
- }
3252
- }
3253
- /** Parse JSON + narrow to array in one step. */
3254
- function parseJsonArray(text) {
3255
- try {
3256
- const parsed = parseJsonUnknown(text);
3257
- return Array.isArray(parsed) ? parsed : null;
3258
- } catch {
3259
- return null;
3260
- }
3261
- }
3262
- //#endregion
3263
1138
  //#region src/utils/hf-url.ts
3264
1139
  function hfModelUrl(repo, path) {
3265
1140
  return `https://huggingface.co/${repo}/resolve/main/${path}`;
@@ -3506,18 +1381,6 @@ function requiresPython(runtime) {
3506
1381
  return runtime === "coreml" || runtime === "pytorch" || runtime === "openvino";
3507
1382
  }
3508
1383
  //#endregion
3509
- //#region src/utils/err-msg.ts
3510
- /**
3511
- import { errMsg } from '@camstack/types'
3512
- * Extract a human-readable message from an unknown error value.
3513
- * Replaces the ubiquitous `errMsg(err)` pattern.
3514
- */
3515
- function errMsg(err) {
3516
- if (err instanceof Error) return err.message;
3517
- if (typeof err === "string") return err;
3518
- return String(err);
3519
- }
3520
- //#endregion
3521
1384
  //#region src/utils/run-inference-step.ts
3522
1385
  /**
3523
1386
  * Execute an inference function with timing and error capture.
@@ -4332,290 +2195,6 @@ var DEVICE_TYPE_INFO = { ["camera"]: {
4332
2195
  icon: "camera"
4333
2196
  } };
4334
2197
  //#endregion
4335
- //#region src/device/device-type.ts
4336
- var DeviceType = /* @__PURE__ */ function(DeviceType) {
4337
- DeviceType["Camera"] = "camera";
4338
- DeviceType["Hub"] = "hub";
4339
- DeviceType["Light"] = "light";
4340
- DeviceType["Siren"] = "siren";
4341
- DeviceType["Switch"] = "switch";
4342
- DeviceType["Sensor"] = "sensor";
4343
- DeviceType["Thermostat"] = "thermostat";
4344
- DeviceType["Button"] = "button";
4345
- /** Generic stateless event emitter — carries a device's EXACT declared
4346
- * event vocabulary verbatim (no normalization). Installed with the
4347
- * `event-emitter` cap. Sources: HA `event.*` entities (structured) and
4348
- * HA bus events (e.g. `zha_event`, generic). */
4349
- DeviceType["EventEmitter"] = "event-emitter";
4350
- /** Firmware/software update entity — current vs available version,
4351
- * updatable flag, update state, and an install action. Installed with
4352
- * the `update` cap. Sources: Homematic firmware-update channels (and
4353
- * reusable by other providers, e.g. HA `update.*` entities). */
4354
- DeviceType["Update"] = "update";
4355
- DeviceType["Generic"] = "generic";
4356
- /** Generic notification delivery target (HA `notify.<service>`, future
4357
- * Telegram / Discord / ntfy / SMTP, …). One device per delivery
4358
- * endpoint; the `notifier` cap defines the send surface. */
4359
- DeviceType["Notifier"] = "notifier";
4360
- /** Pre-recorded action sequence with optional parameters
4361
- * (HA `script.*`). Runnable via `script-runner` cap. */
4362
- DeviceType["Script"] = "script";
4363
- /** Automation rule (HA `automation.*`) — enable/disable + manual
4364
- * trigger surface exposed via `automation-control` cap. */
4365
- DeviceType["Automation"] = "automation";
4366
- /** Door / smart lock device (HA `lock.*`). `lock-control` cap. */
4367
- DeviceType["Lock"] = "lock";
4368
- /** Window covering, blinds, garage door, valve, etc. (HA `cover.*`,
4369
- * `valve.*`). `cover` cap with sub-roles for variant. */
4370
- DeviceType["Cover"] = "cover";
4371
- /** Pipe / water / gas valve with open/close/stop and optional
4372
- * position (HA `valve.*`). `valve` cap — a cover-sibling actuator
4373
- * modelled on the same open/closed lifecycle. */
4374
- DeviceType["Valve"] = "valve";
4375
- /** Humidifier / dehumidifier with on/off + target humidity + mode
4376
- * (HA `humidifier.*`). `humidifier` cap — a climate-family actuator
4377
- * modelled on the same target / mode lifecycle. */
4378
- DeviceType["Humidifier"] = "humidifier";
4379
- /** Water heater / boiler with target temperature + operation mode +
4380
- * away mode (HA `water_heater.*`). `water-heater` cap — a
4381
- * climate-family actuator. */
4382
- DeviceType["WaterHeater"] = "water-heater";
4383
- /** Ceiling / standing / exhaust fan (HA `fan.*`). `fan-control` cap. */
4384
- DeviceType["Fan"] = "fan";
4385
- /** Audio / video playback endpoint (HA `media_player.*`). Disjoint from
4386
- * the camera surface — those use `Camera`. `media-player` cap. */
4387
- DeviceType["MediaPlayer"] = "media-player";
4388
- /** Security panel / alarm system (HA `alarm_control_panel.*`).
4389
- * `alarm-panel` cap. */
4390
- DeviceType["AlarmPanel"] = "alarm-panel";
4391
- /** Generic user-settable input (HA `number` / `input_number` / `select`
4392
- * / `input_select` / `text` / `input_text` / `input_datetime`).
4393
- * Sub-type via `DeviceRole`: NumericControl / SelectControl /
4394
- * TextControl / DateTimeControl. */
4395
- DeviceType["Control"] = "control";
4396
- /** Person / device-tracker presence (HA `person.*`, `device_tracker.*`).
4397
- * `presence` cap. */
4398
- DeviceType["Presence"] = "presence";
4399
- /** Weather provider (HA `weather.*`). Tier-3, low MVP priority.
4400
- * `weather` cap. */
4401
- DeviceType["Weather"] = "weather";
4402
- /** Robot vacuum (HA `vacuum.*`). Tier-3. `vacuum-control` cap. */
4403
- DeviceType["Vacuum"] = "vacuum";
4404
- /** Robotic lawn mower (HA `lawn_mower.*`). Tier-3.
4405
- * `lawn-mower-control` cap. */
4406
- DeviceType["LawnMower"] = "lawn-mower";
4407
- /** Physical HA device group — parent container for entity-children
4408
- * adopted from a single HA device entry. Not renderable as a
4409
- * standalone device; exists only to anchor child entities. */
4410
- DeviceType["Container"] = "container";
4411
- /** Single still-image entity (HA `image.*`). Read-only display of an
4412
- * `entity_picture` signed URL the browser loads directly. `image` cap. */
4413
- DeviceType["Image"] = "image";
4414
- return DeviceType;
4415
- }({});
4416
- var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
4417
- DeviceFeature["BatteryOperated"] = "battery-operated";
4418
- DeviceFeature["Rebootable"] = "rebootable";
4419
- /**
4420
- * Device supports an on-demand re-sync of its derived spec with its
4421
- * upstream source — drives the generic Re-sync button. The owning
4422
- * provider implements the action via the `device-adoption.resync` cap.
4423
- */
4424
- DeviceFeature["Resyncable"] = "resyncable";
4425
- DeviceFeature["NativeSnapshot"] = "native-snapshot";
4426
- DeviceFeature["DoorbellButton"] = "doorbell-button";
4427
- DeviceFeature["TwoWayAudio"] = "two-way-audio";
4428
- DeviceFeature["PanTiltZoom"] = "pan-tilt-zoom";
4429
- /**
4430
- * Camera supports the on-firmware autotrack subsystem (subject-
4431
- * following). Distinct from `PanTiltZoom` because not every PTZ
4432
- * camera ships autotrack — the admin UI uses this flag to gate
4433
- * the autotrack toggle / settings card without re-deriving from
4434
- * the cap registry. Mirrors `ptz-autotrack` cap registration:
4435
- * driver sets this feature when probe confirms the firmware
4436
- * surface, and registers the cap in the same code path.
4437
- */
4438
- DeviceFeature["PtzAutotrack"] = "ptz-autotrack";
4439
- /**
4440
- * Accessory exposes a "trigger on motion" toggle — the parent camera's
4441
- * motion detection automatically activates this device. Mirrors
4442
- * `motion-trigger` cap registration: drivers set this feature in the
4443
- * same code path that calls `ctx.registerNativeCap(motionTriggerCapability, ...)`.
4444
- *
4445
- * Used by admin UI (gate the in-hero `MotionTriggerToggle` against a
4446
- * fast scalar without binding fetch), notifier rules, and `listAll`
4447
- * filters that want "all devices with on-motion behaviour".
4448
- */
4449
- DeviceFeature["MotionTrigger"] = "motion-trigger";
4450
- /** Light supports rgb-triplet color via `color` cap. */
4451
- DeviceFeature["LightColorRgb"] = "light-color-rgb";
4452
- /** Light supports HSV color via `color` cap. */
4453
- DeviceFeature["LightColorHsv"] = "light-color-hsv";
4454
- /** Light supports color-temperature (mired) via `color` cap. */
4455
- DeviceFeature["LightColorMired"] = "light-color-mired";
4456
- /** Thermostat supports a `heat_cool` dual setpoint (targetLow +
4457
- * targetHigh). Gates the range slider UI. */
4458
- DeviceFeature["ClimateDualSetpoint"] = "climate-dual-setpoint";
4459
- /** Thermostat exposes target humidity and/or current humidity
4460
- * readings. Gates the humidity controls. */
4461
- DeviceFeature["ClimateHumidity"] = "climate-humidity";
4462
- /** Thermostat exposes a fan-mode selector. */
4463
- DeviceFeature["ClimateFanMode"] = "climate-fan-mode";
4464
- /** Thermostat exposes preset modes (eco / away / sleep / vendor). */
4465
- DeviceFeature["ClimatePreset"] = "climate-preset";
4466
- /** Cover exposes intermediate position control (0..100). Gates the
4467
- * position slider UI. */
4468
- DeviceFeature["CoverPositionable"] = "cover-positionable";
4469
- /** Cover exposes slat-tilt control. Gates the tilt slider UI. */
4470
- DeviceFeature["CoverTilt"] = "cover-tilt";
4471
- /** Valve exposes intermediate position control (0..100). Gates the
4472
- * position slider / drag surface UI. */
4473
- DeviceFeature["ValvePositionable"] = "valve-positionable";
4474
- /** Fan exposes a speed-percentage setter. Gates the speed slider UI. */
4475
- DeviceFeature["FanSpeed"] = "fan-speed";
4476
- /** Fan exposes a preset mode selector. */
4477
- DeviceFeature["FanPreset"] = "fan-preset";
4478
- /** Fan exposes blade direction (forward/reverse) — typical of
4479
- * ceiling fans. */
4480
- DeviceFeature["FanDirection"] = "fan-direction";
4481
- /** Fan exposes an oscillation toggle. */
4482
- DeviceFeature["FanOscillating"] = "fan-oscillating";
4483
- /** Lock requires a PIN code on lock/unlock. Gates the code-entry
4484
- * field on the UI lock-controls panel. */
4485
- DeviceFeature["LockPinRequired"] = "lock-pin-required";
4486
- /** Lock supports a latch-release ("open door") action distinct from
4487
- * unlock. Mirrors HA `LockEntityFeature.OPEN` (bit 1) in
4488
- * `supported_features`. Gates the Open Door button in the UI. */
4489
- DeviceFeature["LockOpen"] = "lock-open";
4490
- /** Media player exposes a seek-to-position surface. */
4491
- DeviceFeature["MediaPlayerSeek"] = "media-player-seek";
4492
- /** Media player exposes a volume-level setter. */
4493
- DeviceFeature["MediaPlayerVolume"] = "media-player-volume";
4494
- /** Media player exposes a mute toggle distinct from volume=0. */
4495
- DeviceFeature["MediaPlayerMute"] = "media-player-mute";
4496
- /** Media player exposes a shuffle toggle. */
4497
- DeviceFeature["MediaPlayerShuffle"] = "media-player-shuffle";
4498
- /** Media player exposes a repeat mode (off / all / one). */
4499
- DeviceFeature["MediaPlayerRepeat"] = "media-player-repeat";
4500
- /** Media player exposes a source / input selector. */
4501
- DeviceFeature["MediaPlayerSelectSource"] = "media-player-select-source";
4502
- /** Media player exposes a play-arbitrary-media surface (URL / id). */
4503
- DeviceFeature["MediaPlayerPlayMedia"] = "media-player-play-media";
4504
- /** Media player exposes next-track. */
4505
- DeviceFeature["MediaPlayerNext"] = "media-player-next";
4506
- /** Media player exposes previous-track. */
4507
- DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
4508
- /** Media player exposes stop distinct from pause. */
4509
- DeviceFeature["MediaPlayerStop"] = "media-player-stop";
4510
- /** Alarm panel requires a PIN code on arm/disarm. */
4511
- DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
4512
- /** Presence device carries GPS coordinates (lat/lng/accuracy) in
4513
- * addition to a textual location. */
4514
- DeviceFeature["PresenceGps"] = "presence-gps";
4515
- /** Notifier accepts an inline / URL image attachment. */
4516
- DeviceFeature["NotifierImage"] = "notifier-image";
4517
- /** Notifier accepts a priority hint (high/normal/low). */
4518
- DeviceFeature["NotifierPriority"] = "notifier-priority";
4519
- /** Notifier accepts a free-form `data` payload for platform-specific
4520
- * fields. */
4521
- DeviceFeature["NotifierData"] = "notifier-data";
4522
- /** Notifier supports interactive action buttons / callbacks. */
4523
- DeviceFeature["NotifierActions"] = "notifier-actions";
4524
- /** Notifier supports per-call recipient targeting (multi-user). */
4525
- DeviceFeature["NotifierRecipients"] = "notifier-recipients";
4526
- /** Script runner accepts a variables map on each run invocation. */
4527
- DeviceFeature["ScriptVariables"] = "script-variables";
4528
- /** Automation `trigger` accepts a skipCondition flag — fires the
4529
- * automation's actions while bypassing its condition block. */
4530
- DeviceFeature["AutomationSkipCondition"] = "automation-skip-condition";
4531
- return DeviceFeature;
4532
- }({});
4533
- var ChargingStatus = /* @__PURE__ */ function(ChargingStatus) {
4534
- ChargingStatus["ChargingDC"] = "charging-dc";
4535
- ChargingStatus["ChargingSolar"] = "charging-solar";
4536
- ChargingStatus["NotCharging"] = "not-charging";
4537
- return ChargingStatus;
4538
- }({});
4539
- /**
4540
- * Semantic role a device plays within its parent. Populated by driver
4541
- * addons when creating accessory devices (Reolink siren/floodlight/
4542
- * PIR/chime/autotrack/doorbell, ONVIF relay outputs, …). Used by the
4543
- * admin UI to pick icons, labels, and widgets — a `Switch` with
4544
- * `role: Floodlight` renders as a bulb with a brightness slider,
4545
- * whereas a `Switch` with `role: Siren` renders as a klaxon.
4546
- *
4547
- * Undefined for top-level devices (cameras, NVRs, hubs). Persisted in
4548
- * sqlite as a nullable TEXT column — old rows keep working unchanged.
4549
- */
4550
- var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
4551
- DeviceRole["Siren"] = "siren";
4552
- DeviceRole["Floodlight"] = "floodlight";
4553
- DeviceRole["Spotlight"] = "spotlight";
4554
- DeviceRole["PirSensor"] = "pir-sensor";
4555
- DeviceRole["Chime"] = "chime";
4556
- DeviceRole["Autotrack"] = "autotrack";
4557
- DeviceRole["Nightvision"] = "nightvision";
4558
- DeviceRole["PrivacyMask"] = "privacy-mask";
4559
- DeviceRole["Doorbell"] = "doorbell";
4560
- /** Virtual HA toggle (input_boolean.*) — distinguishable from a
4561
- * real Switch device for UI rendering / export adapters. */
4562
- DeviceRole["BinaryHelper"] = "binary-helper";
4563
- /** Generic motion / occupancy / moving event source. Distinct from
4564
- * the camera accessory PirSensor role: that one is a camera child;
4565
- * this is a standalone HA / 3rd-party motion sensor. */
4566
- DeviceRole["MotionSensor"] = "motion-sensor";
4567
- DeviceRole["ContactSensor"] = "contact-sensor";
4568
- DeviceRole["LeakSensor"] = "leak-sensor";
4569
- DeviceRole["SmokeSensor"] = "smoke-sensor";
4570
- DeviceRole["COSensor"] = "co-sensor";
4571
- DeviceRole["GasSensor"] = "gas-sensor";
4572
- DeviceRole["TamperSensor"] = "tamper-sensor";
4573
- DeviceRole["VibrationSensor"] = "vibration-sensor";
4574
- DeviceRole["ConnectivitySensor"] = "connectivity-sensor";
4575
- DeviceRole["SoundSensor"] = "sound-sensor";
4576
- /** Fallback for `binary_sensor` without a known `device_class`. */
4577
- DeviceRole["BinarySensor"] = "binary-sensor";
4578
- DeviceRole["TemperatureSensor"] = "temperature-sensor";
4579
- DeviceRole["HumiditySensor"] = "humidity-sensor";
4580
- DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
4581
- DeviceRole["PressureSensor"] = "pressure-sensor";
4582
- DeviceRole["PowerSensor"] = "power-sensor";
4583
- DeviceRole["EnergySensor"] = "energy-sensor";
4584
- DeviceRole["VoltageSensor"] = "voltage-sensor";
4585
- DeviceRole["CurrentSensor"] = "current-sensor";
4586
- DeviceRole["AirQualitySensor"] = "air-quality-sensor";
4587
- /** Battery level (numeric % via `sensor` OR low-bool via
4588
- * `binary_sensor` — the cap distinguishes via the value type). */
4589
- DeviceRole["BatterySensor"] = "battery-sensor";
4590
- /** Fallback for `sensor` numeric without a known `device_class`. */
4591
- DeviceRole["NumericSensor"] = "numeric-sensor";
4592
- /** String / enum state (HA `sensor` with `state_class: enum` or
4593
- * `attributes.options`). */
4594
- DeviceRole["EnumSensor"] = "enum-sensor";
4595
- /** Date / timestamp state (HA `sensor` with `device_class: timestamp`
4596
- * or `date`). The slice carries the raw ISO string verbatim (hosted on
4597
- * the `enum-sensor` cap); the UI renders it locale-formatted. */
4598
- DeviceRole["DateTimeSensor"] = "datetime-sensor";
4599
- /** Last-resort fallback when nothing else matches. */
4600
- DeviceRole["GenericSensor"] = "generic-sensor";
4601
- DeviceRole["NumericControl"] = "numeric-control";
4602
- DeviceRole["SelectControl"] = "select-control";
4603
- DeviceRole["TextControl"] = "text-control";
4604
- DeviceRole["DateTimeControl"] = "datetime-control";
4605
- /** Mobile push notifier (HA `notify.mobile_app_*`) — supports
4606
- * rich features (image, priority, channel routing). */
4607
- DeviceRole["MobilePushNotifier"] = "mobile-push-notifier";
4608
- /** Chat / messaging service (HA `notify.telegram_*`,
4609
- * `notify.discord_*`, etc.). */
4610
- DeviceRole["MessagingNotifier"] = "messaging-notifier";
4611
- /** Email-based delivery (HA `notify.smtp`, etc.). */
4612
- DeviceRole["EmailNotifier"] = "email-notifier";
4613
- /** Fallback when the notifier service name doesn't match a known
4614
- * pattern. */
4615
- DeviceRole["GenericNotifier"] = "generic-notifier";
4616
- return DeviceRole;
4617
- }({});
4618
- //#endregion
4619
2198
  //#region src/device/accessory.ts
4620
2199
  /**
4621
2200
  * Accessory device helpers — shared across drivers.
@@ -5174,155 +2753,19 @@ var AirQualitySensorStatusSchema = z.object({
5174
2753
  * auto-formatting when absent. */
5175
2754
  precision: z.number().int().min(0).max(10).optional()
5176
2755
  });
5177
- var airQualitySensorCapability = {
5178
- name: "air-quality-sensor",
5179
- scope: "device",
5180
- deviceNative: true,
5181
- mode: "singleton",
5182
- deviceTypes: [DeviceType.Sensor],
5183
- methods: {},
5184
- status: {
5185
- schema: AirQualitySensorStatusSchema,
5186
- kind: "push"
5187
- },
5188
- runtimeState: AirQualitySensorStatusSchema
5189
- };
5190
- //#endregion
5191
- //#region src/capabilities/capability-definition.ts
5192
- /**
5193
- * Generic types for capability definitions.
5194
- *
5195
- * A capability is defined with Zod schemas for methods, events, and settings.
5196
- * TypeScript types are inferred via z.infer<> — zero duplication.
5197
- *
5198
- * Pattern:
5199
- * 1. Define Zod schemas for data, methods, settings
5200
- * 2. Export const capabilityDef = { ... } satisfies CapabilityDefinition
5201
- * 3. Export type IProvider = InferProvider<typeof capabilityDef>
5202
- * 4. Addon implements IProvider
5203
- * 5. Registry auto-mounts tRPC router from definition.methods
5204
- */
5205
- /**
5206
- * Output schema shared by the contribution + live methods.
5207
- *
5208
- * Mirrors the `ConfigUISchemaWithValues` shape (sections[] + optional
5209
- * tabs[]) without importing from `../interfaces/config-ui.js` — a
5210
- * concrete-but-lenient Zod object keeps tRPC output inference happy
5211
- * (using `z.unknown()` here collapses unrelated router branches to
5212
- * `unknown` when the generator re-inlines the huge AppRouter type).
5213
- *
5214
- * `.passthrough()` on sections/fields accepts whatever FormBuilder
5215
- * extensions the caller adds (showWhen, displayScale, …) without
5216
- * rebuilding every time a new field kind is introduced.
5217
- */
5218
- var ContributionSectionSchema = z.object({
5219
- id: z.string(),
5220
- title: z.string(),
5221
- description: z.string().optional(),
5222
- style: z.enum(["card", "accordion"]).optional(),
5223
- defaultCollapsed: z.boolean().optional(),
5224
- columns: z.union([
5225
- z.literal(1),
5226
- z.literal(2),
5227
- z.literal(3),
5228
- z.literal(4)
5229
- ]).optional(),
5230
- tab: z.string().optional(),
5231
- location: z.enum(["settings", "top-tab"]).optional(),
5232
- order: z.number().optional(),
5233
- fields: z.array(z.any())
5234
- });
5235
- var ContributionTabSchema = z.object({
5236
- id: z.string(),
5237
- label: z.string(),
5238
- icon: z.string(),
5239
- order: z.number().optional()
5240
- });
5241
- var ContributionOutputSchema = z.object({
5242
- tabs: z.array(ContributionTabSchema).optional(),
5243
- sections: z.array(ContributionSectionSchema)
5244
- }).nullable();
5245
- var DEVICE_SETTINGS_CONTRIBUTION_METHODS = {
5246
- getDeviceSettingsContribution: {
5247
- input: z.object({ deviceId: z.number() }),
5248
- output: ContributionOutputSchema,
5249
- kind: "query",
5250
- auth: "protected"
5251
- },
5252
- getDeviceLiveContribution: {
5253
- input: z.object({ deviceId: z.number() }),
5254
- output: ContributionOutputSchema,
5255
- kind: "query",
5256
- auth: "protected"
5257
- },
5258
- applyDeviceSettingsPatch: {
5259
- input: z.object({
5260
- deviceId: z.number(),
5261
- patch: z.record(z.string(), z.unknown())
5262
- }),
5263
- output: z.object({ success: z.literal(true) }),
5264
- kind: "mutation",
5265
- auth: "admin"
5266
- }
5267
- };
5268
- /**
5269
- * Schema for the `getStatus` method auto-injected when a cap declares
5270
- * `status`. The runtime shape of the output is the cap's own
5271
- * `status.schema` — but at codegen time we need a concrete Zod to emit
5272
- * a typed tRPC route, so we keep the output as `z.unknown().nullable()`
5273
- * here and tighten it on the client side via the generated
5274
- * `CapStatusTypeMap` (see `scripts/generate-cap-status-types.ts`).
5275
- */
5276
- var DEVICE_STATUS_METHOD = { getStatus: {
5277
- input: z.object({ deviceId: z.number() }),
5278
- output: z.unknown().nullable(),
5279
- kind: "query",
5280
- auth: "protected"
5281
- } };
5282
- /**
5283
- * Expand a cap def's methods map with every auto-injected method:
5284
- * - `exposesDeviceSettings: true` → 3 contribution methods
5285
- * - `status: {...}` → `getStatus`
5286
- *
5287
- * Callers walk `expandCapMethods(def)` instead of `def.methods` when
5288
- * they need the effective runtime method surface (Moleculer actions,
5289
- * proxy shape, tRPC router entries). The cap def stays the single
5290
- * source of truth. Providers still declare their concrete `methods`
5291
- * block; the expansion happens at every consumption point, so there's
5292
- * no accidental divergence between the declared surface and what's
5293
- * actually mounted.
5294
- */
5295
- function expandCapMethods(def) {
5296
- let out = def.methods;
5297
- if (def.exposesDeviceSettings) out = {
5298
- ...DEVICE_SETTINGS_CONTRIBUTION_METHODS,
5299
- ...out
5300
- };
5301
- if (def.status) out = {
5302
- ...DEVICE_STATUS_METHOD,
5303
- ...out
5304
- };
5305
- return out;
5306
- }
5307
- /** Shorthand to define a method schema */
5308
- function method(input, output, options) {
5309
- return {
5310
- input,
5311
- output,
5312
- kind: options?.kind ?? "query",
5313
- auth: options?.auth ?? "protected",
5314
- ...options?.access !== void 0 ? { access: options.access } : {},
5315
- timeoutMs: options?.timeoutMs
5316
- };
5317
- }
5318
- /** Shorthand to define an event schema */
5319
- function event(data) {
5320
- return { data };
5321
- }
5322
- /** Type guard: does this cap declare the D14 device-config archetype? */
5323
- function isDeviceConfigCap(def) {
5324
- return def?.deviceConfig !== void 0;
5325
- }
2756
+ var airQualitySensorCapability = {
2757
+ name: "air-quality-sensor",
2758
+ scope: "device",
2759
+ deviceNative: true,
2760
+ mode: "singleton",
2761
+ deviceTypes: [DeviceType.Sensor],
2762
+ methods: {},
2763
+ status: {
2764
+ schema: AirQualitySensorStatusSchema,
2765
+ kind: "push"
2766
+ },
2767
+ runtimeState: AirQualitySensorStatusSchema
2768
+ };
5326
2769
  //#endregion
5327
2770
  //#region src/capabilities/alarm-panel.cap.ts
5328
2771
  /**
@@ -8401,6 +5844,24 @@ var DetectorOutputSchema = z.object({
8401
5844
  inferenceMs: z.number(),
8402
5845
  modelId: z.string()
8403
5846
  });
5847
+ var EngineProvisioningSchema = z.object({
5848
+ runtimeId: z.enum([
5849
+ "onnx",
5850
+ "openvino",
5851
+ "coreml"
5852
+ ]).nullable(),
5853
+ device: z.string().nullable(),
5854
+ state: z.enum([
5855
+ "idle",
5856
+ "installing",
5857
+ "verifying",
5858
+ "ready",
5859
+ "failed"
5860
+ ]),
5861
+ progress: z.number().optional(),
5862
+ error: z.string().optional(),
5863
+ nextRetryAt: z.number().optional()
5864
+ });
8404
5865
  var PipelineStepInputSchema = z.lazy(() => z.object({
8405
5866
  addonId: z.string(),
8406
5867
  modelId: z.string(),
@@ -8500,6 +5961,15 @@ var pipelineExecutorCapability = {
8500
5961
  kind: "mutation",
8501
5962
  auth: "admin"
8502
5963
  }),
5964
+ /**
5965
+ * Per-node detection-engine provisioning snapshot. Returns the live
5966
+ * state of the lazy runtime-provisioning machine on `nodeId`
5967
+ * (idle / installing / verifying / ready / failed). The UI pairs this
5968
+ * one-shot query with the `pipeline.engine-provisioning` live event
5969
+ * (emitted on every transition) to drive a per-node "engine ready?"
5970
+ * indicator without polling. Phase 2.
5971
+ */
5972
+ getEngineProvisioning: method(z.object({ nodeId: z.string() }), EngineProvisioningSchema),
8503
5973
  getVideoPipelineSteps: method(z.void(), z.record(z.string(), z.object({
8504
5974
  modelId: z.string(),
8505
5975
  settings: z.record(z.string(), z.unknown()).readonly()
@@ -12119,854 +9589,94 @@ function getByPath(obj, path) {
12119
9589
  /** Immutably set a dot-path on an object, returning a new object. Intermediate
12120
9590
  * objects are created when a segment is missing or null. Arrays are NOT
12121
9591
  * traversed (P1 has no array-index syntax — see `itemKey` grouping in P2): if a
12122
- * segment resolves to an array it is replaced by a fresh object, so the array
12123
- * is discarded — the only caller (`mergeLinkedStatus`) re-validates the result
12124
- * with the target cap's Zod schema and rejects a malformed overlay.
12125
- * The return type `T` preserves the OUTER shape; the rewritten key is not
12126
- * narrowed, so callers should re-validate against the status schema. */
12127
- function setByPath(obj, path, value) {
12128
- const dotIndex = path.indexOf(".");
12129
- if (dotIndex === -1) return {
12130
- ...obj,
12131
- [path]: value
12132
- };
12133
- const head = path.slice(0, dotIndex);
12134
- const tail = path.slice(dotIndex + 1);
12135
- const child = obj[head];
12136
- const childObj = isRecord(child) ? child : {};
12137
- return {
12138
- ...obj,
12139
- [head]: setByPath(childObj, tail, value)
12140
- };
12141
- }
12142
- //#endregion
12143
- //#region src/device/device-link-transform.ts
12144
- /** Apply a link transform to a resolved source value. `identity`/undefined
12145
- * pass through; `enum-map` remaps strings (fallback or null when unknown);
12146
- * `linear` does scale*x+offset with optional clamp (null for non-numbers). */
12147
- function applyTransform(value, transform) {
12148
- if (!transform || transform.kind === "identity") return value;
12149
- if (transform.kind === "enum-map") {
12150
- const key = String(value);
12151
- if (Object.prototype.hasOwnProperty.call(transform.mapping, key)) return transform.mapping[key];
12152
- return transform.fallback ?? null;
12153
- }
12154
- if (transform.kind === "linear") {
12155
- if (typeof value !== "number" || Number.isNaN(value)) return null;
12156
- let out = value * transform.scale + transform.offset;
12157
- if (transform.clamp) {
12158
- const min = transform.clamp[0];
12159
- const max = transform.clamp[1];
12160
- out = Math.min(max, Math.max(min, out));
12161
- }
12162
- return out;
12163
- }
12164
- return value;
12165
- }
12166
- //#endregion
12167
- //#region src/device/schema-fields.ts
12168
- /** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
12169
- * Zod v4 exposes `.unwrap()` on all three wrapper classes. */
12170
- function unwrap(schema) {
12171
- let s = schema;
12172
- while (s instanceof z.ZodNullable || s instanceof z.ZodOptional || s instanceof z.ZodDefault) s = s.unwrap();
12173
- return s;
12174
- }
12175
- function leafKind(schema) {
12176
- const s = unwrap(schema);
12177
- if (s instanceof z.ZodEnum) return {
12178
- kind: "enum",
12179
- enumValues: s.options
12180
- };
12181
- if (s instanceof z.ZodNumber) return { kind: "number" };
12182
- if (s instanceof z.ZodString) return { kind: "string" };
12183
- if (s instanceof z.ZodBoolean) return { kind: "boolean" };
12184
- return null;
12185
- }
12186
- /** Walk a cap status schema into flat wireable leaf fields (dotted paths).
12187
- * Recurses into nested ZodObject (unwrapping nullable/optional/default first),
12188
- * so fields with null live values are still offered. Arrays / unknown shapes
12189
- * are skipped — never throws. */
12190
- function enumerateSchemaFields(schema, prefix = "") {
12191
- const root = unwrap(schema);
12192
- if (!(root instanceof z.ZodObject)) return [];
12193
- const out = [];
12194
- const shape = root.shape;
12195
- for (const [key, field] of Object.entries(shape)) {
12196
- const path = prefix ? `${prefix}.${key}` : key;
12197
- const inner = unwrap(field);
12198
- if (inner instanceof z.ZodObject) {
12199
- out.push(...enumerateSchemaFields(inner, path));
12200
- continue;
12201
- }
12202
- const leaf = leafKind(field);
12203
- if (leaf) out.push({
12204
- path,
12205
- kind: leaf.kind,
12206
- ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
12207
- });
12208
- }
12209
- return out;
12210
- }
12211
- //#endregion
12212
- //#region src/device/device-state-handle.ts
12213
- var DEVICE_STATE_EVENT_CATEGORY = "device.state-changed";
12214
- /**
12215
- * Lazy tRPC source — the default behavior. Maintains a per-key local
12216
- * cache (`{deviceId}:{capName}` → last slice), one shared
12217
- * `live.onEvent` bridge that fans out into the listener map, and
12218
- * `refresh` round-trips through `deviceState.getCapSlice`.
12219
- *
12220
- * The bridge is opened on first `watch()` and closed when the last
12221
- * watcher unsubscribes — keeps idle handles cheap.
12222
- */
12223
- function createLazyTrpcSource(api) {
12224
- const cache = /* @__PURE__ */ new Map();
12225
- const listeners = /* @__PURE__ */ new Map();
12226
- let bridge = null;
12227
- const keyOf = (deviceId, capName) => `${deviceId}:${capName}`;
12228
- const ensureBridge = () => {
12229
- if (bridge) return;
12230
- if (!api.live?.onEvent) return;
12231
- bridge = api.live.onEvent.subscribe({ category: DEVICE_STATE_EVENT_CATEGORY }, { onData: (evt) => {
12232
- const data = evt.data;
12233
- if (!data || typeof data.deviceId !== "number" || typeof data.capName !== "string") return;
12234
- const k = keyOf(data.deviceId, data.capName);
12235
- cache.set(k, data.slice);
12236
- const set = listeners.get(k);
12237
- if (!set) return;
12238
- for (const cb of set) try {
12239
- cb(data.slice);
12240
- } catch {}
12241
- } });
12242
- };
12243
- const closeBridgeIfIdle = () => {
12244
- if (!bridge) return;
12245
- if (listeners.size > 0) return;
12246
- bridge.unsubscribe();
12247
- bridge = null;
12248
- };
12249
- return {
12250
- read(deviceId, capName) {
12251
- return cache.get(keyOf(deviceId, capName));
12252
- },
12253
- async refresh(deviceId, capName) {
12254
- const slice = await api.deviceState.getCapSlice.query({
12255
- deviceId,
12256
- capName
12257
- });
12258
- const k = keyOf(deviceId, capName);
12259
- cache.set(k, slice ?? void 0);
12260
- const set = listeners.get(k);
12261
- if (set) for (const cb of set) try {
12262
- cb(slice ?? void 0);
12263
- } catch {}
12264
- },
12265
- watch(deviceId, capName, cb) {
12266
- const k = keyOf(deviceId, capName);
12267
- let set = listeners.get(k);
12268
- if (!set) {
12269
- set = /* @__PURE__ */ new Set();
12270
- listeners.set(k, set);
12271
- }
12272
- set.add(cb);
12273
- ensureBridge();
12274
- return () => {
12275
- set.delete(cb);
12276
- if (set.size === 0) listeners.delete(k);
12277
- closeBridgeIfIdle();
12278
- };
12279
- },
12280
- async write(deviceId, capName, slice) {
12281
- await api.deviceState.setCapSlice.mutate({
12282
- deviceId,
12283
- capName,
12284
- slice
12285
- });
12286
- }
12287
- };
12288
- }
12289
- /**
12290
- * Mirror source — reads from a shared map populated by a
12291
- * `SystemManager` warm-boot + push event handler. Refresh is a no-op
12292
- * (the SystemManager owns the update loop). `watch()` registers in a
12293
- * shared listener set — the SystemManager calls these from its single
12294
- * `device.state-changed` subscription.
12295
- *
12296
- * The mirror map and listener map are passed in by reference so the
12297
- * SystemManager can mutate both as events arrive.
12298
- */
12299
- function createMirrorSource(mirror, listeners, api) {
12300
- const keyOf = (deviceId, capName) => `${deviceId}:${capName}`;
12301
- return {
12302
- read(deviceId, capName) {
12303
- return mirror.get(deviceId)?.get(capName);
12304
- },
12305
- async refresh() {},
12306
- watch(deviceId, capName, cb) {
12307
- const k = keyOf(deviceId, capName);
12308
- let set = listeners.get(k);
12309
- if (!set) {
12310
- set = /* @__PURE__ */ new Set();
12311
- listeners.set(k, set);
12312
- }
12313
- set.add(cb);
12314
- return () => {
12315
- set.delete(cb);
12316
- if (set.size === 0) listeners.delete(k);
12317
- };
12318
- },
12319
- async write(deviceId, capName, slice) {
12320
- if (!api) throw new Error("createMirrorSource: write requires an api reference — pass it as the third argument when constructing the source");
12321
- await api.deviceState.setCapSlice.mutate({
12322
- deviceId,
12323
- capName,
12324
- slice
12325
- });
12326
- }
12327
- };
12328
- }
12329
- /**
12330
- * Build a `SliceHandle<T>` bound to `(deviceId, capName)`. Caller
12331
- * provides the type parameter — codegen passes
12332
- * `InferRuntimeState<typeof <cap>>` so consumers see the cap's typed
12333
- * shape:
12334
- *
12335
- * const dev = createDeviceProxy(api, binding)
12336
- * dev.state.battery.value // BatteryStatus | undefined
12337
- * dev.state.battery.value?.percentage
12338
- *
12339
- * Two-arg form (deprecated path, kept for spec compatibility):
12340
- * passes the api directly, builds a per-handle lazy source. New
12341
- * code should pre-build a single source and reuse it across handles
12342
- * via the explicit `source` form.
12343
- */
12344
- function createSliceHandle(source, deviceId, capName) {
12345
- const src = isSource(source) ? source : createLazyTrpcSource(source);
12346
- return {
12347
- get value() {
12348
- return src.read(deviceId, capName);
12349
- },
12350
- refresh() {
12351
- return src.refresh(deviceId, capName);
12352
- },
12353
- subscribe(cb) {
12354
- const unwatch = src.watch(deviceId, capName, (slice) => {
12355
- try {
12356
- cb(slice);
12357
- } catch {}
12358
- });
12359
- try {
12360
- cb(src.read(deviceId, capName));
12361
- } catch {}
12362
- if (src.read(deviceId, capName) === void 0) src.refresh(deviceId, capName).catch(() => void 0);
12363
- return unwatch;
12364
- },
12365
- async set(slice) {
12366
- await src.write(deviceId, capName, slice);
12367
- },
12368
- async patch(partial) {
12369
- const next = {
12370
- ...src.read(deviceId, capName),
12371
- ...partial
12372
- };
12373
- await src.write(deviceId, capName, next);
12374
- }
12375
- };
12376
- }
12377
- function isSource(x) {
12378
- return typeof x.read === "function" && typeof x.watch === "function";
12379
- }
12380
- //#endregion
12381
- //#region src/generated/device-proxy.ts
12382
- /**
12383
- * Build a DeviceProxy that pre-binds deviceId + nodeId on every method call
12384
- * and dispatches through the existing cap-router tRPC procedures.
12385
- *
12386
- * Optional `opts.stateSource` lets a SystemManager pass a shared mirror
12387
- * source so every device proxy reads from the same in-memory map and
12388
- * shares the warm-boot/push-event update loop. When omitted, a per-proxy
12389
- * lazy tRPC source is created — the path used by `ctx.fetchDevice` and
12390
- * `BackendClient.fetchDevice` for one-off reads.
12391
- *
12392
- * The returned proxy's `binding` field is set to the input `binding`
12393
- * by default — callers that want to expose a different value (e.g. the
12394
- * SDK's `System` patches it from a shared cache) can overwrite the
12395
- * field on the returned object.
12396
- */
12397
- function createDeviceProxy(api, binding, opts) {
12398
- const typedApi = api;
12399
- const stateSource = opts?.stateSource ?? createLazyTrpcSource(api);
12400
- /** Merge `{deviceId, nodeId?}` into the caller-supplied input. */
12401
- function mergeInput(input, nodeId) {
12402
- const base = typeof input === "object" && input !== null ? input : {};
12403
- return nodeId !== void 0 ? {
12404
- ...base,
12405
- deviceId: binding.deviceId,
12406
- nodeId
12407
- } : {
12408
- ...base,
12409
- deviceId: binding.deviceId
12410
- };
12411
- }
12412
- /**
12413
- * Invoke `api.<capProp>.<method>.{query|mutate|subscribe}(merged)`.
12414
- * Returns `any` so the caller-side per-method declaration assigns
12415
- * cleanly into the strongly-typed `InferDeviceProxyCap<...>` shape on
12416
- * the `DeviceProxy` interface — the proxy's contract is enforced by
12417
- * the interface, not the dispatch helper.
12418
- */
12419
- function callLeaf(capProp, method, kind, merged, push) {
12420
- const leaf = typedApi[capProp]?.[method];
12421
- if (!leaf) throw new Error(`DeviceProxy: api has no '${capProp}.${method}'`);
12422
- const fn = leaf;
12423
- if (kind === "mutation") return fn.mutate(merged);
12424
- if (kind === "subscription") return fn.subscribe(merged, push);
12425
- return fn.query(merged);
12426
- }
12427
- /**
12428
- * Device-scoped cap dispatch. Looks up the binding entry for `capName`
12429
- * to pin the call to the worker that owns the per-device provider; when
12430
- * no entry exists (cluster-wide singletons like `zones` /
12431
- * `zone-rules` / `audio-metrics` that don't register per-device
12432
- * natives), nodeId is omitted and the cap-router's `resolveProvider`
12433
- * falls through to the local provider — a Moleculer bridge proxy when
12434
- * the actual singleton lives in a worker.
12435
- */
12436
- function dispatch(capName, capProp, method, kind, input, push) {
12437
- return callLeaf(capProp, method, kind, mergeInput(input, binding.entries.find((e) => e.capName === capName)?.providerNodeId), push);
12438
- }
12439
- /**
12440
- * System-cap dispatch. No binding gate — system caps are cluster-wide
12441
- * singletons; the nodeId is left absent so caps that load-balance (e.g.
12442
- * `pipeline-runner`) can resolve their own target node.
12443
- */
12444
- function dispatchSystem(capProp, method, kind, input, push) {
12445
- return callLeaf(capProp, method, kind, mergeInput(input, void 0), push);
12446
- }
12447
- return {
12448
- deviceId: binding.deviceId,
12449
- binding,
12450
- state: {
12451
- airQualitySensor: createSliceHandle(stateSource, binding.deviceId, "air-quality-sensor"),
12452
- alarmPanel: createSliceHandle(stateSource, binding.deviceId, "alarm-panel"),
12453
- ambientLightSensor: createSliceHandle(stateSource, binding.deviceId, "ambient-light-sensor"),
12454
- audioMetrics: createSliceHandle(stateSource, binding.deviceId, "audio-metrics"),
12455
- automationControl: createSliceHandle(stateSource, binding.deviceId, "automation-control"),
12456
- battery: createSliceHandle(stateSource, binding.deviceId, "battery"),
12457
- binary: createSliceHandle(stateSource, binding.deviceId, "binary"),
12458
- brightness: createSliceHandle(stateSource, binding.deviceId, "brightness"),
12459
- cameraStreams: createSliceHandle(stateSource, binding.deviceId, "camera-streams"),
12460
- carbonMonoxide: createSliceHandle(stateSource, binding.deviceId, "carbon-monoxide"),
12461
- climateControl: createSliceHandle(stateSource, binding.deviceId, "climate-control"),
12462
- color: createSliceHandle(stateSource, binding.deviceId, "color"),
12463
- connectivity: createSliceHandle(stateSource, binding.deviceId, "connectivity"),
12464
- consumables: createSliceHandle(stateSource, binding.deviceId, "consumables"),
12465
- contact: createSliceHandle(stateSource, binding.deviceId, "contact"),
12466
- control: createSliceHandle(stateSource, binding.deviceId, "control"),
12467
- cover: createSliceHandle(stateSource, binding.deviceId, "cover"),
12468
- deviceDiscovery: createSliceHandle(stateSource, binding.deviceId, "device-discovery"),
12469
- deviceStatus: createSliceHandle(stateSource, binding.deviceId, "device-status"),
12470
- doorbell: createSliceHandle(stateSource, binding.deviceId, "doorbell"),
12471
- enumSensor: createSliceHandle(stateSource, binding.deviceId, "enum-sensor"),
12472
- eventEmitter: createSliceHandle(stateSource, binding.deviceId, "event-emitter"),
12473
- fanControl: createSliceHandle(stateSource, binding.deviceId, "fan-control"),
12474
- featureProbe: createSliceHandle(stateSource, binding.deviceId, "feature-probe"),
12475
- flood: createSliceHandle(stateSource, binding.deviceId, "flood"),
12476
- gas: createSliceHandle(stateSource, binding.deviceId, "gas"),
12477
- humidifier: createSliceHandle(stateSource, binding.deviceId, "humidifier"),
12478
- humiditySensor: createSliceHandle(stateSource, binding.deviceId, "humidity-sensor"),
12479
- image: createSliceHandle(stateSource, binding.deviceId, "image"),
12480
- lawnMowerControl: createSliceHandle(stateSource, binding.deviceId, "lawn-mower-control"),
12481
- lockControl: createSliceHandle(stateSource, binding.deviceId, "lock-control"),
12482
- mediaPlayer: createSliceHandle(stateSource, binding.deviceId, "media-player"),
12483
- motion: createSliceHandle(stateSource, binding.deviceId, "motion"),
12484
- motionTrigger: createSliceHandle(stateSource, binding.deviceId, "motion-trigger"),
12485
- motionZones: createSliceHandle(stateSource, binding.deviceId, "motion-zones"),
12486
- nativeObjectDetection: createSliceHandle(stateSource, binding.deviceId, "native-object-detection"),
12487
- notifier: createSliceHandle(stateSource, binding.deviceId, "notifier"),
12488
- numericSensor: createSliceHandle(stateSource, binding.deviceId, "numeric-sensor"),
12489
- powerMeter: createSliceHandle(stateSource, binding.deviceId, "power-meter"),
12490
- presence: createSliceHandle(stateSource, binding.deviceId, "presence"),
12491
- pressureSensor: createSliceHandle(stateSource, binding.deviceId, "pressure-sensor"),
12492
- privacyMask: createSliceHandle(stateSource, binding.deviceId, "privacy-mask"),
12493
- ptzAutotrack: createSliceHandle(stateSource, binding.deviceId, "ptz-autotrack"),
12494
- scriptRunner: createSliceHandle(stateSource, binding.deviceId, "script-runner"),
12495
- smoke: createSliceHandle(stateSource, binding.deviceId, "smoke"),
12496
- streamParams: createSliceHandle(stateSource, binding.deviceId, "stream-params"),
12497
- switch: createSliceHandle(stateSource, binding.deviceId, "switch"),
12498
- tamper: createSliceHandle(stateSource, binding.deviceId, "tamper"),
12499
- temperatureSensor: createSliceHandle(stateSource, binding.deviceId, "temperature-sensor"),
12500
- update: createSliceHandle(stateSource, binding.deviceId, "update"),
12501
- vacuumControl: createSliceHandle(stateSource, binding.deviceId, "vacuum-control"),
12502
- valve: createSliceHandle(stateSource, binding.deviceId, "valve"),
12503
- vibration: createSliceHandle(stateSource, binding.deviceId, "vibration"),
12504
- waterHeater: createSliceHandle(stateSource, binding.deviceId, "water-heater"),
12505
- weather: createSliceHandle(stateSource, binding.deviceId, "weather"),
12506
- zoneAnalytics: createSliceHandle(stateSource, binding.deviceId, "zone-analytics"),
12507
- zoneRules: createSliceHandle(stateSource, binding.deviceId, "zone-rules"),
12508
- zones: createSliceHandle(stateSource, binding.deviceId, "zones")
12509
- },
12510
- accessories: {
12511
- setChildHidden: (input) => dispatch("accessories", "accessories", "setChildHidden", "mutation", input),
12512
- getStatus: (input) => dispatch("accessories", "accessories", "getStatus", "query", input)
12513
- },
12514
- airQualitySensor: { getStatus: (input) => dispatch("air-quality-sensor", "airQualitySensor", "getStatus", "query", input) },
12515
- alarmPanel: {
12516
- arm: (input) => dispatch("alarm-panel", "alarmPanel", "arm", "mutation", input),
12517
- disarm: (input) => dispatch("alarm-panel", "alarmPanel", "disarm", "mutation", input),
12518
- trigger: (input) => dispatch("alarm-panel", "alarmPanel", "trigger", "mutation", input),
12519
- getStatus: (input) => dispatch("alarm-panel", "alarmPanel", "getStatus", "query", input)
12520
- },
12521
- ambientLightSensor: { getStatus: (input) => dispatch("ambient-light-sensor", "ambientLightSensor", "getStatus", "query", input) },
12522
- audioAnalysis: {
12523
- resolveDeviceSettings: (input) => dispatch("audio-analysis", "audioAnalysis", "resolveDeviceSettings", "query", input),
12524
- getDeviceSettingsContribution: (input) => dispatch("audio-analysis", "audioAnalysis", "getDeviceSettingsContribution", "query", input),
12525
- getDeviceLiveContribution: (input) => dispatch("audio-analysis", "audioAnalysis", "getDeviceLiveContribution", "query", input),
12526
- applyDeviceSettingsPatch: (input) => dispatch("audio-analysis", "audioAnalysis", "applyDeviceSettingsPatch", "mutation", input)
12527
- },
12528
- audioMetrics: {
12529
- getCurrentSnapshot: (input) => dispatch("audio-metrics", "audioMetrics", "getCurrentSnapshot", "query", input),
12530
- getHistory: (input) => dispatch("audio-metrics", "audioMetrics", "getHistory", "query", input)
12531
- },
12532
- automationControl: {
12533
- enable: (input) => dispatch("automation-control", "automationControl", "enable", "mutation", input),
12534
- disable: (input) => dispatch("automation-control", "automationControl", "disable", "mutation", input),
12535
- trigger: (input) => dispatch("automation-control", "automationControl", "trigger", "mutation", input),
12536
- getStatus: (input) => dispatch("automation-control", "automationControl", "getStatus", "query", input)
12537
- },
12538
- battery: {
12539
- wakeForStream: (input) => dispatch("battery", "battery", "wakeForStream", "mutation", input),
12540
- getStatus: (input) => dispatch("battery", "battery", "getStatus", "query", input)
12541
- },
12542
- binary: { getStatus: (input) => dispatch("binary", "binary", "getStatus", "query", input) },
12543
- brightness: {
12544
- setBrightness: (input) => dispatch("brightness", "brightness", "setBrightness", "mutation", input),
12545
- getStatus: (input) => dispatch("brightness", "brightness", "getStatus", "query", input)
12546
- },
12547
- button: { press: (input) => dispatch("button", "button", "press", "mutation", input) },
12548
- cameraCredentials: {
12549
- getCredentials: (input) => dispatch("camera-credentials", "cameraCredentials", "getCredentials", "query", input),
12550
- getStatus: (input) => dispatch("camera-credentials", "cameraCredentials", "getStatus", "query", input)
12551
- },
12552
- cameraStreams: {
12553
- getCameraStreams: (input) => dispatch("camera-streams", "cameraStreams", "getCameraStreams", "query", input),
12554
- getBrokerStreams: (input) => dispatch("camera-streams", "cameraStreams", "getBrokerStreams", "query", input),
12555
- getRtspEntries: (input) => dispatch("camera-streams", "cameraStreams", "getRtspEntries", "query", input),
12556
- getProfileRtspEntries: (input) => dispatch("camera-streams", "cameraStreams", "getProfileRtspEntries", "query", input),
12557
- pickStream: (input) => dispatch("camera-streams", "cameraStreams", "pickStream", "query", input)
12558
- },
12559
- carbonMonoxide: { getStatus: (input) => dispatch("carbon-monoxide", "carbonMonoxide", "getStatus", "query", input) },
12560
- climateControl: {
12561
- setMode: (input) => dispatch("climate-control", "climateControl", "setMode", "mutation", input),
12562
- setFanMode: (input) => dispatch("climate-control", "climateControl", "setFanMode", "mutation", input),
12563
- setPreset: (input) => dispatch("climate-control", "climateControl", "setPreset", "mutation", input),
12564
- setTarget: (input) => dispatch("climate-control", "climateControl", "setTarget", "mutation", input),
12565
- setTargetRange: (input) => dispatch("climate-control", "climateControl", "setTargetRange", "mutation", input),
12566
- setTargetHumidity: (input) => dispatch("climate-control", "climateControl", "setTargetHumidity", "mutation", input),
12567
- getStatus: (input) => dispatch("climate-control", "climateControl", "getStatus", "query", input)
12568
- },
12569
- color: {
12570
- setColor: (input) => dispatch("color", "color", "setColor", "mutation", input),
12571
- getStatus: (input) => dispatch("color", "color", "getStatus", "query", input)
12572
- },
12573
- connectivity: { getStatus: (input) => dispatch("connectivity", "connectivity", "getStatus", "query", input) },
12574
- consumables: {
12575
- reset: (input) => dispatch("consumables", "consumables", "reset", "mutation", input),
12576
- getStatus: (input) => dispatch("consumables", "consumables", "getStatus", "query", input)
12577
- },
12578
- contact: { getStatus: (input) => dispatch("contact", "contact", "getStatus", "query", input) },
12579
- control: {
12580
- setValue: (input) => dispatch("control", "control", "setValue", "mutation", input),
12581
- getStatus: (input) => dispatch("control", "control", "getStatus", "query", input)
12582
- },
12583
- cover: {
12584
- open: (input) => dispatch("cover", "cover", "open", "mutation", input),
12585
- close: (input) => dispatch("cover", "cover", "close", "mutation", input),
12586
- stop: (input) => dispatch("cover", "cover", "stop", "mutation", input),
12587
- setPosition: (input) => dispatch("cover", "cover", "setPosition", "mutation", input),
12588
- setTiltPosition: (input) => dispatch("cover", "cover", "setTiltPosition", "mutation", input),
12589
- getStatus: (input) => dispatch("cover", "cover", "getStatus", "query", input)
12590
- },
12591
- detectionPipeline: {
12592
- getDeviceSettingsContribution: (input) => dispatch("detection-pipeline", "detectionPipeline", "getDeviceSettingsContribution", "query", input),
12593
- getDeviceLiveContribution: (input) => dispatch("detection-pipeline", "detectionPipeline", "getDeviceLiveContribution", "query", input),
12594
- applyDeviceSettingsPatch: (input) => dispatch("detection-pipeline", "detectionPipeline", "applyDeviceSettingsPatch", "mutation", input)
12595
- },
12596
- deviceDiscovery: {
12597
- listDiscovered: (input) => dispatch("device-discovery", "deviceDiscovery", "listDiscovered", "query", input),
12598
- refreshDiscovery: (input) => dispatch("device-discovery", "deviceDiscovery", "refreshDiscovery", "mutation", input),
12599
- adoptDevice: (input) => dispatch("device-discovery", "deviceDiscovery", "adoptDevice", "mutation", input),
12600
- releaseDevice: (input) => dispatch("device-discovery", "deviceDiscovery", "releaseDevice", "mutation", input),
12601
- getStatus: (input) => dispatch("device-discovery", "deviceDiscovery", "getStatus", "query", input)
12602
- },
12603
- deviceOps: {
12604
- getStreamSources: (input) => dispatch("device-ops", "deviceOps", "getStreamSources", "query", input),
12605
- getConfigEntries: (input) => dispatch("device-ops", "deviceOps", "getConfigEntries", "query", input),
12606
- setConfig: (input) => dispatch("device-ops", "deviceOps", "setConfig", "mutation", input),
12607
- runAction: (input) => dispatch("device-ops", "deviceOps", "runAction", "mutation", input),
12608
- removeDevice: (input) => dispatch("device-ops", "deviceOps", "removeDevice", "mutation", input),
12609
- getSettingsSchema: (input) => dispatch("device-ops", "deviceOps", "getSettingsSchema", "query", input),
12610
- getRawState: (input) => dispatch("device-ops", "deviceOps", "getRawState", "query", input)
12611
- },
12612
- deviceStatus: { getStatus: (input) => dispatch("device-status", "deviceStatus", "getStatus", "query", input) },
12613
- doorbell: { getStatus: (input) => dispatch("doorbell", "doorbell", "getStatus", "query", input) },
12614
- enumSensor: { getStatus: (input) => dispatch("enum-sensor", "enumSensor", "getStatus", "query", input) },
12615
- eventEmitter: { getStatus: (input) => dispatch("event-emitter", "eventEmitter", "getStatus", "query", input) },
12616
- events: {
12617
- getEvents: (input) => dispatch("events", "events", "getEvents", "query", input),
12618
- getEventThumbnail: (input) => dispatch("events", "events", "getEventThumbnail", "query", input),
12619
- getEventClipUrl: (input) => dispatch("events", "events", "getEventClipUrl", "query", input)
12620
- },
12621
- fanControl: {
12622
- setPercentage: (input) => dispatch("fan-control", "fanControl", "setPercentage", "mutation", input),
12623
- setPreset: (input) => dispatch("fan-control", "fanControl", "setPreset", "mutation", input),
12624
- setDirection: (input) => dispatch("fan-control", "fanControl", "setDirection", "mutation", input),
12625
- setOscillating: (input) => dispatch("fan-control", "fanControl", "setOscillating", "mutation", input),
12626
- getStatus: (input) => dispatch("fan-control", "fanControl", "getStatus", "query", input)
12627
- },
12628
- featureProbe: { getStatus: (input) => dispatch("feature-probe", "featureProbe", "getStatus", "query", input) },
12629
- flood: { getStatus: (input) => dispatch("flood", "flood", "getStatus", "query", input) },
12630
- gas: { getStatus: (input) => dispatch("gas", "gas", "getStatus", "query", input) },
12631
- humidifier: {
12632
- setOn: (input) => dispatch("humidifier", "humidifier", "setOn", "mutation", input),
12633
- setTargetHumidity: (input) => dispatch("humidifier", "humidifier", "setTargetHumidity", "mutation", input),
12634
- setMode: (input) => dispatch("humidifier", "humidifier", "setMode", "mutation", input),
12635
- getStatus: (input) => dispatch("humidifier", "humidifier", "getStatus", "query", input)
12636
- },
12637
- humiditySensor: { getStatus: (input) => dispatch("humidity-sensor", "humiditySensor", "getStatus", "query", input) },
12638
- image: { getStatus: (input) => dispatch("image", "image", "getStatus", "query", input) },
12639
- intercom: {
12640
- startSession: (input) => dispatch("intercom", "intercom", "startSession", "mutation", input),
12641
- handleAnswer: (input) => dispatch("intercom", "intercom", "handleAnswer", "mutation", input),
12642
- stopSession: (input) => dispatch("intercom", "intercom", "stopSession", "mutation", input),
12643
- startTalkSession: (input) => dispatch("intercom", "intercom", "startTalkSession", "mutation", input),
12644
- pushTalkAudio: (input) => dispatch("intercom", "intercom", "pushTalkAudio", "mutation", input),
12645
- endTalkSession: (input) => dispatch("intercom", "intercom", "endTalkSession", "mutation", input),
12646
- getStatus: (input) => dispatch("intercom", "intercom", "getStatus", "query", input)
12647
- },
12648
- lawnMowerControl: {
12649
- startMowing: (input) => dispatch("lawn-mower-control", "lawnMowerControl", "startMowing", "mutation", input),
12650
- pause: (input) => dispatch("lawn-mower-control", "lawnMowerControl", "pause", "mutation", input),
12651
- dock: (input) => dispatch("lawn-mower-control", "lawnMowerControl", "dock", "mutation", input),
12652
- getStatus: (input) => dispatch("lawn-mower-control", "lawnMowerControl", "getStatus", "query", input)
12653
- },
12654
- lockControl: {
12655
- lock: (input) => dispatch("lock-control", "lockControl", "lock", "mutation", input),
12656
- unlock: (input) => dispatch("lock-control", "lockControl", "unlock", "mutation", input),
12657
- open: (input) => dispatch("lock-control", "lockControl", "open", "mutation", input),
12658
- getStatus: (input) => dispatch("lock-control", "lockControl", "getStatus", "query", input)
12659
- },
12660
- mediaPlayer: {
12661
- play: (input) => dispatch("media-player", "mediaPlayer", "play", "mutation", input),
12662
- pause: (input) => dispatch("media-player", "mediaPlayer", "pause", "mutation", input),
12663
- stop: (input) => dispatch("media-player", "mediaPlayer", "stop", "mutation", input),
12664
- next: (input) => dispatch("media-player", "mediaPlayer", "next", "mutation", input),
12665
- previous: (input) => dispatch("media-player", "mediaPlayer", "previous", "mutation", input),
12666
- seek: (input) => dispatch("media-player", "mediaPlayer", "seek", "mutation", input),
12667
- setVolume: (input) => dispatch("media-player", "mediaPlayer", "setVolume", "mutation", input),
12668
- setMute: (input) => dispatch("media-player", "mediaPlayer", "setMute", "mutation", input),
12669
- setShuffle: (input) => dispatch("media-player", "mediaPlayer", "setShuffle", "mutation", input),
12670
- setRepeat: (input) => dispatch("media-player", "mediaPlayer", "setRepeat", "mutation", input),
12671
- selectSource: (input) => dispatch("media-player", "mediaPlayer", "selectSource", "mutation", input),
12672
- playMedia: (input) => dispatch("media-player", "mediaPlayer", "playMedia", "mutation", input),
12673
- getStatus: (input) => dispatch("media-player", "mediaPlayer", "getStatus", "query", input)
12674
- },
12675
- motion: {
12676
- isDetected: (input) => dispatch("motion", "motion", "isDetected", "query", input),
12677
- getStatus: (input) => dispatch("motion", "motion", "getStatus", "query", input)
12678
- },
12679
- motionDetection: {
12680
- analyze: (input) => dispatch("motion-detection", "motionDetection", "analyze", "mutation", input),
12681
- removeCamera: (input) => dispatch("motion-detection", "motionDetection", "removeCamera", "mutation", input),
12682
- reset: (input) => dispatch("motion-detection", "motionDetection", "reset", "mutation", input),
12683
- getDeviceSettingsContribution: (input) => dispatch("motion-detection", "motionDetection", "getDeviceSettingsContribution", "query", input),
12684
- getDeviceLiveContribution: (input) => dispatch("motion-detection", "motionDetection", "getDeviceLiveContribution", "query", input),
12685
- applyDeviceSettingsPatch: (input) => dispatch("motion-detection", "motionDetection", "applyDeviceSettingsPatch", "mutation", input)
12686
- },
12687
- motionTrigger: {
12688
- setMotionTrigger: (input) => dispatch("motion-trigger", "motionTrigger", "setMotionTrigger", "mutation", input),
12689
- getStatus: (input) => dispatch("motion-trigger", "motionTrigger", "getStatus", "query", input)
12690
- },
12691
- motionZones: {
12692
- getOptions: (input) => dispatch("motion-zones", "motionZones", "getOptions", "query", input),
12693
- setZone: (input) => dispatch("motion-zones", "motionZones", "setZone", "mutation", input),
12694
- getStatus: (input) => dispatch("motion-zones", "motionZones", "getStatus", "query", input)
12695
- },
12696
- nativeObjectDetection: {
12697
- setEnabled: (input) => dispatch("native-object-detection", "nativeObjectDetection", "setEnabled", "mutation", input),
12698
- getStatus: (input) => dispatch("native-object-detection", "nativeObjectDetection", "getStatus", "query", input)
12699
- },
12700
- notifier: {
12701
- send: (input) => dispatch("notifier", "notifier", "send", "mutation", input),
12702
- cancel: (input) => dispatch("notifier", "notifier", "cancel", "mutation", input),
12703
- getStatus: (input) => dispatch("notifier", "notifier", "getStatus", "query", input)
12704
- },
12705
- numericSensor: { getStatus: (input) => dispatch("numeric-sensor", "numericSensor", "getStatus", "query", input) },
12706
- osd: {
12707
- setOverlay: (input) => dispatch("osd", "osd", "setOverlay", "mutation", input),
12708
- getStatus: (input) => dispatch("osd", "osd", "getStatus", "query", input)
12709
- },
12710
- pipelineAnalytics: {
12711
- getActiveTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getActiveTracks", "query", input),
12712
- getTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrack", "query", input),
12713
- listTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listTracks", "query", input),
12714
- clearTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "clearTracks", "mutation", input),
12715
- getMotionEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getMotionEvents", "query", input),
12716
- getObjectEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getObjectEvents", "query", input),
12717
- getAudioEvents: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getAudioEvents", "query", input),
12718
- getEventDensity: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getEventDensity", "query", input),
12719
- pruneEventsBefore: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "pruneEventsBefore", "mutation", input),
12720
- getEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getEventMedia", "query", input),
12721
- getTrackMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrackMedia", "query", input),
12722
- getDeviceSettingsContribution: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getDeviceSettingsContribution", "query", input),
12723
- getDeviceLiveContribution: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getDeviceLiveContribution", "query", input),
12724
- applyDeviceSettingsPatch: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "applyDeviceSettingsPatch", "mutation", input)
12725
- },
12726
- powerMeter: { getStatus: (input) => dispatch("power-meter", "powerMeter", "getStatus", "query", input) },
12727
- presence: { getStatus: (input) => dispatch("presence", "presence", "getStatus", "query", input) },
12728
- pressureSensor: { getStatus: (input) => dispatch("pressure-sensor", "pressureSensor", "getStatus", "query", input) },
12729
- privacyMask: {
12730
- getOptions: (input) => dispatch("privacy-mask", "privacyMask", "getOptions", "query", input),
12731
- setMask: (input) => dispatch("privacy-mask", "privacyMask", "setMask", "mutation", input),
12732
- getStatus: (input) => dispatch("privacy-mask", "privacyMask", "getStatus", "query", input)
12733
- },
12734
- ptz: {
12735
- move: (input) => dispatch("ptz", "ptz", "move", "mutation", input),
12736
- continuousMove: (input) => dispatch("ptz", "ptz", "continuousMove", "mutation", input),
12737
- stop: (input) => dispatch("ptz", "ptz", "stop", "mutation", input),
12738
- getPresets: (input) => dispatch("ptz", "ptz", "getPresets", "query", input),
12739
- goToPreset: (input) => dispatch("ptz", "ptz", "goToPreset", "mutation", input),
12740
- savePreset: (input) => dispatch("ptz", "ptz", "savePreset", "mutation", input),
12741
- deletePreset: (input) => dispatch("ptz", "ptz", "deletePreset", "mutation", input),
12742
- getOptions: (input) => dispatch("ptz", "ptz", "getOptions", "query", input),
12743
- goHome: (input) => dispatch("ptz", "ptz", "goHome", "mutation", input),
12744
- getPosition: (input) => dispatch("ptz", "ptz", "getPosition", "query", input),
12745
- setAutofocus: (input) => dispatch("ptz", "ptz", "setAutofocus", "mutation", input),
12746
- getStatus: (input) => dispatch("ptz", "ptz", "getStatus", "query", input)
12747
- },
12748
- ptzAutotrack: {
12749
- getStatus: (input) => dispatch("ptz-autotrack", "ptzAutotrack", "getStatus", "query", input),
12750
- setEnabled: (input) => dispatch("ptz-autotrack", "ptzAutotrack", "setEnabled", "mutation", input),
12751
- getSettings: (input) => dispatch("ptz-autotrack", "ptzAutotrack", "getSettings", "query", input),
12752
- setSettings: (input) => dispatch("ptz-autotrack", "ptzAutotrack", "setSettings", "mutation", input)
12753
- },
12754
- reboot: { reboot: (input) => dispatch("reboot", "reboot", "reboot", "mutation", input) },
12755
- scriptRunner: {
12756
- run: (input) => dispatch("script-runner", "scriptRunner", "run", "mutation", input),
12757
- stop: (input) => dispatch("script-runner", "scriptRunner", "stop", "mutation", input),
12758
- getStatus: (input) => dispatch("script-runner", "scriptRunner", "getStatus", "query", input)
12759
- },
12760
- smoke: { getStatus: (input) => dispatch("smoke", "smoke", "getStatus", "query", input) },
12761
- snapshot: {
12762
- getSnapshot: (input) => dispatch("snapshot", "snapshot", "getSnapshot", "query", input),
12763
- invalidateCache: (input) => dispatch("snapshot", "snapshot", "invalidateCache", "mutation", input),
12764
- getStatus: (input) => dispatch("snapshot", "snapshot", "getStatus", "query", input),
12765
- getDeviceSettingsContribution: (input) => dispatch("snapshot", "snapshot", "getDeviceSettingsContribution", "query", input),
12766
- getDeviceLiveContribution: (input) => dispatch("snapshot", "snapshot", "getDeviceLiveContribution", "query", input),
12767
- applyDeviceSettingsPatch: (input) => dispatch("snapshot", "snapshot", "applyDeviceSettingsPatch", "mutation", input)
12768
- },
12769
- streamCatalog: { getCatalog: (input) => dispatch("stream-catalog", "streamCatalog", "getCatalog", "query", input) },
12770
- streamParams: {
12771
- getOptions: (input) => dispatch("stream-params", "streamParams", "getOptions", "query", input),
12772
- setProfile: (input) => dispatch("stream-params", "streamParams", "setProfile", "mutation", input),
12773
- getConfigSchema: (input) => dispatch("stream-params", "streamParams", "getConfigSchema", "query", input),
12774
- getStatus: (input) => dispatch("stream-params", "streamParams", "getStatus", "query", input)
12775
- },
12776
- switch: {
12777
- setState: (input) => dispatch("switch", "switch", "setState", "mutation", input),
12778
- getStatus: (input) => dispatch("switch", "switch", "getStatus", "query", input)
12779
- },
12780
- tamper: { getStatus: (input) => dispatch("tamper", "tamper", "getStatus", "query", input) },
12781
- temperatureSensor: { getStatus: (input) => dispatch("temperature-sensor", "temperatureSensor", "getStatus", "query", input) },
12782
- update: {
12783
- installUpdate: (input) => dispatch("update", "update", "installUpdate", "mutation", input),
12784
- getStatus: (input) => dispatch("update", "update", "getStatus", "query", input)
12785
- },
12786
- vacuumControl: {
12787
- start: (input) => dispatch("vacuum-control", "vacuumControl", "start", "mutation", input),
12788
- pause: (input) => dispatch("vacuum-control", "vacuumControl", "pause", "mutation", input),
12789
- stop: (input) => dispatch("vacuum-control", "vacuumControl", "stop", "mutation", input),
12790
- returnToBase: (input) => dispatch("vacuum-control", "vacuumControl", "returnToBase", "mutation", input),
12791
- locate: (input) => dispatch("vacuum-control", "vacuumControl", "locate", "mutation", input),
12792
- setFanSpeed: (input) => dispatch("vacuum-control", "vacuumControl", "setFanSpeed", "mutation", input),
12793
- getStatus: (input) => dispatch("vacuum-control", "vacuumControl", "getStatus", "query", input)
12794
- },
12795
- valve: {
12796
- open: (input) => dispatch("valve", "valve", "open", "mutation", input),
12797
- close: (input) => dispatch("valve", "valve", "close", "mutation", input),
12798
- stop: (input) => dispatch("valve", "valve", "stop", "mutation", input),
12799
- setPosition: (input) => dispatch("valve", "valve", "setPosition", "mutation", input),
12800
- getStatus: (input) => dispatch("valve", "valve", "getStatus", "query", input)
12801
- },
12802
- vibration: { getStatus: (input) => dispatch("vibration", "vibration", "getStatus", "query", input) },
12803
- videoclips: {
12804
- listClips: (input) => dispatch("videoclips", "videoclips", "listClips", "query", input),
12805
- getClipPlayback: (input) => dispatch("videoclips", "videoclips", "getClipPlayback", "query", input)
12806
- },
12807
- waterHeater: {
12808
- setTargetTemp: (input) => dispatch("water-heater", "waterHeater", "setTargetTemp", "mutation", input),
12809
- setOperationMode: (input) => dispatch("water-heater", "waterHeater", "setOperationMode", "mutation", input),
12810
- setAway: (input) => dispatch("water-heater", "waterHeater", "setAway", "mutation", input),
12811
- getStatus: (input) => dispatch("water-heater", "waterHeater", "getStatus", "query", input)
12812
- },
12813
- weather: { getStatus: (input) => dispatch("weather", "weather", "getStatus", "query", input) },
12814
- webrtcSession: {
12815
- listStreams: (input) => dispatch("webrtc-session", "webrtcSession", "listStreams", "query", input),
12816
- createSession: (input) => dispatch("webrtc-session", "webrtcSession", "createSession", "mutation", input),
12817
- handleOffer: (input) => dispatch("webrtc-session", "webrtcSession", "handleOffer", "mutation", input),
12818
- handleAnswer: (input) => dispatch("webrtc-session", "webrtcSession", "handleAnswer", "mutation", input),
12819
- addIceCandidate: (input) => dispatch("webrtc-session", "webrtcSession", "addIceCandidate", "mutation", input),
12820
- getIceCandidates: (input) => dispatch("webrtc-session", "webrtcSession", "getIceCandidates", "query", input),
12821
- closeSession: (input) => dispatch("webrtc-session", "webrtcSession", "closeSession", "mutation", input),
12822
- hasAdaptiveBitrate: (input) => dispatch("webrtc-session", "webrtcSession", "hasAdaptiveBitrate", "query", input),
12823
- getSessionState: (input) => dispatch("webrtc-session", "webrtcSession", "getSessionState", "query", input)
12824
- },
12825
- zoneAnalytics: {
12826
- getCurrentSnapshot: (input) => dispatch("zone-analytics", "zoneAnalytics", "getCurrentSnapshot", "query", input),
12827
- getZoneHistory: (input) => dispatch("zone-analytics", "zoneAnalytics", "getZoneHistory", "query", input),
12828
- getCameraHistory: (input) => dispatch("zone-analytics", "zoneAnalytics", "getCameraHistory", "query", input),
12829
- getUnzonedHistory: (input) => dispatch("zone-analytics", "zoneAnalytics", "getUnzonedHistory", "query", input)
12830
- },
12831
- zoneRules: {
12832
- listRules: (input) => dispatch("zone-rules", "zoneRules", "listRules", "query", input),
12833
- setRules: (input) => dispatch("zone-rules", "zoneRules", "setRules", "mutation", input)
12834
- },
12835
- zones: {
12836
- listZones: (input) => dispatch("zones", "zones", "listZones", "query", input),
12837
- addZone: (input) => dispatch("zones", "zones", "addZone", "mutation", input),
12838
- removeZone: (input) => dispatch("zones", "zones", "removeZone", "mutation", input),
12839
- updateZone: (input) => dispatch("zones", "zones", "updateZone", "mutation", input)
12840
- },
12841
- addonSettings: {
12842
- getDeviceSettings: (input) => dispatchSystem("addonSettings", "getDeviceSettings", "query", input),
12843
- updateDeviceSettings: (input) => dispatchSystem("addonSettings", "updateDeviceSettings", "mutation", input)
12844
- },
12845
- cameraPipelineConfig: {
12846
- getDeviceSettingsContribution: (input) => dispatchSystem("cameraPipelineConfig", "getDeviceSettingsContribution", "query", input),
12847
- getDeviceLiveContribution: (input) => dispatchSystem("cameraPipelineConfig", "getDeviceLiveContribution", "query", input),
12848
- applyDeviceSettingsPatch: (input) => dispatchSystem("cameraPipelineConfig", "applyDeviceSettingsPatch", "mutation", input)
12849
- },
12850
- deviceAdoption: { getStatus: (input) => dispatchSystem("deviceAdoption", "getStatus", "query", input) },
12851
- deviceExport: {
12852
- getDeviceSettingsContribution: (input) => dispatchSystem("deviceExport", "getDeviceSettingsContribution", "query", input),
12853
- getDeviceLiveContribution: (input) => dispatchSystem("deviceExport", "getDeviceLiveContribution", "query", input),
12854
- applyDeviceSettingsPatch: (input) => dispatchSystem("deviceExport", "applyDeviceSettingsPatch", "mutation", input)
12855
- },
12856
- deviceManager: {
12857
- loadConfig: (input) => dispatchSystem("deviceManager", "loadConfig", "query", input),
12858
- loadRuntimeState: (input) => dispatchSystem("deviceManager", "loadRuntimeState", "query", input),
12859
- loadMeta: (input) => dispatchSystem("deviceManager", "loadMeta", "query", input),
12860
- setName: (input) => dispatchSystem("deviceManager", "setName", "mutation", input),
12861
- setLocation: (input) => dispatchSystem("deviceManager", "setLocation", "mutation", input),
12862
- setType: (input) => dispatchSystem("deviceManager", "setType", "mutation", input),
12863
- setIntegrationId: (input) => dispatchSystem("deviceManager", "setIntegrationId", "mutation", input),
12864
- setLinkDeviceId: (input) => dispatchSystem("deviceManager", "setLinkDeviceId", "mutation", input),
12865
- setPrimaryChildEntityId: (input) => dispatchSystem("deviceManager", "setPrimaryChildEntityId", "mutation", input),
12866
- setChildLayout: (input) => dispatchSystem("deviceManager", "setChildLayout", "mutation", input),
12867
- setDeviceLinks: (input) => dispatchSystem("deviceManager", "setDeviceLinks", "mutation", input),
12868
- getWireableFields: (input) => dispatchSystem("deviceManager", "getWireableFields", "query", input),
12869
- setRole: (input) => dispatchSystem("deviceManager", "setRole", "mutation", input),
12870
- applyInitialMeta: (input) => dispatchSystem("deviceManager", "applyInitialMeta", "mutation", input),
12871
- setMetadata: (input) => dispatchSystem("deviceManager", "setMetadata", "mutation", input),
12872
- setDisabled: (input) => dispatchSystem("deviceManager", "setDisabled", "mutation", input),
12873
- getDevice: (input) => dispatchSystem("deviceManager", "getDevice", "query", input),
12874
- getStreamSources: (input) => dispatchSystem("deviceManager", "getStreamSources", "query", input),
12875
- getConfigSchema: (input) => dispatchSystem("deviceManager", "getConfigSchema", "query", input),
12876
- getSettingsSchema: (input) => dispatchSystem("deviceManager", "getSettingsSchema", "query", input),
12877
- updateConfig: (input) => dispatchSystem("deviceManager", "updateConfig", "mutation", input),
12878
- enable: (input) => dispatchSystem("deviceManager", "enable", "mutation", input),
12879
- disable: (input) => dispatchSystem("deviceManager", "disable", "mutation", input),
12880
- remove: (input) => dispatchSystem("deviceManager", "remove", "mutation", input),
12881
- getStreamProfileMap: (input) => dispatchSystem("deviceManager", "getStreamProfileMap", "query", input),
12882
- setStreamProfileMap: (input) => dispatchSystem("deviceManager", "setStreamProfileMap", "mutation", input),
12883
- probeStreams: (input) => dispatchSystem("deviceManager", "probeStreams", "mutation", input),
12884
- getBindings: (input) => dispatchSystem("deviceManager", "getBindings", "query", input),
12885
- getAllBindings: (input) => dispatchSystem("deviceManager", "getAllBindings", "query", input),
12886
- setWrapperActive: (input) => dispatchSystem("deviceManager", "setWrapperActive", "mutation", input),
12887
- getDeviceSettingsAggregate: (input) => dispatchSystem("deviceManager", "getDeviceSettingsAggregate", "query", input),
12888
- getDeviceLiveInfoAggregate: (input) => dispatchSystem("deviceManager", "getDeviceLiveInfoAggregate", "query", input),
12889
- getDeviceAggregate: (input) => dispatchSystem("deviceManager", "getDeviceAggregate", "query", input),
12890
- runDeviceAction: (input) => dispatchSystem("deviceManager", "runDeviceAction", "mutation", input),
12891
- updateDeviceField: (input) => dispatchSystem("deviceManager", "updateDeviceField", "mutation", input),
12892
- updateDeviceFieldsBatch: (input) => dispatchSystem("deviceManager", "updateDeviceFieldsBatch", "mutation", input),
12893
- testField: (input) => dispatchSystem("deviceManager", "testField", "mutation", input),
12894
- getDeviceStatusAggregate: (input) => dispatchSystem("deviceManager", "getDeviceStatusAggregate", "query", input)
12895
- },
12896
- deviceState: {
12897
- getSnapshot: (input) => dispatchSystem("deviceState", "getSnapshot", "query", input),
12898
- getCapSlice: (input) => dispatchSystem("deviceState", "getCapSlice", "query", input),
12899
- setCapSlice: (input) => dispatchSystem("deviceState", "setCapSlice", "mutation", input)
12900
- },
12901
- faceGallery: { getFaceByTrack: (input) => dispatchSystem("faceGallery", "getFaceByTrack", "query", input) },
12902
- networkQuality: {
12903
- getDeviceStats: (input) => dispatchSystem("networkQuality", "getDeviceStats", "query", input),
12904
- reportClientStats: (input) => dispatchSystem("networkQuality", "reportClientStats", "mutation", input)
12905
- },
12906
- pipelineExecutor: {
12907
- runPipeline: (input) => dispatchSystem("pipelineExecutor", "runPipeline", "mutation", input),
12908
- runPipelineBatch: (input) => dispatchSystem("pipelineExecutor", "runPipelineBatch", "mutation", input)
12909
- },
12910
- pipelineOrchestrator: {
12911
- assignPipeline: (input) => dispatchSystem("pipelineOrchestrator", "assignPipeline", "mutation", input),
12912
- unassignPipeline: (input) => dispatchSystem("pipelineOrchestrator", "unassignPipeline", "mutation", input),
12913
- getPipelineAssignment: (input) => dispatchSystem("pipelineOrchestrator", "getPipelineAssignment", "query", input),
12914
- getCameraMetrics: (input) => dispatchSystem("pipelineOrchestrator", "getCameraMetrics", "query", input),
12915
- assignDecoder: (input) => dispatchSystem("pipelineOrchestrator", "assignDecoder", "mutation", input),
12916
- unassignDecoder: (input) => dispatchSystem("pipelineOrchestrator", "unassignDecoder", "mutation", input),
12917
- assignAudio: (input) => dispatchSystem("pipelineOrchestrator", "assignAudio", "mutation", input),
12918
- unassignAudio: (input) => dispatchSystem("pipelineOrchestrator", "unassignAudio", "mutation", input),
12919
- getAudioAssignment: (input) => dispatchSystem("pipelineOrchestrator", "getAudioAssignment", "query", input),
12920
- getAudioAssignments: (input) => dispatchSystem("pipelineOrchestrator", "getAudioAssignments", "query", input),
12921
- getDecoderAssignment: (input) => dispatchSystem("pipelineOrchestrator", "getDecoderAssignment", "query", input),
12922
- getCameraSettings: (input) => dispatchSystem("pipelineOrchestrator", "getCameraSettings", "query", input),
12923
- setCameraStepToggle: (input) => dispatchSystem("pipelineOrchestrator", "setCameraStepToggle", "mutation", input),
12924
- getCameraStepOverrides: (input) => dispatchSystem("pipelineOrchestrator", "getCameraStepOverrides", "query", input),
12925
- setCameraStepOverride: (input) => dispatchSystem("pipelineOrchestrator", "setCameraStepOverride", "mutation", input),
12926
- setCameraPipelineForAgent: (input) => dispatchSystem("pipelineOrchestrator", "setCameraPipelineForAgent", "mutation", input),
12927
- resolvePipeline: (input) => dispatchSystem("pipelineOrchestrator", "resolvePipeline", "query", input),
12928
- getDeviceSettingsContribution: (input) => dispatchSystem("pipelineOrchestrator", "getDeviceSettingsContribution", "query", input),
12929
- getDeviceLiveContribution: (input) => dispatchSystem("pipelineOrchestrator", "getDeviceLiveContribution", "query", input),
12930
- applyDeviceSettingsPatch: (input) => dispatchSystem("pipelineOrchestrator", "applyDeviceSettingsPatch", "mutation", input)
12931
- },
12932
- pipelineRunner: {
12933
- detachCamera: (input) => dispatchSystem("pipelineRunner", "detachCamera", "mutation", input),
12934
- getCameraMetrics: (input) => dispatchSystem("pipelineRunner", "getCameraMetrics", "query", input)
12935
- },
12936
- plateGallery: {
12937
- listPlates: (input) => dispatchSystem("plateGallery", "listPlates", "query", input),
12938
- getPlateByTrack: (input) => dispatchSystem("plateGallery", "getPlateByTrack", "query", input)
12939
- },
12940
- recording: {
12941
- getAvailability: (input) => dispatchSystem("recording", "getAvailability", "query", input),
12942
- getDaysWithRecordings: (input) => dispatchSystem("recording", "getDaysWithRecordings", "query", input),
12943
- getPlaybackManifest: (input) => dispatchSystem("recording", "getPlaybackManifest", "query", input),
12944
- getDeviceConfig: (input) => dispatchSystem("recording", "getDeviceConfig", "query", input),
12945
- locateSegment: (input) => dispatchSystem("recording", "locateSegment", "query", input),
12946
- readSegmentBytes: (input) => dispatchSystem("recording", "readSegmentBytes", "query", input),
12947
- setDeviceConfig: (input) => dispatchSystem("recording", "setDeviceConfig", "mutation", input),
12948
- rescanStorage: (input) => dispatchSystem("recording", "rescanStorage", "mutation", input),
12949
- pruneFootage: (input) => dispatchSystem("recording", "pruneFootage", "mutation", input),
12950
- getStatus: (input) => dispatchSystem("recording", "getStatus", "query", input),
12951
- getDeviceSettingsContribution: (input) => dispatchSystem("recording", "getDeviceSettingsContribution", "query", input),
12952
- getDeviceLiveContribution: (input) => dispatchSystem("recording", "getDeviceLiveContribution", "query", input),
12953
- applyDeviceSettingsPatch: (input) => dispatchSystem("recording", "applyDeviceSettingsPatch", "mutation", input)
12954
- },
12955
- snapshotProvider: {
12956
- supportsDevice: (input) => dispatchSystem("snapshotProvider", "supportsDevice", "query", input),
12957
- getSnapshot: (input) => dispatchSystem("snapshotProvider", "getSnapshot", "query", input)
12958
- },
12959
- streamBroker: {
12960
- publishCameraStream: (input) => dispatchSystem("streamBroker", "publishCameraStream", "mutation", input),
12961
- retractCameraStream: (input) => dispatchSystem("streamBroker", "retractCameraStream", "mutation", input),
12962
- assignProfile: (input) => dispatchSystem("streamBroker", "assignProfile", "mutation", input),
12963
- unassignProfile: (input) => dispatchSystem("streamBroker", "unassignProfile", "mutation", input),
12964
- restartProfile: (input) => dispatchSystem("streamBroker", "restartProfile", "mutation", input),
12965
- getDeviceSettingsContribution: (input) => dispatchSystem("streamBroker", "getDeviceSettingsContribution", "query", input),
12966
- getDeviceLiveContribution: (input) => dispatchSystem("streamBroker", "getDeviceLiveContribution", "query", input),
12967
- applyDeviceSettingsPatch: (input) => dispatchSystem("streamBroker", "applyDeviceSettingsPatch", "mutation", input)
9592
+ * segment resolves to an array it is replaced by a fresh object, so the array
9593
+ * is discarded — the only caller (`mergeLinkedStatus`) re-validates the result
9594
+ * with the target cap's Zod schema and rejects a malformed overlay.
9595
+ * The return type `T` preserves the OUTER shape; the rewritten key is not
9596
+ * narrowed, so callers should re-validate against the status schema. */
9597
+ function setByPath(obj, path, value) {
9598
+ const dotIndex = path.indexOf(".");
9599
+ if (dotIndex === -1) return {
9600
+ ...obj,
9601
+ [path]: value
9602
+ };
9603
+ const head = path.slice(0, dotIndex);
9604
+ const tail = path.slice(dotIndex + 1);
9605
+ const child = obj[head];
9606
+ const childObj = isRecord(child) ? child : {};
9607
+ return {
9608
+ ...obj,
9609
+ [head]: setByPath(childObj, tail, value)
9610
+ };
9611
+ }
9612
+ //#endregion
9613
+ //#region src/device/device-link-transform.ts
9614
+ /** Apply a link transform to a resolved source value. `identity`/undefined
9615
+ * pass through; `enum-map` remaps strings (fallback or null when unknown);
9616
+ * `linear` does scale*x+offset with optional clamp (null for non-numbers). */
9617
+ function applyTransform(value, transform) {
9618
+ if (!transform || transform.kind === "identity") return value;
9619
+ if (transform.kind === "enum-map") {
9620
+ const key = String(value);
9621
+ if (Object.prototype.hasOwnProperty.call(transform.mapping, key)) return transform.mapping[key];
9622
+ return transform.fallback ?? null;
9623
+ }
9624
+ if (transform.kind === "linear") {
9625
+ if (typeof value !== "number" || Number.isNaN(value)) return null;
9626
+ let out = value * transform.scale + transform.offset;
9627
+ if (transform.clamp) {
9628
+ const min = transform.clamp[0];
9629
+ const max = transform.clamp[1];
9630
+ out = Math.min(max, Math.max(min, out));
12968
9631
  }
9632
+ return out;
9633
+ }
9634
+ return value;
9635
+ }
9636
+ //#endregion
9637
+ //#region src/device/schema-fields.ts
9638
+ /** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
9639
+ * Zod v4 exposes `.unwrap()` on all three wrapper classes. */
9640
+ function unwrap(schema) {
9641
+ let s = schema;
9642
+ while (s instanceof z.ZodNullable || s instanceof z.ZodOptional || s instanceof z.ZodDefault) s = s.unwrap();
9643
+ return s;
9644
+ }
9645
+ function leafKind(schema) {
9646
+ const s = unwrap(schema);
9647
+ if (s instanceof z.ZodEnum) return {
9648
+ kind: "enum",
9649
+ enumValues: s.options
12969
9650
  };
9651
+ if (s instanceof z.ZodNumber) return { kind: "number" };
9652
+ if (s instanceof z.ZodString) return { kind: "string" };
9653
+ if (s instanceof z.ZodBoolean) return { kind: "boolean" };
9654
+ return null;
9655
+ }
9656
+ /** Walk a cap status schema into flat wireable leaf fields (dotted paths).
9657
+ * Recurses into nested ZodObject (unwrapping nullable/optional/default first),
9658
+ * so fields with null live values are still offered. Arrays / unknown shapes
9659
+ * are skipped — never throws. */
9660
+ function enumerateSchemaFields(schema, prefix = "") {
9661
+ const root = unwrap(schema);
9662
+ if (!(root instanceof z.ZodObject)) return [];
9663
+ const out = [];
9664
+ const shape = root.shape;
9665
+ for (const [key, field] of Object.entries(shape)) {
9666
+ const path = prefix ? `${prefix}.${key}` : key;
9667
+ const inner = unwrap(field);
9668
+ if (inner instanceof z.ZodObject) {
9669
+ out.push(...enumerateSchemaFields(inner, path));
9670
+ continue;
9671
+ }
9672
+ const leaf = leafKind(field);
9673
+ if (leaf) out.push({
9674
+ path,
9675
+ kind: leaf.kind,
9676
+ ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
9677
+ });
9678
+ }
9679
+ return out;
12970
9680
  }
12971
9681
  //#endregion
12972
9682
  //#region src/utils/zone-rule-eval.ts
@@ -13292,7 +10002,8 @@ function createSystemProxy(api) {
13292
10002
  listAddonInstances: (input) => dispatch("metricsProvider", "listAddonInstances", "query", input),
13293
10003
  getAddonStats: (input) => dispatch("metricsProvider", "getAddonStats", "query", input),
13294
10004
  listNodeProcesses: (input) => dispatch("metricsProvider", "listNodeProcesses", "query", input),
13295
- killProcess: (input) => dispatch("metricsProvider", "killProcess", "mutation", input)
10005
+ killProcess: (input) => dispatch("metricsProvider", "killProcess", "mutation", input),
10006
+ dumpHeapSnapshot: (input) => dispatch("metricsProvider", "dumpHeapSnapshot", "mutation", input)
13296
10007
  },
13297
10008
  mqttBroker: {
13298
10009
  listBrokers: (input) => dispatch("mqttBroker", "listBrokers", "query", input),
@@ -13336,6 +10047,7 @@ function createSystemProxy(api) {
13336
10047
  getSelectedEngine: (input) => dispatch("pipelineExecutor", "getSelectedEngine", "query", input),
13337
10048
  getDefaultSteps: (input) => dispatch("pipelineExecutor", "getDefaultSteps", "query", input),
13338
10049
  reprobeEngine: (input) => dispatch("pipelineExecutor", "reprobeEngine", "mutation", input),
10050
+ getEngineProvisioning: (input) => dispatch("pipelineExecutor", "getEngineProvisioning", "query", input),
13339
10051
  getVideoPipelineSteps: (input) => dispatch("pipelineExecutor", "getVideoPipelineSteps", "query", input),
13340
10052
  setVideoPipelineSteps: (input) => dispatch("pipelineExecutor", "setVideoPipelineSteps", "mutation", input),
13341
10053
  getSchema: (input) => dispatch("pipelineExecutor", "getSchema", "query", input),
@@ -13379,6 +10091,8 @@ function createSystemProxy(api) {
13379
10091
  listAgentSettings: (input) => dispatch("pipelineOrchestrator", "listAgentSettings", "query", input),
13380
10092
  setAgentAddonDefaults: (input) => dispatch("pipelineOrchestrator", "setAgentAddonDefaults", "mutation", input),
13381
10093
  removeAgentSettings: (input) => dispatch("pipelineOrchestrator", "removeAgentSettings", "mutation", input),
10094
+ setAgentMaxCameras: (input) => dispatch("pipelineOrchestrator", "setAgentMaxCameras", "mutation", input),
10095
+ getCameraStatuses: (input) => dispatch("pipelineOrchestrator", "getCameraStatuses", "query", input),
13382
10096
  listTemplates: (input) => dispatch("pipelineOrchestrator", "listTemplates", "query", input),
13383
10097
  saveTemplate: (input) => dispatch("pipelineOrchestrator", "saveTemplate", "mutation", input),
13384
10098
  updateTemplate: (input) => dispatch("pipelineOrchestrator", "updateTemplate", "mutation", input),
@@ -15053,20 +11767,6 @@ var logDestinationCapability = {
15053
11767
  }
15054
11768
  };
15055
11769
  //#endregion
15056
- //#region src/capabilities/admin-ui.cap.ts
15057
- var StaticDirOutputSchema = z.object({ staticDir: z.string() });
15058
- var VersionOutputSchema = z.object({ version: z.string() });
15059
- var adminUiCapability = {
15060
- name: "admin-ui",
15061
- scope: "system",
15062
- mode: "singleton",
15063
- internal: true,
15064
- methods: {
15065
- getStaticDir: method(z.void(), StaticDirOutputSchema),
15066
- getVersion: method(z.void(), VersionOutputSchema)
15067
- }
15068
- };
15069
- //#endregion
15070
11770
  //#region src/schemas/auth-records.ts
15071
11771
  /**
15072
11772
  * Zod schemas for persisted record types.
@@ -16994,7 +13694,10 @@ var AgentAddonConfigSchema = z.object({
16994
13694
  modelId: z.string(),
16995
13695
  settings: z.record(z.string(), z.unknown()).readonly()
16996
13696
  });
16997
- var AgentPipelineSettingsSchema = z.object({ addonDefaults: z.record(z.string(), AgentAddonConfigSchema).readonly() });
13697
+ var AgentPipelineSettingsSchema = z.object({
13698
+ addonDefaults: z.record(z.string(), AgentAddonConfigSchema).readonly(),
13699
+ maxCameras: z.number().int().nonnegative().nullable().default(null)
13700
+ });
16998
13701
  var CameraPipelineForAgentSchema = z.object({
16999
13702
  steps: z.array(PipelineStepInputSchema).readonly(),
17000
13703
  audio: z.object({
@@ -17096,6 +13799,141 @@ var GlobalMetricsSchema = z.object({
17096
13799
  * capability providers.
17097
13800
  */
17098
13801
  var CapabilityBindingsSchema = z.record(z.string(), z.string());
13802
+ /** Stream entry surfaced from the stream-catalog for one device. */
13803
+ var CameraSourceStreamSchema = z.object({
13804
+ camStreamId: z.string(),
13805
+ codec: z.string(),
13806
+ width: z.number(),
13807
+ height: z.number(),
13808
+ fps: z.number(),
13809
+ kind: z.string()
13810
+ });
13811
+ /** Source block — always present; derives from the stream catalog. */
13812
+ var CameraSourceStatusSchema = z.object({ streams: z.array(CameraSourceStreamSchema).readonly() });
13813
+ /** Assignment block — always present (orchestrator-local, no remote call). */
13814
+ var CameraAssignmentStatusSchema = z.object({
13815
+ detectionNodeId: z.string().nullable(),
13816
+ decoderNodeId: z.string().nullable(),
13817
+ audioNodeId: z.string().nullable(),
13818
+ pinned: z.object({
13819
+ detection: z.boolean(),
13820
+ decoder: z.boolean(),
13821
+ audio: z.boolean()
13822
+ }),
13823
+ reasons: z.object({
13824
+ detection: z.string().optional(),
13825
+ decoder: z.string().optional(),
13826
+ audio: z.string().optional()
13827
+ })
13828
+ });
13829
+ /** Broker profile slot entry within the broker block. */
13830
+ var CameraBrokerProfileSchema = z.object({
13831
+ profile: z.string(),
13832
+ status: z.string(),
13833
+ codec: z.string(),
13834
+ width: z.number(),
13835
+ height: z.number(),
13836
+ subscribers: z.number(),
13837
+ inFps: z.number(),
13838
+ outFps: z.number()
13839
+ });
13840
+ /** Broker block — null when the broker stage is unreachable or inactive. */
13841
+ var CameraBrokerStatusSchema = z.object({
13842
+ profiles: z.array(CameraBrokerProfileSchema).readonly(),
13843
+ webrtcSessions: z.number(),
13844
+ rtspRestream: z.boolean()
13845
+ });
13846
+ /** Shared-memory ring statistics within the decoder block. */
13847
+ var CameraDecoderShmSchema = z.object({
13848
+ framesWritten: z.number(),
13849
+ getFrameHits: z.number(),
13850
+ getFrameMisses: z.number(),
13851
+ budgetMb: z.number()
13852
+ });
13853
+ /** Decoder block — null when the decoder stage is unreachable or inactive. */
13854
+ var CameraDecoderStatusSchema = z.object({
13855
+ nodeId: z.string(),
13856
+ formats: z.array(z.string()).readonly(),
13857
+ sessionCount: z.number(),
13858
+ shm: CameraDecoderShmSchema
13859
+ });
13860
+ /** Motion block — null when motion detection is not active for this device. */
13861
+ var CameraMotionStatusSchema = z.object({
13862
+ enabled: z.boolean(),
13863
+ fps: z.number()
13864
+ });
13865
+ /** Detection engine provisioning state (mirrors pipeline-executor provisioning states). */
13866
+ var CameraDetectionProvisioningStateSchema = z.enum([
13867
+ "idle",
13868
+ "installing",
13869
+ "verifying",
13870
+ "ready",
13871
+ "failed"
13872
+ ]);
13873
+ /** Detection provisioning sub-block. */
13874
+ var CameraDetectionProvisioningSchema = z.object({
13875
+ state: CameraDetectionProvisioningStateSchema,
13876
+ error: z.string().optional()
13877
+ });
13878
+ /** Detection phase — derived from the runner's engine phase. */
13879
+ var CameraDetectionPhaseSchema = z.enum([
13880
+ "idle",
13881
+ "watching",
13882
+ "active"
13883
+ ]);
13884
+ /** Detection block — null when no detection node is assigned or reachable. */
13885
+ var CameraDetectionStatusSchema = z.object({
13886
+ nodeId: z.string(),
13887
+ engine: z.object({
13888
+ backend: z.string(),
13889
+ device: z.string()
13890
+ }),
13891
+ phase: CameraDetectionPhaseSchema,
13892
+ configuredFps: z.number(),
13893
+ actualFps: z.number(),
13894
+ queueDepth: z.number(),
13895
+ avgInferenceMs: z.number(),
13896
+ provisioning: CameraDetectionProvisioningSchema
13897
+ });
13898
+ /** Audio block — null when no audio node is assigned or reachable. */
13899
+ var CameraAudioStatusSchema = z.object({
13900
+ nodeId: z.string(),
13901
+ enabled: z.boolean()
13902
+ });
13903
+ /** Recording mode enum (matches recording cap). */
13904
+ var CameraRecordingModeSchema = z.enum([
13905
+ "off",
13906
+ "continuous",
13907
+ "events"
13908
+ ]);
13909
+ /** Recording block — null when no recording cap is active for this device. */
13910
+ var CameraRecordingStatusSchema = z.object({
13911
+ mode: CameraRecordingModeSchema,
13912
+ active: z.boolean(),
13913
+ storageBytes: z.number()
13914
+ });
13915
+ /**
13916
+ * Aggregated per-camera pipeline status — server-composed, single call.
13917
+ *
13918
+ * The `assignment` and `source` blocks are always present.
13919
+ * Every other block is `null` when the stage is inactive or unreachable
13920
+ * during the bounded parallel fan-out in the orchestrator implementation.
13921
+ *
13922
+ * See spec: `docs/superpowers/specs/2026-06-24-camera-status-aggregator-cap.md`
13923
+ */
13924
+ var CameraStatusSchema = z.object({
13925
+ deviceId: z.number(),
13926
+ assignment: CameraAssignmentStatusSchema,
13927
+ source: CameraSourceStatusSchema,
13928
+ broker: CameraBrokerStatusSchema.nullable(),
13929
+ decoder: CameraDecoderStatusSchema.nullable(),
13930
+ motion: CameraMotionStatusSchema.nullable(),
13931
+ detection: CameraDetectionStatusSchema.nullable(),
13932
+ audio: CameraAudioStatusSchema.nullable(),
13933
+ recording: CameraRecordingStatusSchema.nullable(),
13934
+ /** Unix timestamp (ms) when this snapshot was composed server-side. */
13935
+ fetchedAt: z.number()
13936
+ });
17099
13937
  /**
17100
13938
  * Pipeline Orchestrator capability — global load balancer + camera dispatcher.
17101
13939
  *
@@ -17272,6 +14110,25 @@ var pipelineOrchestratorCapability = {
17272
14110
  kind: "mutation",
17273
14111
  auth: "admin"
17274
14112
  }),
14113
+ /**
14114
+ * Set the per-node camera cap for one agent.
14115
+ *
14116
+ * `maxCameras: 0` and `maxCameras: null` are both treated as unlimited
14117
+ * by the load balancer (0 is stored as-is; the balancer treats ≤0 as
14118
+ * unlimited alongside null). Pass `null` to clear an existing cap.
14119
+ * Note: the admin UI normalises 0→null before calling this method, so
14120
+ * `maxCameras: 0` only reaches the store via direct SDK or CLI calls.
14121
+ * Changes take effect immediately on the next dispatch cycle — cameras
14122
+ * currently assigned over-cap are left in place (no forced eviction),
14123
+ * but new assignments obey the updated cap.
14124
+ */
14125
+ setAgentMaxCameras: method(z.object({
14126
+ agentNodeId: z.string(),
14127
+ maxCameras: z.number().int().nonnegative().nullable()
14128
+ }), z.object({ success: z.literal(true) }), {
14129
+ kind: "mutation",
14130
+ auth: "admin"
14131
+ }),
17275
14132
  /** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
17276
14133
  getCameraSettings: method(z.object({ deviceId: z.number() }), CameraPipelineSettingsSchema.nullable()),
17277
14134
  /** Set or clear the 3-state toggle for one (camera, addonId). Pass `enabled: null` to clear and revert to agent default. */
@@ -17312,6 +14169,29 @@ var pipelineOrchestratorCapability = {
17312
14169
  deviceId: z.number(),
17313
14170
  agentNodeId: z.string().optional()
17314
14171
  }), CameraPipelineConfigSchema),
14172
+ /**
14173
+ * Server-composed aggregated status for a single camera.
14174
+ *
14175
+ * Fans out in parallel (bounded, per-stage graceful degradation) to
14176
+ * broker / decoder / motion / detection / audio / recording source caps
14177
+ * via `ctx.api`. A stage whose source errors or times out becomes `null`
14178
+ * in the returned payload — one slow agent never breaks the whole call.
14179
+ *
14180
+ * Intended for "on open / on camera select" snapshots. Live deltas
14181
+ * keep arriving from the existing ~1Hz events the UI already subscribes to.
14182
+ */
14183
+ getCameraStatus: method(z.object({ deviceId: z.number() }), CameraStatusSchema),
14184
+ /**
14185
+ * Server-composed aggregated status for multiple cameras in one call.
14186
+ *
14187
+ * `deviceIds` defaults to all cameras currently tracked by the
14188
+ * orchestrator's assignment map when omitted.
14189
+ *
14190
+ * Runs per-device composition in parallel (bounded). Use this to
14191
+ * populate the cluster assignments table and the pipeline flow overview
14192
+ * rail without issuing N parallel browser round-trips.
14193
+ */
14194
+ getCameraStatuses: method(z.object({ deviceIds: z.array(z.number()).optional() }), z.array(CameraStatusSchema).readonly()),
17315
14195
  /** List every template the operator has saved. */
17316
14196
  listTemplates: method(z.void(), z.array(PipelineTemplateSchema).readonly()),
17317
14197
  /** Create a new named preset from a given CameraPipelineConfig. */
@@ -19245,7 +16125,20 @@ var DiskSpaceInfoSchema = z.object({
19245
16125
  var PidResourceStatsSchema = z.object({
19246
16126
  pid: z.number(),
19247
16127
  cpu: z.number(),
19248
- memory: z.number()
16128
+ memory: z.number(),
16129
+ /**
16130
+ * Private (anonymous) resident bytes — the per-process V8 heap + native
16131
+ * allocations NOT shared with other processes (Linux RssAnon). This is the
16132
+ * "real" per-runner cost; summing it across runners is meaningful, unlike
16133
+ * `memory` (RSS), which double-counts the shared mmap'd framework code.
16134
+ * Undefined where /proc is unavailable (e.g. macOS).
16135
+ */
16136
+ privateBytes: z.number().optional(),
16137
+ /**
16138
+ * Shared file-backed resident bytes (Linux RssFile) — mmap'd framework/lib
16139
+ * code shared copy-on-write across runners. Undefined on macOS.
16140
+ */
16141
+ sharedBytes: z.number().optional()
19249
16142
  });
19250
16143
  var AddonInstanceSchema = z.object({
19251
16144
  addonId: z.string(),
@@ -19294,6 +16187,17 @@ var KillProcessResultSchema = z.object({
19294
16187
  reason: z.string().optional(),
19295
16188
  signal: z.enum(["SIGTERM", "SIGKILL"]).optional()
19296
16189
  });
16190
+ var DumpHeapSnapshotInputSchema = z.object({
16191
+ /** The addon whose runner should dump a heap snapshot. */
16192
+ addonId: z.string() });
16193
+ var DumpHeapSnapshotResultSchema = z.object({
16194
+ success: z.boolean(),
16195
+ /** Path of the written .heapsnapshot inside the runner's container/host. */
16196
+ path: z.string().optional(),
16197
+ /** Process pid that was signalled. */
16198
+ pid: z.number().optional(),
16199
+ reason: z.string().optional()
16200
+ });
19297
16201
  var SystemMetricsSchema = z.object({
19298
16202
  cpuPercent: z.number(),
19299
16203
  memoryPercent: z.number(),
@@ -19354,6 +16258,17 @@ var metricsProviderCapability = {
19354
16258
  killProcess: method(KillProcessInputSchema, KillProcessResultSchema, {
19355
16259
  kind: "mutation",
19356
16260
  auth: "admin"
16261
+ }),
16262
+ /**
16263
+ * Tell the addon's forked runner to write a V8 heap snapshot to disk (via
16264
+ * SIGUSR2 — the runner's diagnostic handler). Also logs its
16265
+ * `process.memoryUsage()` + heap-space breakdown. Refuses pids not in the
16266
+ * live `listNodeProcesses()` snapshot. Use for deep per-addon memory
16267
+ * attribution; copy the returned path off the node to analyze.
16268
+ */
16269
+ dumpHeapSnapshot: method(DumpHeapSnapshotInputSchema, DumpHeapSnapshotResultSchema, {
16270
+ kind: "mutation",
16271
+ auth: "admin"
19357
16272
  })
19358
16273
  }
19359
16274
  };
@@ -19867,124 +16782,6 @@ var recordingCapability = {
19867
16782
  }
19868
16783
  };
19869
16784
  //#endregion
19870
- //#region src/capabilities/device-ops.cap.ts
19871
- /**
19872
- * device-ops — device-scoped cap that unifies the per-IDevice operations
19873
- * previously routed through the `.device-ops` Moleculer bridge service.
19874
- *
19875
- * Each worker that hosts live `IDevice` instances auto-registers a native
19876
- * provider for this cap (per device) backed by its local
19877
- * `DeviceRegistry`. Hub-side callers reach it transparently through
19878
- * `ctx.fetchDevice(id).deviceOps.*` — the DeviceProxy injects
19879
- * `deviceId` + `nodeId` and dispatches through the standard cap-router,
19880
- * so there's no parallel bridge path anymore.
19881
- *
19882
- * The surface is intentionally small — every method corresponds to a
19883
- * single action on the live `IDevice` (or `ICameraDevice` for
19884
- * `getStreamSources`). Richer orchestration (enable/disable with
19885
- * integration plumbing, bulk updates) stays in the `device-manager` cap;
19886
- * `device-ops` is the per-device primitive the device-manager routes to.
19887
- */
19888
- var StreamSourceEntrySchema$1 = z.object({
19889
- id: z.string(),
19890
- label: z.string(),
19891
- protocol: z.enum([
19892
- "rtsp",
19893
- "rtmp",
19894
- "annexb",
19895
- "http-mjpeg",
19896
- "webrtc",
19897
- "custom"
19898
- ]),
19899
- url: z.string().optional(),
19900
- resolution: z.object({
19901
- width: z.number(),
19902
- height: z.number()
19903
- }).optional(),
19904
- fps: z.number().optional(),
19905
- bitrate: z.number().optional(),
19906
- codec: z.string().optional(),
19907
- profileHint: CamProfileSchema.optional(),
19908
- sdp: z.string().optional()
19909
- });
19910
- var ConfigEntrySchema$1 = z.object({
19911
- key: z.string(),
19912
- value: z.unknown()
19913
- });
19914
- var RawStateResultSchema = z.object({
19915
- /** Originating provider id, e.g. 'homeassistant' | 'reolink' | 'hikvision'. */
19916
- source: z.string(),
19917
- /** Opaque, DISPLAY-SAFE upstream blob (no secrets/PII). */
19918
- data: z.record(z.string(), z.unknown())
19919
- });
19920
- var deviceOpsCapability = {
19921
- name: "device-ops",
19922
- scope: "device",
19923
- deviceNative: true,
19924
- mode: "singleton",
19925
- methods: {
19926
- /**
19927
- * Return stream sources for camera-like devices. Non-camera devices
19928
- * return an empty array (the bridge did the same; preserved for compat).
19929
- */
19930
- getStreamSources: method(z.object({ deviceId: z.number() }), z.array(StreamSourceEntrySchema$1)),
19931
- /**
19932
- * Return the device's config entries (key + current value). Used by
19933
- * the device-manager aggregator when reading the driver's schema+values.
19934
- */
19935
- getConfigEntries: method(z.object({ deviceId: z.number() }), z.array(ConfigEntrySchema$1)),
19936
- /**
19937
- * Bulk-apply a config patch via `IDevice.config.setAll`. Covers the
19938
- * updateConfig / setStreamProfileMap / enable-as-config paths from
19939
- * the old bridge.
19940
- */
19941
- setConfig: method(z.object({
19942
- deviceId: z.number(),
19943
- values: z.record(z.string(), z.unknown())
19944
- }), z.void(), { kind: "mutation" }),
19945
- /**
19946
- * Invoke a device custom action on a forked/remote device (the
19947
- * cross-process transport for `IDevice.runDeviceAction`). Mirrors
19948
- * `setConfig` — the device-manager calls this when the device is not
19949
- * hub-local.
19950
- */
19951
- runAction: method(z.object({
19952
- deviceId: z.number(),
19953
- action: z.string().min(1),
19954
- input: z.unknown()
19955
- }), z.unknown(), { kind: "mutation" }),
19956
- /**
19957
- * Invoke `IDevice.removeDevice()` so the driver can release resources
19958
- * (close sockets, stop background tasks, …). The device-manager still
19959
- * performs its own persistence cleanup before/after this call.
19960
- */
19961
- removeDevice: method(z.object({ deviceId: z.number() }), z.void(), { kind: "mutation" }),
19962
- /**
19963
- * Build the ConfigUISchema (FormBuilder input shape) from the device's
19964
- * Zod config schema. Runs on the worker that owns the IDevice so the
19965
- * Zod types stay local (they're function references, not
19966
- * serializable). Returns a fully JSON-serializable schema with
19967
- * sections/fields the admin UI renders directly.
19968
- *
19969
- * Needed because the hub-side `device-manager.getSettingsSchema`
19970
- * couldn't reach forked-worker devices — it had no registry entry
19971
- * and no cross-process lookup, so the UI silently rendered an empty
19972
- * settings panel for every worker-owned device.
19973
- *
19974
- * Returns `null` when the device isn't found on this worker.
19975
- */
19976
- getSettingsSchema: method(z.object({ deviceId: z.number() }), z.unknown().nullable()),
19977
- /**
19978
- * Opt-in: return the device's RAW upstream state (the provider's
19979
- * cached values) as a display-safe `{ source, data }` blob. Returns
19980
- * `null` when the device exposes no raw state — the State panel hides
19981
- * its Raw toggle in that case. One-shot (read the provider's existing
19982
- * cache; no upstream round-trip).
19983
- */
19984
- getRawState: method(z.object({ deviceId: z.number() }), RawStateResultSchema.nullable(), { auth: "protected" })
19985
- }
19986
- };
19987
- //#endregion
19988
16785
  //#region src/capabilities/camera-credentials.cap.ts
19989
16786
  /**
19990
16787
  * camera-credentials — device-scoped cap exposing the camera's network
@@ -25568,6 +22365,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
25568
22365
  addonId: null,
25569
22366
  access: "view"
25570
22367
  },
22368
+ "metricsProvider.dumpHeapSnapshot": {
22369
+ capName: "metrics-provider",
22370
+ capScope: "system",
22371
+ addonId: null,
22372
+ access: "create"
22373
+ },
25571
22374
  "metricsProvider.getAddonStats": {
25572
22375
  capName: "metrics-provider",
25573
22376
  capScope: "system",
@@ -26024,6 +22827,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
26024
22827
  addonId: null,
26025
22828
  access: "view"
26026
22829
  },
22830
+ "pipelineExecutor.getEngineProvisioning": {
22831
+ capName: "pipeline-executor",
22832
+ capScope: "system",
22833
+ addonId: null,
22834
+ access: "view"
22835
+ },
26027
22836
  "pipelineExecutor.getGlobalPipelineConfig": {
26028
22837
  capName: "pipeline-executor",
26029
22838
  capScope: "system",
@@ -26228,6 +23037,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
26228
23037
  addonId: null,
26229
23038
  access: "view"
26230
23039
  },
23040
+ "pipelineOrchestrator.getCameraStatus": {
23041
+ capName: "pipeline-orchestrator",
23042
+ capScope: "system",
23043
+ addonId: null,
23044
+ access: "view"
23045
+ },
23046
+ "pipelineOrchestrator.getCameraStatuses": {
23047
+ capName: "pipeline-orchestrator",
23048
+ capScope: "system",
23049
+ addonId: null,
23050
+ access: "view"
23051
+ },
26231
23052
  "pipelineOrchestrator.getCameraStepOverrides": {
26232
23053
  capName: "pipeline-orchestrator",
26233
23054
  capScope: "system",
@@ -26312,6 +23133,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
26312
23133
  addonId: null,
26313
23134
  access: "create"
26314
23135
  },
23136
+ "pipelineOrchestrator.setAgentMaxCameras": {
23137
+ capName: "pipeline-orchestrator",
23138
+ capScope: "system",
23139
+ addonId: null,
23140
+ access: "create"
23141
+ },
26315
23142
  "pipelineOrchestrator.setCameraPipelineForAgent": {
26316
23143
  capName: "pipeline-orchestrator",
26317
23144
  capScope: "system",
@@ -28372,59 +25199,6 @@ function cellsToRects(cells, gridWidth, gridHeight, maxRegions) {
28372
25199
  }));
28373
25200
  }
28374
25201
  //#endregion
28375
- //#region src/utils/sleep.ts
28376
- /**
28377
- * Promise-based timer helpers — used everywhere the codebase needs to
28378
- * wait, back off, or schedule a retry. Before these helpers landed, each
28379
- * call site re-implemented `new Promise(r => setTimeout(r, ms))` inline,
28380
- * with subtle variations (some swallowing cancellation, some not). Two
28381
- * shapes cover every observed use case:
28382
- *
28383
- * - {@link sleep} for a plain, uncancellable wait — the default choice.
28384
- * - {@link sleepCancellable} for a wait that wakes early when an
28385
- * abort signal trips, used by long-running pollers whose teardown
28386
- * must stop a pending backoff promptly.
28387
- */
28388
- /**
28389
- * Resolve after `ms` milliseconds. Never rejects, never cancels. The
28390
- * sleep cannot be interrupted; for a wakeable variant use
28391
- * {@link sleepCancellable}.
28392
- *
28393
- * `ms <= 0` resolves on the next microtask via `setTimeout(0)`, which
28394
- * still gives the event loop a chance to drain — useful for breaking
28395
- * up tight async loops without changing call-site semantics.
28396
- */
28397
- function sleep(ms) {
28398
- return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
28399
- }
28400
- /**
28401
- * Resolve after `ms` milliseconds OR when `signal.aborted` flips,
28402
- * whichever fires first. The returned promise never rejects — aborted
28403
- * sleeps resolve normally so callers can model "wait or wake" without
28404
- * try/catch noise.
28405
- *
28406
- * await sleepCancellable(backoffMs, lifecycle.abortSignal)
28407
- * if (lifecycle.aborted) return
28408
- *
28409
- * If `signal` is already aborted at call time the helper resolves on
28410
- * the next microtask.
28411
- */
28412
- function sleepCancellable(ms, signal) {
28413
- if (signal.aborted) return Promise.resolve();
28414
- return new Promise((resolve) => {
28415
- const timer = setTimeout(() => {
28416
- signal.removeEventListener("abort", onAbort);
28417
- resolve();
28418
- }, Math.max(0, ms));
28419
- const onAbort = () => {
28420
- clearTimeout(timer);
28421
- signal.removeEventListener("abort", onAbort);
28422
- resolve();
28423
- };
28424
- signal.addEventListener("abort", onAbort, { once: true });
28425
- });
28426
- }
28427
- //#endregion
28428
25202
  //#region src/helpers/bind-addon-actions.ts
28429
25203
  /**
28430
25204
  * Bind an addon's custom-action catalog to its tRPC surface, returning a
@@ -28450,4 +25224,4 @@ function bindAddonActions(api, addonId, catalog) {
28450
25224
  return out;
28451
25225
  }
28452
25226
  //#endregion
28453
- export { ACCESSORY_LABEL, ALL_CAPABILITY_DEFINITIONS, APPLE_SA_TO_MACRO, AUDIO_BACKEND_CHOICES, AUDIO_MACRO_LABELS, AccessoriesStatusSchema, AccessoryKind, AddBrokerInputSchema, AddonAutoUpdateSchema, AddonListItemSchema, AddonPageDeclarationSchema, AddonPageInfoSchema, AdoptInputSchema as AdoptionAdoptInputSchema, AdoptResultSchema as AdoptionAdoptResultSchema, AdoptionFilterSchema, GetCandidateInputSchema as AdoptionGetCandidateInputSchema, ListCandidatesInputSchema as AdoptionListCandidatesInputSchema, ListCandidatesOutputSchema as AdoptionListCandidatesOutputSchema, ReleaseInputSchema as AdoptionReleaseInputSchema, AdoptionStatusSchema, AgentLoadSummarySchema, AirQualitySensorStatusSchema, AlarmArmModeSchema, AlarmPanelStatusSchema, AlarmStateSchema, AlertSchema, AlertSeveritySchema, AlertSourceSchema, AlertStatusSchema, AmbientLightSensorStatusSchema, ApiKeyRecordSchema, ApiKeySummarySchema, ArchiveEntrySchema, ArchiveManifestSchema, AudioAnalysisResultSchema, AudioAnalysisSettingsSchema, AudioChunkInputSchema, AudioClassSummarySchema, AudioClassificationLabelSchema, AudioClassificationResultSchema, AudioCodecInfoSchema, AudioDecodeSessionConfigSchema, AudioEncodeSchema, AudioEncodeSessionConfigSchema, AudioEncodedChunkSchema, AudioEventSchema, AudioLevelSchema, AudioMetricsHistoryPointSchema, AudioMetricsHistorySchema, AudioMetricsSnapshotSchema, AudioPcmChunkSchema, AuthResultSchema, AutoUpdateSettingsSchema, AutomationControlStatusSchema, AvailableIntegrationTypeSchema, BACKEND_TO_FORMAT, BATTERY_DEVICE_PROFILE, BackupDestinationInfoSchema, BackupEntrySchema, BaseAddon, BaseDevice, BaseDeviceProvider, BatteryStatusSchema, BinaryStatusSchema, BoundingBoxSchema, BrightnessStatusSchema, AddInputSchema as BrokerAddInputSchema, BrokerAudioClientSchema, BrokerClientsSchema, BrokerConnectionDetailsSchema, BrokerConsumerAttributionSchema, BrokerConsumerKindSchema, BrokerDecodedClientSchema, BrokerEncodedClientSchema, GetStateInputSchema as BrokerGetStateInputSchema, BrokerInfoSchema, BrokerProviderInfoSchema, PublishInputSchema as BrokerPublishInputSchema, RegistryStatusSchema as BrokerRegistryStatusSchema, BrokerRtspClientSchema, BrokerStatsSchema, BrokerStatusEnum, BrokerStatusSchema, SubscribeInputSchema as BrokerSubscribeInputSchema, SubscribeResultSchema as BrokerSubscribeResultSchema, TestConnectionResultSchema as BrokerTestConnectionResultSchema, UnsubscribeInputSchema as BrokerUnsubscribeInputSchema, CAM_PROFILE_ORDER, CAPABILITY_NAMES, CAPABILITY_ROUTER_KEYS, CAP_NAMES_WITH_STATUS, CAP_PROVIDER_KIND_MAP, COCO_80_LABELS, COCO_TO_MACRO, CamProfileSchema, CamStreamDescriptorSchema, CamStreamKindSchema, CamStreamResolutionSchema, CameraCredentialsSchema, CameraCredentialsStatusSchema, CameraMetricsSchema, CameraMetricsWithDeviceIdSchema, CameraStreamSchema, CandidateQueryFilterSchema, CapScopeSchema, CapabilityBindingsSchema, CarbonMonoxideStatusSchema, ChargingStatus, ClientNetworkStatsSchema, ClimateControlStatusSchema, ClipPlaybackSchema, ClipSchema, ClusterAddonNodeDeploymentSchema, ClusterAddonStatusEntrySchema, CollectionColumnSchema, CollectionIndexSchema, ColorStatusSchema, ConfigEntrySchema, ConfigSectionWithValuesSchema, ConfigTabDeclarationSchema, ConnectivityStatusSchema, ConsumableItemSchema, ConsumablesStatusSchema, ContactStatusSchema, ControlKindSchema, ControlStatusSchema, CoverStateSchema, CoverStatusSchema, CreateApiKeyInputSchema, CreateApiKeyResultSchema, CreateIntegrationInputSchema, CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, CreateUserInputSchema, CustomActionInputSchema, DATAPLANE_SECRET_HEADER, DEFAULT_ADDON_PLACEMENT, DEFAULT_AUDIO_ANALYZER_CONFIG, DEFAULT_DECODER_HWACCEL_CONFIG, DEFAULT_FEATURES, DEFAULT_RETENTION, DEVICE_CAP_NAMES, DEVICE_PROFILES, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DEVICE_TYPE_INFO, DecodedAudioChunkSchema, DecodedFrameSchema, DecoderAssignmentSchema, DecoderSessionConfigSchema, DecoderStatsSchema, DeleteIntegrationResultSchema, DetectionSourceSchema, DetectorOutputSchema, DeviceConfig, DeviceDiscoveryStatusSchema, ExposeInputSchema as DeviceExportExposeInputSchema, DeviceExportStatusSchema, UnexposeInputSchema as DeviceExportUnexposeInputSchema, DeviceFeature, DeviceInfoSchema, DeviceNetworkStatsSchema, DeviceRole, DeviceRuntimeState, DeviceStatusSchema, DeviceType, DiscoveredChildDeviceSchema, DiscoveredChildStatusSchema, DiscoveredDeviceSchema, DisposerChain, DoorbellPressEventSchema, DoorbellStatusSchema, ElementConfigStore, EmbeddingInfoSchema, EmbeddingResultSchema, EncodeProfileSchema, EncodedPacketSchema, EnrichedWidgetMetadataSchema, EnumSensorDateTimeFormatSchema, EnumSensorStatusSchema, EventCategory, EventEmitterStatusSchema, EventFireSchema, EventItemSchema, EventKindSchema, EventSourceType, ExportSetupFieldSchema, ExportSetupSchema, ExposedDeviceSchema, ExposedResourceSchema, FanControlStatusSchema, FanDirectionSchema, FeatureManifestSchema, FeatureProbeStatusSchema, FloodStatusSchema, FrameHandleFormatSchema, FrameHandleSchema, FrameInputSchema, GasStatusSchema, GetStreamWithCodecInputSchema, GlobalMetricsSchema, HF_BASE_URL, HF_REPO, HWACCEL_OPTIONS, HealthStatusSchema, HistoryPointSchema, HistoryResolutionEnum, HumidifierStatusSchema, HumiditySensorStatusSchema, HvacModeSchema, ImageStatusSchema, InstalledPackageSchema, IntegrationLiteSchema, IntegrationWithStateSchema, IntercomAbilitySchema, IntercomStatusSchema, KNOWN_CAP_NAMES, LawnMowerActivitySchema, LawnMowerControlStatusSchema, LocateSegmentResultSchema, LocationStatSchema, LockControlStatusSchema, LockStateSchema, LogEntrySchema, LogLevelSchema, LogStreamEntrySchema, MACRO_LABELS, METHOD_ACCESS_MAP, MODEL_FORMATS, MaskGridDimsSchema, MaskGridShapeSchema, MaskLineShapeSchema, MaskPointSchema, MaskPolygonShapeSchema, MaskPolygonVerticesSchema, MaskRectShapeSchema, MaskShapeKindSchema, MaskShapeSchema, MediaFileSchema, MediaPlayerRepeatSchema, MediaPlayerStateSchema, MediaPlayerStatusSchema, MeshPeerSchema, MeshStatusSchema, MethodAccessSchema, MotionAnalysisResultSchema, MotionEventSchema, MotionOnMotionChangedDataSchema, MotionRegionSchema, MotionSourceEnum, MotionSourcesSchema, MotionStatusSchema, MotionTriggerRuntimeStateSchema, MotionTriggerStatusSchema, MotionZoneOptionsSchema, MotionZonePatchSchema, MotionZoneRegionSchema, MotionZoneStatusSchema, StatusSchema as MqttBrokerStatusSchema, NativeDetectionSchema, NativeObjectClassEnum, NativeObjectDetectionRuntimeStateSchema, NativeObjectDetectionStatusSchema, NetworkAccessStatusSchema, NetworkAddressSchema, NetworkEndpointSchema, NotificationHistoryEntrySchema, NotificationRuleSchema, NotificationSchema, NotifierStatusSchema, NumericSensorStatusSchema, OauthIntegrationDescriptorSchema, ObjectEventSchema, OrchestratorMetricsSchema, OsdOverlayKindEnum, OsdOverlayPatchSchema, OsdOverlaySchema, OsdPositionEnum, OsdStatusSchema, PIPELINE_FLOW_CAPABILITY_NAMES, PIPELINE_OWNER_CAPABILITY_NAMES, PROVIDER_KIND_CAP_NAMES, PYTHON_SCRIPT, PackageUpdateSchema, PackageVersionInfoSchema, PasskeySummarySchema, PcmSampleFormatSchema, PerScopeBreakdownSchema, PickStreamPreferencesSchema, PickStreamRequirementsSchema, PickedCamStreamSchema, PipelineAssignmentSchema, PipelineDefaultStepSchema, PipelineEngineChoiceSchema, PipelineRunResultBridge, PipelineStepInputSchema, PlaceholderReasonSchema, PolygonPointSchema, PowerMeterStatusSchema, PresenceStatusSchema, PressureSensorStatusSchema, PrivacyMaskOptionsSchema, PrivacyMaskPatchSchema, PrivacyMaskRegionSchema, PrivacyMaskShapeSchema, PrivacyMaskStatusSchema, ProfileRtspEntrySchema, ProfileSlotSchema, ProfileSlotStatusSchema, ProviderStatusSchema, PtzAutotrackRuntimeStateSchema, PtzAutotrackSettingsSchema, PtzAutotrackStatusSchema, PtzAutotrackTargetOptionSchema, PtzMoveCommandSchema, PtzPositionSchema, PtzPresetSchema, PtzStatusSchema, QueryFilterSchema, RECOGNITION_TYPES, RUNTIME_DEFAULTS, RUNTIME_TO_FORMAT, RawStateResultSchema, ReadSegmentBytesResultSchema, ReadinessRegistry, ReadinessTimeoutError, RecordingAvailabilitySchema, RecordingBandModeSchema, RecordingBandSchema, RecordingBandTriggersSchema, RecordingConfigSchema, RecordingDaysSchema, RecordingDeviceUsageSchema, RecordingLocationUsageSchema, RecordingManifestSchema, RecordingModeSchema, RecordingRangeSchema, RecordingRetentionSchema, RecordingRuleSchema, RecordingScheduleSchema, RecordingStatusSchema, RecordingStorageModeSchema, RecordingStorageUsageSchema, RecordingTriggersSchema, RecordingWeekdaySchema, RegisteredStreamSchema, ReportMotionInputSchema, RingBuffer, RtpSourceSchema, RtspRestreamEntrySchema, RunnerCameraConfigSchema, RunnerCameraDeviceUIFields, RunnerLocalLoadSchema, RunnerLocalMetricsSchema, SCOPE_PRESETS, SOURCE_INFO_METADATA_KEY, STREAM_PROFILE_META, STREAM_QUALITY_LABELS, SUB_DETECTION_TYPES, SYSTEM_CAP_NAMES, ScopedTokenSchema, ScopedTokenSummarySchema, ScriptRunnerStatusSchema, SearchResultSchema, SendEmailInputSchema, SendEmailResultSchema, SettingsPatchSchema, SettingsRecordSchema, SettingsSchemaWithValuesSchema, SettingsUpdateResultSchema, ShmRingStatsSchema, SmokeStatusSchema, SmtpStatusSchema, SnapshotImageSchema, SourceInfoSchema, SpatialDetectionSchema, SsoBridgeClaimsSchema, StartEmbeddedInputSchema, AbortUploadInputSchema as StorageAbortUploadInputSchema, BeginDownloadInputSchema as StorageBeginDownloadInputSchema, BeginDownloadResultSchema as StorageBeginDownloadResultSchema, BeginUploadInputSchema as StorageBeginUploadInputSchema, BeginUploadResultSchema as StorageBeginUploadResultSchema, EndDownloadInputSchema as StorageEndDownloadInputSchema, FinalizeUploadInputSchema as StorageFinalizeUploadInputSchema, StorageLocationDeclarationSchema, StorageLocationRefSchema, StorageLocationSchema, StorageLocationTypeSchema, ProviderInfoSchema as StorageProviderInfoSchema, ReadChunkInputSchema as StorageReadChunkInputSchema, TestLocationResultSchema as StorageTestLocationResultSchema, WriteChunkInputSchema as StorageWriteChunkInputSchema, StreamCodecSchema, StreamFormatSchema, StreamInfoSchema, StreamNetworkStatsSchema, StreamParamsOptionsSchema, StreamParamsStatusSchema, StreamProfileConfigSchema, StreamProfileOptionsSchema, StreamProfilePatchSchema, StreamProfileSchema, StreamSourceEntrySchema, StreamSourceSchema, SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, SubscribeFramesInputSchema, SubscribeFramesResultSchema, SwitchStatusSchema, SystemMetricsSchema, SystemMirror, TIMEZONES, TamperStatusSchema, TankStatusSchema, TemperatureSensorStatusSchema, TestConnectionResultSchema$1 as TestConnectionResultSchema, ToastSchema, TokenScopeSchema, TopologyNodeSchema, TopologyProcessSchema, TopologyServiceSchema, TrackSchema, TrackStateSchema, TrackedDetectionSchema, TurnServerSchema, BrokerInfoSchema$1 as UnifiedBrokerInfoSchema, UpdateIntegrationInputSchema, UpdateStatusSchema, UpdateUserInputSchema, UserRecordSchema, UserSummarySchema, VacuumControlStatusSchema, VacuumStateSchema, ValveStateSchema, ValveStatusSchema, VibrationStatusSchema, VideoEncodeSchema, WELL_KNOWN_TABS, WELL_KNOWN_TAB_MAP, WaterHeaterStatusSchema, WeatherStatusSchema, WebrtcStreamChoiceSchema, WebrtcStreamTargetSchema, WidgetHostEnum, WidgetMetadataSchema, WidgetRemoteSchema, WidgetSizeEnum, YAMNET_TO_MACRO, ZoneKindEnum, ZoneRuleModeEnum, ZoneRuleSchema, ZoneRuleStageEnum, ZoneRulesArraySchema, ZoneSchema, ZoneScopeBreakdownSchema, accessoriesCapability, accessoryStableId, addonPagesCapability, addonPagesSourceCapability, addonRoutesCapability, addonSettingsCapability, addonWidgetsCapability, addonWidgetsSourceCapability, addonsCapability, adminUiCapability, advancedNotifierCapability, airQualitySensorCapability, alarmPanelCapability, alertsCapability, ambientLightSensorCapability, applyTransform, asBoolean, asJsonArray, asJsonObject, asNumber, asString, audioAnalysisCapability, audioAnalyzerCapability, audioCodecCapability, audioMetricsCapability, authProviderCapability, autoAssignProfiles, automationControlCapability, backupCapability, batteryCapability, bestLocationMatch, binaryCapability, bindAddonActions, brightnessCapability, brokerCapability, buildAddonRouteProvider, buildStreamParamsConfigSchema, buttonCapability, cameraCredentialsCapability, cameraPipelineConfigCapability, cameraStreamsCapability, carbonMonoxideCapability, cellsToRects, classifyStream, classifyStreams, climateControlCapability, colorCapability, connectivityCapability, consumablesCapability, contactCapability, controlCapability, cosineSimilarity, coverCapability, createDeviceProxy, createDurableState, createEvent, createLazyTrpcSource, createMirrorSource, createRuntimeStateBridge, createSliceHandle, createSystemProxy, customAction, decoderCapability, defineCustomActions, detectionPipelineCapability, deviceAdoptionCapability, deviceCustomAction, deviceDiscoveryCapability, deviceExportCapability, deviceManagerCapability, deviceMatchesProfile, deviceOpsCapability, deviceProviderCapability, deviceStateCapability, deviceStatusCapability, doorbellCapability, embeddingEncoderCapability, emitDownForOwnedCaps, emitReadiness, encodeProfileFromStreamShape, enumSensorCapability, enumerateSchemaFields, errMsg, evaluateZoneRules, event, eventEmitterCapability, eventsCapability, expandCapMethods, extractSourceInfoFromMetadata, faceGalleryCapability, fanControlCapability, featureProbeCapability, filesystemBrowseCapability, findTimezone, floodCapability, formatForBackend, formatForRuntime, frameworkSwapConfirmSchema, frameworkSwapPackageSchema, gasCapability, getAudioMacroClassIds, getByPath, getCapsByProviderKind, hfModelUrl, humidifierCapability, humiditySensorCapability, hydrateSchema, imageCapability, integrationsCapability, intercomCapability, isAgentOnlyPlacement, isDeployableToAgent, isDeviceConfigCap, isEvent, jobKindSchema, lawnMowerControlCapability, lifecycleJobSchema, lifecycleJobScopeSchema, lifecycleJobStateSchema, lifecycleTaskSchema, localNetworkCapability, locationSimilarity, lockControlCapability, logDestinationCapability, makeProfileBrokerId, makeSourceBrokerId, mapAudioLabelToMacro, maskUrlCredentials, mediaPlayerCapability, mergeSourceInfo, meshNetworkCapability, method, metricsProviderCapability, migrateConfigToBands, motionCapability, motionDetectionCapability, motionTriggerCapability, motionZonesCapability, mqttBrokerCapability, nativeObjectDetectionCapability, networkAccessCapability, networkQualityCapability, nodesCapability, normalizeAddonInitResult, normalizeUnit, notificationOutputCapability, notifierCapability, numericSensorCapability, oauthIntegrationCapability, osdCapability, parseCameraStreamConfig, parseJsonArray, parseJsonObject, parseJsonUnknown, parseProfileBrokerId, parseStreamParamsFormPatch, pendingFrameworkSwapSchema, pickPreferredRtspEntry, pipelineAnalyticsCapability, pipelineExecutorCapability, pipelineOrchestratorCapability, pipelineRunnerCapability, plateGalleryCapability, platformProbeCapability, powerMeterCapability, presenceCapability, pressureSensorCapability, privacyMaskCapability, ptzAutotrackCapability, ptzCapability, pythonScriptForBackend, readinessKey, rebootCapability, recordingCapability, rectsToCells, requiresPython, resolveAddonExecution, resolveAddonGroup, resolveAddonPlacement, resolveAddonRuntime, resolveDetectionRuntime, resolveDeviceProfile, resolveModelFormat, resolveRunnerId, restreamerCapability, runInferenceStep, scopeKey, scriptRunnerCapability, selectAssignedProfileSlots, setByPath, settingsStoreCapability, sleep, sleepCancellable, smokeCapability, smtpProviderCapability, snapshotCapability, snapshotProviderCapability, ssoBridgeCapability, storageCapability, storageEvictableCapability, storageProviderCapability, streamBrokerCapability, streamCatalogCapability, streamParamsCapability, streamPixels, streamQualityLabel, streamingEngineCapability, switchCapability, synthesizeSourceInfo, systemCapability, tamperCapability, taskLogEntrySchema, taskPhaseSchema, taskTargetSchema, temperatureSensorCapability, toDeviceSummary, toStreamSourceEntry, toastCapability, turnProviderCapability, updateCapability, userManagementCapability, userPasskeysCapability, vacuumControlCapability, valveCapability, vibrationCapability, videoclipsCapability, waterHeaterCapability, weatherCapability, webrtcCapability, webrtcClientHintsSchema, webrtcSessionCapability, wiringAddonHealthSchema, wiringHealthSnapshotSchema, wiringNodeHealthSchema, wiringProbeKindSchema, wiringProbeResultSchema, zodEntriesToConfigUI, zoneAnalyticsCapability, zoneRulesCapability, zonesCapability };
25227
+ export { ACCESSORY_LABEL, ALL_CAPABILITY_DEFINITIONS, APPLE_SA_TO_MACRO, AUDIO_BACKEND_CHOICES, AUDIO_MACRO_LABELS, AccessoriesStatusSchema, AccessoryKind, AddBrokerInputSchema, AddonAutoUpdateSchema, AddonListItemSchema, AddonPageDeclarationSchema, AddonPageInfoSchema, AdoptInputSchema as AdoptionAdoptInputSchema, AdoptResultSchema as AdoptionAdoptResultSchema, AdoptionFilterSchema, GetCandidateInputSchema as AdoptionGetCandidateInputSchema, ListCandidatesInputSchema as AdoptionListCandidatesInputSchema, ListCandidatesOutputSchema as AdoptionListCandidatesOutputSchema, ReleaseInputSchema as AdoptionReleaseInputSchema, AdoptionStatusSchema, AgentLoadSummarySchema, AirQualitySensorStatusSchema, AlarmArmModeSchema, AlarmPanelStatusSchema, AlarmStateSchema, AlertSchema, AlertSeveritySchema, AlertSourceSchema, AlertStatusSchema, AmbientLightSensorStatusSchema, ApiKeyRecordSchema, ApiKeySummarySchema, ArchiveEntrySchema, ArchiveManifestSchema, AudioAnalysisResultSchema, AudioAnalysisSettingsSchema, AudioChunkInputSchema, AudioClassSummarySchema, AudioClassificationLabelSchema, AudioClassificationResultSchema, AudioCodecInfoSchema, AudioDecodeSessionConfigSchema, AudioEncodeSchema, AudioEncodeSessionConfigSchema, AudioEncodedChunkSchema, AudioEventSchema, AudioLevelSchema, AudioMetricsHistoryPointSchema, AudioMetricsHistorySchema, AudioMetricsSnapshotSchema, AudioPcmChunkSchema, AuthResultSchema, AutoUpdateSettingsSchema, AutomationControlStatusSchema, AvailableIntegrationTypeSchema, BACKEND_TO_FORMAT, BATTERY_DEVICE_PROFILE, BackupDestinationInfoSchema, BackupEntrySchema, BaseAddon, BaseDevice, BaseDeviceProvider, BatteryStatusSchema, BinaryStatusSchema, BoundingBoxSchema, BrightnessStatusSchema, AddInputSchema as BrokerAddInputSchema, BrokerAudioClientSchema, BrokerClientsSchema, BrokerConnectionDetailsSchema, BrokerConsumerAttributionSchema, BrokerConsumerKindSchema, BrokerDecodedClientSchema, BrokerEncodedClientSchema, GetStateInputSchema as BrokerGetStateInputSchema, BrokerInfoSchema, BrokerProviderInfoSchema, PublishInputSchema as BrokerPublishInputSchema, RegistryStatusSchema as BrokerRegistryStatusSchema, BrokerRtspClientSchema, BrokerStatsSchema, BrokerStatusEnum, BrokerStatusSchema, SubscribeInputSchema as BrokerSubscribeInputSchema, SubscribeResultSchema as BrokerSubscribeResultSchema, TestConnectionResultSchema as BrokerTestConnectionResultSchema, UnsubscribeInputSchema as BrokerUnsubscribeInputSchema, CAM_PROFILE_ORDER, CAPABILITY_NAMES, CAPABILITY_ROUTER_KEYS, CAP_NAMES_WITH_STATUS, CAP_PROVIDER_KIND_MAP, COCO_80_LABELS, COCO_TO_MACRO, CamProfileSchema, CamStreamDescriptorSchema, CamStreamKindSchema, CamStreamResolutionSchema, CameraAssignmentStatusSchema, CameraAudioStatusSchema, CameraBrokerProfileSchema, CameraBrokerStatusSchema, CameraCredentialsSchema, CameraCredentialsStatusSchema, CameraDecoderShmSchema, CameraDecoderStatusSchema, CameraDetectionPhaseSchema, CameraDetectionProvisioningSchema, CameraDetectionProvisioningStateSchema, CameraDetectionStatusSchema, CameraMetricsSchema, CameraMetricsWithDeviceIdSchema, CameraMotionStatusSchema, CameraRecordingModeSchema, CameraRecordingStatusSchema, CameraSourceStatusSchema, CameraSourceStreamSchema, CameraStatusSchema, CameraStreamSchema, CandidateQueryFilterSchema, CapScopeSchema, CapabilityBindingsSchema, CarbonMonoxideStatusSchema, ChargingStatus, ClientNetworkStatsSchema, ClimateControlStatusSchema, ClipPlaybackSchema, ClipSchema, ClusterAddonNodeDeploymentSchema, ClusterAddonStatusEntrySchema, CollectionColumnSchema, CollectionIndexSchema, ColorStatusSchema, ConfigEntrySchema, ConfigSectionWithValuesSchema, ConfigTabDeclarationSchema, ConnectivityStatusSchema, ConsumableItemSchema, ConsumablesStatusSchema, ContactStatusSchema, ControlKindSchema, ControlStatusSchema, CoverStateSchema, CoverStatusSchema, CreateApiKeyInputSchema, CreateApiKeyResultSchema, CreateIntegrationInputSchema, CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, CreateUserInputSchema, CustomActionInputSchema, DATAPLANE_SECRET_HEADER, DEFAULT_ADDON_PLACEMENT, DEFAULT_AUDIO_ANALYZER_CONFIG, DEFAULT_DECODER_HWACCEL_CONFIG, DEFAULT_FEATURES, DEFAULT_RETENTION, DEVICE_CAP_NAMES, DEVICE_PROFILES, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DEVICE_TYPE_INFO, DecodedAudioChunkSchema, DecodedFrameSchema, DecoderAssignmentSchema, DecoderSessionConfigSchema, DecoderStatsSchema, DeleteIntegrationResultSchema, DetectionSourceSchema, DetectorOutputSchema, DeviceConfig, DeviceDiscoveryStatusSchema, ExposeInputSchema as DeviceExportExposeInputSchema, DeviceExportStatusSchema, UnexposeInputSchema as DeviceExportUnexposeInputSchema, DeviceFeature, DeviceInfoSchema, DeviceNetworkStatsSchema, DeviceRole, DeviceRuntimeState, DeviceStatusSchema, DeviceType, DiscoveredChildDeviceSchema, DiscoveredChildStatusSchema, DiscoveredDeviceSchema, DisposerChain, DoorbellPressEventSchema, DoorbellStatusSchema, ElementConfigStore, EmbeddingInfoSchema, EmbeddingResultSchema, EncodeProfileSchema, EncodedPacketSchema, EnrichedWidgetMetadataSchema, EnumSensorDateTimeFormatSchema, EnumSensorStatusSchema, EventCategory, EventEmitterStatusSchema, EventFireSchema, EventItemSchema, EventKindSchema, EventSourceType, ExportSetupFieldSchema, ExportSetupSchema, ExposedDeviceSchema, ExposedResourceSchema, FanControlStatusSchema, FanDirectionSchema, FeatureManifestSchema, FeatureProbeStatusSchema, FloodStatusSchema, FrameHandleFormatSchema, FrameHandleSchema, FrameInputSchema, GasStatusSchema, GetStreamWithCodecInputSchema, GlobalMetricsSchema, HF_BASE_URL, HF_REPO, HWACCEL_OPTIONS, HealthStatusSchema, HistoryPointSchema, HistoryResolutionEnum, HumidifierStatusSchema, HumiditySensorStatusSchema, HvacModeSchema, ImageStatusSchema, InstalledPackageSchema, IntegrationLiteSchema, IntegrationWithStateSchema, IntercomAbilitySchema, IntercomStatusSchema, KNOWN_CAP_NAMES, LawnMowerActivitySchema, LawnMowerControlStatusSchema, LocateSegmentResultSchema, LocationStatSchema, LockControlStatusSchema, LockStateSchema, LogEntrySchema, LogLevelSchema, LogStreamEntrySchema, MACRO_LABELS, METHOD_ACCESS_MAP, MODEL_FORMATS, MaskGridDimsSchema, MaskGridShapeSchema, MaskLineShapeSchema, MaskPointSchema, MaskPolygonShapeSchema, MaskPolygonVerticesSchema, MaskRectShapeSchema, MaskShapeKindSchema, MaskShapeSchema, MediaFileSchema, MediaPlayerRepeatSchema, MediaPlayerStateSchema, MediaPlayerStatusSchema, MeshPeerSchema, MeshStatusSchema, MethodAccessSchema, MotionAnalysisResultSchema, MotionEventSchema, MotionOnMotionChangedDataSchema, MotionRegionSchema, MotionSourceEnum, MotionSourcesSchema, MotionStatusSchema, MotionTriggerRuntimeStateSchema, MotionTriggerStatusSchema, MotionZoneOptionsSchema, MotionZonePatchSchema, MotionZoneRegionSchema, MotionZoneStatusSchema, StatusSchema as MqttBrokerStatusSchema, NativeDetectionSchema, NativeObjectClassEnum, NativeObjectDetectionRuntimeStateSchema, NativeObjectDetectionStatusSchema, NetworkAccessStatusSchema, NetworkAddressSchema, NetworkEndpointSchema, NotificationHistoryEntrySchema, NotificationRuleSchema, NotificationSchema, NotifierStatusSchema, NumericSensorStatusSchema, OauthIntegrationDescriptorSchema, ObjectEventSchema, OrchestratorMetricsSchema, OsdOverlayKindEnum, OsdOverlayPatchSchema, OsdOverlaySchema, OsdPositionEnum, OsdStatusSchema, PIPELINE_FLOW_CAPABILITY_NAMES, PIPELINE_OWNER_CAPABILITY_NAMES, PROVIDER_KIND_CAP_NAMES, PYTHON_SCRIPT, PackageUpdateSchema, PackageVersionInfoSchema, PasskeySummarySchema, PcmSampleFormatSchema, PerScopeBreakdownSchema, PickStreamPreferencesSchema, PickStreamRequirementsSchema, PickedCamStreamSchema, PipelineAssignmentSchema, PipelineDefaultStepSchema, PipelineEngineChoiceSchema, PipelineRunResultBridge, PipelineStepInputSchema, PlaceholderReasonSchema, PolygonPointSchema, PowerMeterStatusSchema, PresenceStatusSchema, PressureSensorStatusSchema, PrivacyMaskOptionsSchema, PrivacyMaskPatchSchema, PrivacyMaskRegionSchema, PrivacyMaskShapeSchema, PrivacyMaskStatusSchema, ProfileRtspEntrySchema, ProfileSlotSchema, ProfileSlotStatusSchema, ProviderStatusSchema, PtzAutotrackRuntimeStateSchema, PtzAutotrackSettingsSchema, PtzAutotrackStatusSchema, PtzAutotrackTargetOptionSchema, PtzMoveCommandSchema, PtzPositionSchema, PtzPresetSchema, PtzStatusSchema, QueryFilterSchema, RECOGNITION_TYPES, RUNTIME_DEFAULTS, RUNTIME_TO_FORMAT, RawStateResultSchema, ReadSegmentBytesResultSchema, ReadinessRegistry, ReadinessTimeoutError, RecordingAvailabilitySchema, RecordingBandModeSchema, RecordingBandSchema, RecordingBandTriggersSchema, RecordingConfigSchema, RecordingDaysSchema, RecordingDeviceUsageSchema, RecordingLocationUsageSchema, RecordingManifestSchema, RecordingModeSchema, RecordingRangeSchema, RecordingRetentionSchema, RecordingRuleSchema, RecordingScheduleSchema, RecordingStatusSchema, RecordingStorageModeSchema, RecordingStorageUsageSchema, RecordingTriggersSchema, RecordingWeekdaySchema, RegisteredStreamSchema, ReportMotionInputSchema, RingBuffer, RtpSourceSchema, RtspRestreamEntrySchema, RunnerCameraConfigSchema, RunnerCameraDeviceUIFields, RunnerLocalLoadSchema, RunnerLocalMetricsSchema, SCOPE_PRESETS, SOURCE_INFO_METADATA_KEY, STREAM_PROFILE_META, STREAM_QUALITY_LABELS, SUB_DETECTION_TYPES, SYSTEM_CAP_NAMES, ScopedTokenSchema, ScopedTokenSummarySchema, ScriptRunnerStatusSchema, SearchResultSchema, SendEmailInputSchema, SendEmailResultSchema, SettingsPatchSchema, SettingsRecordSchema, SettingsSchemaWithValuesSchema, SettingsUpdateResultSchema, ShmRingStatsSchema, SmokeStatusSchema, SmtpStatusSchema, SnapshotImageSchema, SourceInfoSchema, SpatialDetectionSchema, SsoBridgeClaimsSchema, StartEmbeddedInputSchema, AbortUploadInputSchema as StorageAbortUploadInputSchema, BeginDownloadInputSchema as StorageBeginDownloadInputSchema, BeginDownloadResultSchema as StorageBeginDownloadResultSchema, BeginUploadInputSchema as StorageBeginUploadInputSchema, BeginUploadResultSchema as StorageBeginUploadResultSchema, EndDownloadInputSchema as StorageEndDownloadInputSchema, FinalizeUploadInputSchema as StorageFinalizeUploadInputSchema, StorageLocationDeclarationSchema, StorageLocationRefSchema, StorageLocationSchema, StorageLocationTypeSchema, ProviderInfoSchema as StorageProviderInfoSchema, ReadChunkInputSchema as StorageReadChunkInputSchema, TestLocationResultSchema as StorageTestLocationResultSchema, WriteChunkInputSchema as StorageWriteChunkInputSchema, StreamCodecSchema, StreamFormatSchema, StreamInfoSchema, StreamNetworkStatsSchema, StreamParamsOptionsSchema, StreamParamsStatusSchema, StreamProfileConfigSchema, StreamProfileOptionsSchema, StreamProfilePatchSchema, StreamProfileSchema, StreamSourceEntrySchema, StreamSourceSchema, SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, SubscribeFramesInputSchema, SubscribeFramesResultSchema, SwitchStatusSchema, SystemMetricsSchema, SystemMirror, TIMEZONES, TamperStatusSchema, TankStatusSchema, TemperatureSensorStatusSchema, TestConnectionResultSchema$1 as TestConnectionResultSchema, ToastSchema, TokenScopeSchema, TopologyNodeSchema, TopologyProcessSchema, TopologyServiceSchema, TrackSchema, TrackStateSchema, TrackedDetectionSchema, TurnServerSchema, BrokerInfoSchema$1 as UnifiedBrokerInfoSchema, UpdateIntegrationInputSchema, UpdateStatusSchema, UpdateUserInputSchema, UserRecordSchema, UserSummarySchema, VacuumControlStatusSchema, VacuumStateSchema, ValveStateSchema, ValveStatusSchema, VibrationStatusSchema, VideoEncodeSchema, WELL_KNOWN_TABS, WELL_KNOWN_TAB_MAP, WaterHeaterStatusSchema, WeatherStatusSchema, WebrtcStreamChoiceSchema, WebrtcStreamTargetSchema, WidgetHostEnum, WidgetMetadataSchema, WidgetRemoteSchema, WidgetSizeEnum, YAMNET_TO_MACRO, ZoneKindEnum, ZoneRuleModeEnum, ZoneRuleSchema, ZoneRuleStageEnum, ZoneRulesArraySchema, ZoneSchema, ZoneScopeBreakdownSchema, accessoriesCapability, accessoryStableId, addonPagesCapability, addonPagesSourceCapability, addonRoutesCapability, addonSettingsCapability, addonWidgetsCapability, addonWidgetsSourceCapability, addonsCapability, adminUiCapability, advancedNotifierCapability, airQualitySensorCapability, alarmPanelCapability, alertsCapability, ambientLightSensorCapability, applyTransform, asBoolean, asJsonArray, asJsonObject, asNumber, asString, audioAnalysisCapability, audioAnalyzerCapability, audioCodecCapability, audioMetricsCapability, authProviderCapability, autoAssignProfiles, automationControlCapability, backupCapability, batteryCapability, bestLocationMatch, binaryCapability, bindAddonActions, brightnessCapability, brokerCapability, buildAddonRouteProvider, buildStreamParamsConfigSchema, buttonCapability, cameraCredentialsCapability, cameraPipelineConfigCapability, cameraStreamsCapability, carbonMonoxideCapability, cellsToRects, classifyStream, classifyStreams, climateControlCapability, colorCapability, connectivityCapability, consumablesCapability, contactCapability, controlCapability, cosineSimilarity, coverCapability, createDeviceProxy, createDurableState, createEvent, createLazyTrpcSource, createMirrorSource, createRuntimeStateBridge, createSliceHandle, createSystemProxy, customAction, decoderCapability, defineCustomActions, detectionPipelineCapability, deviceAdoptionCapability, deviceCustomAction, deviceDiscoveryCapability, deviceExportCapability, deviceManagerCapability, deviceMatchesProfile, deviceOpsCapability, deviceProviderCapability, deviceStateCapability, deviceStatusCapability, doorbellCapability, embeddingEncoderCapability, emitDownForOwnedCaps, emitReadiness, encodeProfileFromStreamShape, enumSensorCapability, enumerateSchemaFields, errMsg, evaluateZoneRules, event, eventEmitterCapability, eventsCapability, expandCapMethods, extractSourceInfoFromMetadata, faceGalleryCapability, fanControlCapability, featureProbeCapability, filesystemBrowseCapability, findTimezone, floodCapability, formatForBackend, formatForRuntime, frameworkSwapConfirmSchema, frameworkSwapPackageSchema, gasCapability, getAudioMacroClassIds, getByPath, getCapsByProviderKind, hfModelUrl, humidifierCapability, humiditySensorCapability, hydrateSchema, imageCapability, integrationsCapability, intercomCapability, isAgentOnlyPlacement, isDeployableToAgent, isDeviceConfigCap, isEvent, jobKindSchema, lawnMowerControlCapability, lifecycleJobSchema, lifecycleJobScopeSchema, lifecycleJobStateSchema, lifecycleTaskSchema, localNetworkCapability, locationSimilarity, lockControlCapability, logDestinationCapability, makeProfileBrokerId, makeSourceBrokerId, mapAudioLabelToMacro, maskUrlCredentials, mediaPlayerCapability, mergeSourceInfo, meshNetworkCapability, method, metricsProviderCapability, migrateConfigToBands, motionCapability, motionDetectionCapability, motionTriggerCapability, motionZonesCapability, mqttBrokerCapability, nativeObjectDetectionCapability, networkAccessCapability, networkQualityCapability, nodesCapability, normalizeAddonInitResult, normalizeUnit, notificationOutputCapability, notifierCapability, numericSensorCapability, oauthIntegrationCapability, osdCapability, parseCameraStreamConfig, parseJsonArray, parseJsonObject, parseJsonUnknown, parseProfileBrokerId, parseStreamParamsFormPatch, pendingFrameworkSwapSchema, pickPreferredRtspEntry, pipelineAnalyticsCapability, pipelineExecutorCapability, pipelineOrchestratorCapability, pipelineRunnerCapability, plateGalleryCapability, platformProbeCapability, powerMeterCapability, presenceCapability, pressureSensorCapability, privacyMaskCapability, ptzAutotrackCapability, ptzCapability, pythonScriptForBackend, readinessKey, rebootCapability, recordingCapability, rectsToCells, requiresPython, resolveAddonExecution, resolveAddonGroup, resolveAddonPlacement, resolveAddonRuntime, resolveDetectionRuntime, resolveDeviceProfile, resolveModelFormat, resolveRunnerId, restreamerCapability, runInferenceStep, scopeKey, scriptRunnerCapability, selectAssignedProfileSlots, setByPath, settingsStoreCapability, sleep, sleepCancellable, smokeCapability, smtpProviderCapability, snapshotCapability, snapshotProviderCapability, ssoBridgeCapability, storageCapability, storageEvictableCapability, storageProviderCapability, streamBrokerCapability, streamCatalogCapability, streamParamsCapability, streamPixels, streamQualityLabel, streamingEngineCapability, switchCapability, synthesizeSourceInfo, systemCapability, tamperCapability, taskLogEntrySchema, taskPhaseSchema, taskTargetSchema, temperatureSensorCapability, toDeviceSummary, toStreamSourceEntry, toastCapability, turnProviderCapability, updateCapability, userManagementCapability, userPasskeysCapability, vacuumControlCapability, valveCapability, vibrationCapability, videoclipsCapability, waterHeaterCapability, weatherCapability, webrtcCapability, webrtcClientHintsSchema, webrtcSessionCapability, wiringAddonHealthSchema, wiringHealthSnapshotSchema, wiringNodeHealthSchema, wiringProbeKindSchema, wiringProbeResultSchema, zodEntriesToConfigUI, zoneAnalyticsCapability, zoneRulesCapability, zonesCapability };