@camstack/types 1.2.41 → 1.2.43

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 (43) hide show
  1. package/dist/addon.js +1 -1
  2. package/dist/addon.mjs +1 -1
  3. package/dist/canonical-hash-7nfBbEqR.mjs +35 -0
  4. package/dist/canonical-hash-BcZHRHIx.js +40 -0
  5. package/dist/cap-call-context.d.ts +26 -0
  6. package/dist/capabilities/index.d.ts +5 -5
  7. package/dist/capabilities/notification-rules.cap.d.ts +41 -0
  8. package/dist/capabilities/pipeline-analytics.cap.d.ts +92 -4
  9. package/dist/capabilities/pipeline-orchestrator.cap.d.ts +149 -0
  10. package/dist/capabilities/pipeline-runner.cap.d.ts +119 -1
  11. package/dist/capabilities/platform-probe.cap.d.ts +3 -3
  12. package/dist/capabilities/privacy-mask.cap.d.ts +69 -9
  13. package/dist/capabilities/recording.cap.d.ts +30 -0
  14. package/dist/capabilities/snapshot.cap.d.ts +1 -1
  15. package/dist/capabilities/stream-broker.cap.d.ts +304 -0
  16. package/dist/capabilities/stream-params.cap.d.ts +8 -4
  17. package/dist/device/device-profile.d.ts +12 -4
  18. package/dist/device/system-mirror.d.ts +11 -0
  19. package/dist/encode-profile.d.ts +2 -0
  20. package/dist/ffmpeg/encode-defaults.d.ts +107 -0
  21. package/dist/ffmpeg/hwaccel.d.ts +98 -0
  22. package/dist/ffmpeg/invocation.d.ts +348 -0
  23. package/dist/ffmpeg/process.d.ts +135 -0
  24. package/dist/ffmpeg/sharing-key.d.ts +91 -0
  25. package/dist/generated/addon-api.d.ts +92 -4
  26. package/dist/generated/device-proxy.d.ts +2 -2
  27. package/dist/generated/method-access-map.d.ts +1 -1
  28. package/dist/generated/system-proxy.d.ts +2 -2
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.js +2069 -42
  31. package/dist/index.mjs +2006 -43
  32. package/dist/interfaces/camera-switches.d.ts +375 -0
  33. package/dist/interfaces/inference-engine.d.ts +24 -3
  34. package/dist/interfaces/ops-log.d.ts +4 -0
  35. package/dist/interfaces/pipeline-runner-capability.d.ts +9 -1
  36. package/dist/node.d.ts +2 -0
  37. package/dist/node.js +270 -36
  38. package/dist/node.mjs +269 -36
  39. package/dist/pipeline/native-lease.d.ts +150 -0
  40. package/dist/{sleep-DTce7-ch.js → sleep-Bx9IIoT0.js} +43 -1
  41. package/dist/{sleep-CXimb854.mjs → sleep-DtstvzWm.mjs} +38 -2
  42. package/dist/utils/addon-id.d.ts +30 -0
  43. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { t as EventCategory } from "./event-category-41fKf-q9.mjs";
2
- import { $ as ProfileRtspEntrySchema, A as method, B as scopeKey, C as DeviceType, Ct as collectHydratedFieldValues, D as event, E as DEVICE_STATUS_METHOD, F as readNodePin, G as CamStreamKindSchema, H as BrokerStatusSchema, I as ReadinessRegistry, J as DecodedAudioChunkSchema, K as CamStreamResolutionSchema, L as ReadinessTimeoutError, M as systemMethod, N as CAP_NODE_PIN_CONTEXT_KEY, O as expandCapMethods, P as nodePin, Q as FrameHandleSchema, R as emitDownForOwnedCaps, S as DeviceRole, St as collectHydratedFieldEntries, T as DEVICE_SETTINGS_CONTRIBUTION_METHODS, Tt as resolveHydratedFieldValue, U as CAM_PROFILE_ORDER, V as BrokerStatsSchema, W as CamProfileSchema, X as EncodedPacketSchema, Y as DecodedFrameSchema, Z as FrameHandleFormatSchema, _ as RawStateResultSchema, _t as createEvent, a as asJsonObject, at as SubscribeAudioChunksResultSchema, b as ChargingStatus, bt as WELL_KNOWN_TABS, c as parseJsonArray, ct as makeProfileBrokerId, d as DEVICE_SCOPED_CAPS, dt as selectAssignedProfileSlots, et as ProfileSlotSchema, f as isDeviceScopedCap, ft as DATAPLANE_SECRET_HEADER, g as createSliceHandle, gt as createDurableState, h as createMirrorSource, ht as normalizeAddonInitResult, i as asJsonArray, it as SubscribeAudioChunksInputSchema, j as resolveCapMount, k as isDeviceConfigCap, l as parseJsonObject, lt as makeSourceBrokerId, m as createLazyTrpcSource, mt as BaseAddon, n as sleepCancellable, nt as StreamSourceEntrySchema, o as asNumber, ot as SubscribeFramesInputSchema, p as createDeviceProxy, pt as DisposerChain, q as CameraStreamSchema, r as asBoolean, rt as StreamSourceSchema, s as asString, st as SubscribeFramesResultSchema, t as sleep, tt as ProfileSlotStatusSchema, u as parseJsonUnknown, ut as parseProfileBrokerId, v as deviceOpsCapability, vt as emitReadiness, w as adminUiCapability, wt as hydrateSchema, x as DeviceFeature, xt as WELL_KNOWN_TAB_MAP, y as viewerUiCapability, yt as isEvent, z as readinessKey } from "./sleep-CXimb854.mjs";
2
+ import { $ as FrameHandleSchema, A as method, B as readinessKey, C as DeviceType, Ct as collectHydratedFieldEntries, D as event, E as DEVICE_STATUS_METHOD, Et as resolveHydratedFieldValue, F as readNodePin, G as CamProfileSchema, H as BrokerStatsSchema, I as toNodeId, J as CameraStreamSchema, K as CamStreamKindSchema, L as ReadinessRegistry, M as systemMethod, N as CAP_NODE_PIN_CONTEXT_KEY, O as expandCapMethods, P as nodePin, Q as FrameHandleFormatSchema, R as ReadinessTimeoutError, S as DeviceRole, St as WELL_KNOWN_TAB_MAP, T as DEVICE_SETTINGS_CONTRIBUTION_METHODS, Tt as hydrateSchema, U as BrokerStatusSchema, V as scopeKey, W as CAM_PROFILE_ORDER, X as DecodedFrameSchema, Y as DecodedAudioChunkSchema, Z as EncodedPacketSchema, _ as RawStateResultSchema, _t as createDurableState, a as asJsonObject, at as SubscribeAudioChunksInputSchema, b as ChargingStatus, bt as isEvent, c as parseJsonArray, ct as SubscribeFramesResultSchema, d as DEVICE_SCOPED_CAPS, dt as parseProfileBrokerId, et as ProfileRtspEntrySchema, f as isDeviceScopedCap, ft as selectAssignedProfileSlots, g as createSliceHandle, gt as normalizeAddonInitResult, h as createMirrorSource, ht as BaseAddon, i as asJsonArray, it as StreamSourceSchema, j as resolveCapMount, k as isDeviceConfigCap, l as parseJsonObject, lt as makeProfileBrokerId, m as createLazyTrpcSource, mt as DisposerChain, n as sleepCancellable, nt as ProfileSlotStatusSchema, o as asNumber, ot as SubscribeAudioChunksResultSchema, p as createDeviceProxy, pt as DATAPLANE_SECRET_HEADER, q as CamStreamResolutionSchema, r as asBoolean, rt as StreamSourceEntrySchema, s as asString, st as SubscribeFramesInputSchema, t as sleep, tt as ProfileSlotSchema, u as parseJsonUnknown, ut as makeSourceBrokerId, v as deviceOpsCapability, vt as createEvent, w as adminUiCapability, wt as collectHydratedFieldValues, x as DeviceFeature, xt as WELL_KNOWN_TABS, y as viewerUiCapability, yt as emitReadiness, z as emitDownForOwnedCaps } from "./sleep-DtstvzWm.mjs";
3
+ import { t as canonicalHash } from "./canonical-hash-7nfBbEqR.mjs";
3
4
  import { EventSourceType } from "./enums.mjs";
4
5
  import { t as errMsg } from "./err-msg-IQTHeDzc.mjs";
5
6
  import { z } from "zod";
@@ -146,6 +147,14 @@ var VideoEncodeSchema = z.object({
146
147
  "main",
147
148
  "high"
148
149
  ]).optional(),
150
+ /**
151
+ * `-level`, e.g. `'3.1'`. A consumer that ADVERTISES a level in its SDP
152
+ * (`profile-level-id=42e01f` is Baseline 3.1) must constrain the encoder to
153
+ * it, or it ships a stream that does not match its own advertisement — the
154
+ * defect class that kept HomeKit black for a year and that Alexa carried
155
+ * silently. Optional because a browser negotiates the level itself.
156
+ */
157
+ level: z.string().optional(),
149
158
  width: z.number().int().positive().optional(),
150
159
  height: z.number().int().positive().optional(),
151
160
  fps: z.number().positive().optional(),
@@ -221,6 +230,636 @@ function encodeProfileFromStreamShape(stream) {
221
230
  };
222
231
  }
223
232
  //#endregion
233
+ //#region src/ffmpeg/invocation.ts
234
+ var AUDIO_ENCODER_BY_CODEC = {
235
+ opus: "libopus",
236
+ aac: "aac",
237
+ pcmu: "pcm_mulaw",
238
+ pcma: "pcm_alaw"
239
+ };
240
+ /**
241
+ * Camera-microphone audio, per codec. Lives HERE rather than in
242
+ * `encode-defaults.ts` only to avoid an import cycle (`encode-defaults` depends
243
+ * on these types); it is re-exported from there, which is where to read it.
244
+ *
245
+ * Every source in this repo is a mono camera mic. The former broker preset
246
+ * encoded Opus at `channels: 2`, spending bitrate duplicating one channel —
247
+ * that is the value this consolidation changed.
248
+ */
249
+ var AUDIO_PRESETS = {
250
+ aac: {
251
+ kind: "encode",
252
+ codec: "aac",
253
+ bitrateKbps: 128,
254
+ sampleRateHz: 48e3,
255
+ channels: 1
256
+ },
257
+ opus: {
258
+ kind: "encode",
259
+ codec: "opus",
260
+ bitrateKbps: 64,
261
+ sampleRateHz: 48e3,
262
+ channels: 1
263
+ },
264
+ pcmu: {
265
+ kind: "encode",
266
+ codec: "pcmu",
267
+ sampleRateHz: 8e3,
268
+ channels: 1
269
+ }
270
+ };
271
+ /** `-hide_banner -loglevel <level>` — every ffmpeg site opens with this. */
272
+ function logBannerArgs(level) {
273
+ return [
274
+ "-hide_banner",
275
+ "-loglevel",
276
+ level
277
+ ];
278
+ }
279
+ /** `true` when the resolved value means "decode in software" (⇒ no `-hwaccel`). */
280
+ function isSoftwareDecode(decodeHwAccel) {
281
+ return !decodeHwAccel || decodeHwAccel === "none" || decodeHwAccel === "copy";
282
+ }
283
+ /**
284
+ * Every INPUT option, in order, terminated by `-i <url>`. Nothing may be
285
+ * appended to this list by a caller — that is the whole point of the function.
286
+ */
287
+ function buildInputArgs(input, decodeHwAccel) {
288
+ const args = [];
289
+ if (!isSoftwareDecode(decodeHwAccel)) args.push("-hwaccel", String(decodeHwAccel));
290
+ if (input.extraArgs?.length) args.push(...input.extraArgs);
291
+ if (input.analyzeDurationUs !== void 0) args.push("-analyzeduration", String(input.analyzeDurationUs));
292
+ if (input.probeSizeBytes !== void 0) args.push("-probesize", String(input.probeSizeBytes));
293
+ if (input.fflags?.length) for (const flag of input.fflags) args.push("-fflags", flag);
294
+ if (input.rtspTransport) args.push("-rtsp_transport", input.rtspTransport);
295
+ args.push("-i", input.url);
296
+ return args;
297
+ }
298
+ /** The `-vf` filter args, or `[]` when a consumer `-vf` already claims the slot. */
299
+ function buildVideoFilterArgs(scale, outputArgs) {
300
+ if (!scale) return [];
301
+ if (outputArgs.some((a) => a === "-vf")) return [];
302
+ if (scale.mode === "exact") return ["-vf", `scale=${scale.width}:${scale.height}`];
303
+ return ["-vf", `scale='min(${scale.width},iw)':'min(${scale.height},ih)':force_original_aspect_ratio=decrease:force_divisible_by=2`];
304
+ }
305
+ /** Rate-control args for an encode plan. */
306
+ function buildRateControlArgs(video) {
307
+ const kbps = video.bitrateKbps;
308
+ if (kbps === void 0) return [];
309
+ const rc = video.rateControl ?? {
310
+ kind: "cap",
311
+ vbvSeconds: 2
312
+ };
313
+ const bufsize = Math.max(1, Math.round(kbps * rc.vbvSeconds));
314
+ return [
315
+ ...rc.kind === "cbr" ? ["-b:v", `${kbps}k`] : [],
316
+ "-maxrate",
317
+ `${kbps}k`,
318
+ "-bufsize",
319
+ `${bufsize}k`
320
+ ];
321
+ }
322
+ /** The whole video block (`-vf` … `-c:v` … knobs), after `-i`. */
323
+ function buildVideoArgs(video, outputArgs) {
324
+ if (video.kind === "copy") return [
325
+ "-c:v",
326
+ "copy",
327
+ ...video.bitstreamFilter ? ["-bsf:v", video.bitstreamFilter] : []
328
+ ];
329
+ const args = [
330
+ ...buildVideoFilterArgs(video.scale, outputArgs),
331
+ "-c:v",
332
+ video.encoder
333
+ ];
334
+ if (video.preset !== void 0) args.push("-preset", video.preset);
335
+ if (video.tune !== void 0) args.push("-tune", video.tune);
336
+ if (video.profile !== void 0) args.push("-profile:v", video.profile);
337
+ if (video.level !== void 0) args.push("-level", video.level);
338
+ if (video.pixelFormat !== void 0) args.push("-pix_fmt", video.pixelFormat);
339
+ if (video.fps !== void 0) args.push("-r", String(video.fps));
340
+ if (video.gopFrames !== void 0) args.push("-g", String(video.gopFrames));
341
+ if (video.forceKeyFramesSeconds !== void 0) args.push("-force_key_frames", `expr:gte(t,n_forced*${video.forceKeyFramesSeconds})`);
342
+ if (video.bf !== void 0) args.push("-bf", String(video.bf));
343
+ args.push(...buildRateControlArgs(video));
344
+ if (video.bitstreamFilter !== void 0) args.push("-bsf:v", video.bitstreamFilter);
345
+ return args;
346
+ }
347
+ /** The whole audio block, after `-i`. */
348
+ function buildAudioArgs(audio) {
349
+ if (audio.kind === "none") return ["-an"];
350
+ if (audio.kind === "copy") return ["-c:a", "copy"];
351
+ const args = [];
352
+ if (audio.filter !== void 0) args.push("-af", audio.filter);
353
+ args.push("-c:a", AUDIO_ENCODER_BY_CODEC[audio.codec]);
354
+ if (audio.application !== void 0) args.push("-application", audio.application);
355
+ if (audio.frameDurationMs !== void 0) args.push("-frame_duration", String(audio.frameDurationMs));
356
+ if (audio.globalHeader === true) args.push("-flags", "+global_header");
357
+ if (audio.sampleRateHz !== void 0) args.push("-ar", String(audio.sampleRateHz));
358
+ if (audio.bitrateKbps !== void 0) args.push("-b:a", `${audio.bitrateKbps}k`);
359
+ if (audio.vbvBufferKbits !== void 0) args.push("-bufsize", `${audio.vbvBufferKbits}k`);
360
+ if (audio.channels !== void 0) args.push("-ac", String(audio.channels));
361
+ return args;
362
+ }
363
+ /** RTP output-leg args (`-payload_type`, `-ssrc`, `-sdp_file`, `-f rtp <url>`). */
364
+ function buildRtpOutputArgs(out) {
365
+ const args = [];
366
+ if (out.payloadType !== void 0) args.push("-payload_type", String(out.payloadType));
367
+ if (out.ssrc !== void 0) args.push("-ssrc", String(out.ssrc));
368
+ if (out.sdpFile !== void 0) args.push("-sdp_file", out.sdpFile);
369
+ args.push("-f", "rtp", out.url);
370
+ return args;
371
+ }
372
+ /** `true` when the sink is a raw elementary bytestream that cannot mux audio. */
373
+ function isElementaryVideoSink(sink) {
374
+ return sink.kind === "stdout" && (sink.container === "h264" || sink.container === "hevc");
375
+ }
376
+ /**
377
+ * The fragmented-MP4 muxer flags, in the order the recorder has proven them
378
+ * (`recorder/addon/ffmpeg-args.ts` passes the same `movflags` string through
379
+ * `-segment_format_options`, across every vendor in the fleet):
380
+ *
381
+ * - `frag_keyframe` — cut a fragment at each key frame, so every fragment
382
+ * opens on a sync sample. HKSV's whole requirement.
383
+ * - `empty_moov` — write `ftyp`+`moov` up front with no samples in it, which
384
+ * is what makes the head a standalone INITIALISATION segment.
385
+ * - `default_base_moof` — fragment offsets are self-relative, so a fragment is
386
+ * demuxable without the bytes that preceded it. D31's byte-range read path
387
+ * depends on exactly this property of the recorder's segments.
388
+ */
389
+ var FMP4_MOVFLAGS = "+frag_keyframe+empty_moov+default_base_moof";
390
+ /**
391
+ * The terminal sink args for every non-`rtp-outputs` sink. Exhaustive over the
392
+ * union so a new member cannot fall through to `['-f', container, 'pipe:1']`,
393
+ * which is what a plain `container` read would have done for `mp4` — a valid
394
+ * argv that writes a NON-fragmented, unseekable-to-a-pipe MP4 and produces one
395
+ * unusable byte stream.
396
+ */
397
+ function buildStdoutOrRtspSinkArgs(sink) {
398
+ if (sink.kind === "rtsp-listen") return [
399
+ "-f",
400
+ "rtsp",
401
+ "-rtsp_transport",
402
+ "tcp",
403
+ "-rtsp_flags",
404
+ "listen",
405
+ sink.url
406
+ ];
407
+ if (sink.kind === "rtp-outputs") return [];
408
+ return sink.container === "mp4" ? buildFmp4SinkArgs(sink) : [
409
+ "-f",
410
+ sink.container,
411
+ "pipe:1"
412
+ ];
413
+ }
414
+ /** `-movflags … -min_frag_duration <us> -f mp4 pipe:1`. */
415
+ function buildFmp4SinkArgs(sink) {
416
+ return [
417
+ "-movflags",
418
+ FMP4_MOVFLAGS,
419
+ "-min_frag_duration",
420
+ String(Math.max(0, Math.round(sink.fragmentMs * 1e3))),
421
+ "-f",
422
+ "mp4",
423
+ "pipe:1"
424
+ ];
425
+ }
426
+ /**
427
+ * A second output mapping source audio to RTP-over-UDP. `0:a:0?` makes the
428
+ * audio optional so a source with no audio skips it instead of failing the
429
+ * whole invocation.
430
+ */
431
+ function buildAudioSidecarArgs(sidecar) {
432
+ return [
433
+ "-map",
434
+ "0:a:0?",
435
+ ...buildAudioArgs(sidecar.codec === "pcma" ? {
436
+ kind: "encode",
437
+ codec: "pcma",
438
+ sampleRateHz: 8e3,
439
+ channels: 1
440
+ } : AUDIO_PRESETS[sidecar.codec]),
441
+ ...buildRtpOutputArgs({
442
+ url: sidecar.rtpUrl,
443
+ sdpFile: sidecar.sdpFile
444
+ })
445
+ ];
446
+ }
447
+ /**
448
+ * Assemble the full ffmpeg argument list. Layout:
449
+ *
450
+ * -hide_banner -loglevel <level>
451
+ * [-hwaccel <backend|auto>] ─┐ INPUT options — strictly before -i.
452
+ * [<input.extraArgs>] │
453
+ * [-fflags <flag>…] │
454
+ * [-rtsp_transport tcp] │
455
+ * -i <url> ─┘
456
+ * <video block> <threads> <audio block> ─┐ OUTPUT options.
457
+ * <consumer outputArgs verbatim> │
458
+ * <sink> ─┘ terminal
459
+ */
460
+ function buildFfmpegArgs(inv) {
461
+ const head = [...logBannerArgs(inv.logLevel), ...buildInputArgs(inv.input, inv.decodeHwAccel)];
462
+ const threadArgs = inv.threadCount > 0 ? ["-threads", String(inv.threadCount)] : [];
463
+ if (inv.sink.kind === "rtp-outputs") {
464
+ const videoLeg = inv.sink.video ? [
465
+ "-an",
466
+ "-map",
467
+ "0:v:0",
468
+ ...buildVideoArgs(inv.video, inv.outputArgs),
469
+ ...threadArgs,
470
+ ...inv.outputArgs,
471
+ ...buildRtpOutputArgs(inv.sink.video)
472
+ ] : [];
473
+ const audioLeg = inv.sink.audio ? [
474
+ "-vn",
475
+ "-map",
476
+ "0:a:0?",
477
+ ...buildAudioArgs(inv.audio),
478
+ ...buildRtpOutputArgs(inv.sink.audio)
479
+ ] : [];
480
+ return [
481
+ ...head,
482
+ ...videoLeg,
483
+ ...audioLeg
484
+ ];
485
+ }
486
+ const audioArgs = isElementaryVideoSink(inv.sink) ? ["-an"] : buildAudioArgs(inv.audio);
487
+ const sinkArgs = buildStdoutOrRtspSinkArgs(inv.sink);
488
+ return [
489
+ ...head,
490
+ ...buildVideoArgs(inv.video, inv.outputArgs),
491
+ ...threadArgs,
492
+ ...audioArgs,
493
+ ...inv.outputArgs,
494
+ ...sinkArgs,
495
+ ...inv.audioSidecar ? buildAudioSidecarArgs(inv.audioSidecar) : []
496
+ ];
497
+ }
498
+ /**
499
+ * Hardware ENCODER ids per decode-hwaccel backend — a static, deterministic
500
+ * map (the same shape the reference NVR uses: platform → encoder, no probe).
501
+ */
502
+ var ENCODER_IDS_BY_BACKEND = {
503
+ videotoolbox: {
504
+ h264: "h264_videotoolbox",
505
+ h265: "hevc_videotoolbox"
506
+ },
507
+ vaapi: {
508
+ h264: "h264_vaapi",
509
+ h265: "hevc_vaapi"
510
+ },
511
+ qsv: {
512
+ h264: "h264_qsv",
513
+ h265: "hevc_qsv"
514
+ },
515
+ cuda: {
516
+ h264: "h264_nvenc",
517
+ h265: "hevc_nvenc"
518
+ },
519
+ nvdec: {
520
+ h264: "h264_nvenc",
521
+ h265: "hevc_nvenc"
522
+ },
523
+ amf: {
524
+ h264: "h264_amf",
525
+ h265: "hevc_amf"
526
+ }
527
+ };
528
+ /**
529
+ * The hardware encoder for a target codec on `backend`, or the software one.
530
+ * `'auto'` is NOT a backend identity (it is an instruction to ffmpeg), so it
531
+ * maps to software encoding.
532
+ */
533
+ function pickVideoEncoder(target, backend, useHardware) {
534
+ const software = target === "h264" ? "libx264" : "libx265";
535
+ if (!useHardware || backend === null || isSoftwareDecode(backend) || backend === "auto") return software;
536
+ const ids = ENCODER_IDS_BY_BACKEND[backend.toLowerCase()];
537
+ if (!ids) return software;
538
+ return target === "h264" ? ids.h264 : ids.h265;
539
+ }
540
+ /** Map an `EncodeProfile.audio` to an audio plan. */
541
+ function audioPlanFromEncodeProfile(audio) {
542
+ if (audio === "passthrough") return { kind: "none" };
543
+ if (audio.codec === "copy") return { kind: "copy" };
544
+ return {
545
+ kind: "encode",
546
+ codec: audio.codec,
547
+ ...audio.bitrateKbps !== void 0 ? { bitrateKbps: audio.bitrateKbps } : {},
548
+ ...audio.sampleRateHz !== void 0 ? { sampleRateHz: audio.sampleRateHz } : {},
549
+ ...audio.channels !== void 0 ? { channels: audio.channels } : {}
550
+ };
551
+ }
552
+ /**
553
+ * Adapt an `EncodeProfile` (the operator/consumer-facing shape) into an
554
+ * {@link FfmpegInvocation}. This is the ONLY bridge between the two models —
555
+ * a second one is how the repo grew two argv builders that disagreed about
556
+ * hardware.
557
+ *
558
+ * Smart video copy: when the source already speaks the requested codec the
559
+ * encode block is elided entirely and ffmpeg runs as a re-muxer on the video
560
+ * plane. Width / height / fps / bitrate in the profile are a downstream BUDGET,
561
+ * not a forced rescale.
562
+ */
563
+ function invocationFromEncodeProfile(input) {
564
+ const v = input.profile.video;
565
+ const shouldCopy = v.codec === "copy" || input.forceReencode !== true && v.codec === input.sourceCodec;
566
+ const scale = v.width !== void 0 && v.height !== void 0 ? {
567
+ mode: "fit",
568
+ width: v.width,
569
+ height: v.height
570
+ } : null;
571
+ const target = v.codec === "h265" ? "h265" : "h264";
572
+ const video = shouldCopy ? {
573
+ kind: "copy",
574
+ ...input.bitstreamFilter !== void 0 ? { bitstreamFilter: input.bitstreamFilter } : {}
575
+ } : {
576
+ kind: "encode",
577
+ encoder: pickVideoEncoder(target, input.decodeHwAccel, input.hardwareEncoders === true),
578
+ scale,
579
+ ...v.preset !== void 0 ? { preset: v.preset } : {},
580
+ ...v.tune !== void 0 ? { tune: v.tune } : {},
581
+ ...v.profile !== void 0 ? { profile: v.profile } : {},
582
+ ...v.level !== void 0 ? { level: v.level } : {},
583
+ ...input.pixelFormat !== void 0 ? { pixelFormat: input.pixelFormat } : {},
584
+ ...v.fps !== void 0 ? { fps: v.fps } : {},
585
+ ...v.gopFrames !== void 0 ? { gopFrames: v.gopFrames } : {},
586
+ ...input.forceKeyFramesSeconds !== void 0 ? { forceKeyFramesSeconds: input.forceKeyFramesSeconds } : {},
587
+ ...v.bf !== void 0 ? { bf: v.bf } : {},
588
+ ...v.bitrateKbps !== void 0 ? { bitrateKbps: v.bitrateKbps } : {},
589
+ ...input.rateControl !== void 0 ? { rateControl: input.rateControl } : {},
590
+ ...input.bitstreamFilter !== void 0 ? { bitstreamFilter: input.bitstreamFilter } : {}
591
+ };
592
+ return {
593
+ logLevel: input.logLevel ?? "error",
594
+ decodeHwAccel: input.decodeHwAccel,
595
+ input: {
596
+ url: input.sourceUrl,
597
+ rtspTransport: "tcp",
598
+ fflags: ["+discardcorrupt"],
599
+ ...input.profile.inputArgs?.length ? { extraArgs: input.profile.inputArgs } : {}
600
+ },
601
+ video,
602
+ audio: audioPlanFromEncodeProfile(input.profile.audio),
603
+ threadCount: input.threadCount ?? 0,
604
+ outputArgs: input.profile.outputArgs ?? [],
605
+ sink: input.sink,
606
+ ...input.audioSidecar !== void 0 ? { audioSidecar: input.audioSidecar } : {}
607
+ };
608
+ }
609
+ //#endregion
610
+ //#region src/ffmpeg/encode-defaults.ts
611
+ /**
612
+ * The shape every live egress starts from: H.264 Baseline 3.1 at 720p25.
613
+ * Baseline because it is the one profile every consumer in this repo decodes
614
+ * (Echo, iOS, an old browser); 3.1 because that is what the SDPs advertise.
615
+ */
616
+ var BASE_LIVE_EGRESS_PROFILE = {
617
+ video: {
618
+ codec: "h264",
619
+ profile: "baseline",
620
+ level: "3.1",
621
+ width: 1280,
622
+ height: 720,
623
+ fps: 25,
624
+ bitrateKbps: 2500,
625
+ gopFrames: 25,
626
+ bf: 0,
627
+ preset: "veryfast",
628
+ tune: "zerolatency"
629
+ },
630
+ audio: "passthrough"
631
+ };
632
+ /**
633
+ * The browser WebRTC viewer's transcode profile. Used only when the broker
634
+ * MUST spawn ffmpeg (source codec ∉ the browser-accepted set — an H.265 source
635
+ * on an H.264-only browser). Audio is `passthrough` because werift carries the
636
+ * audio plane out-of-band; putting it in the video pipeline would encode a
637
+ * track nothing reads.
638
+ */
639
+ var WEBRTC_EGRESS_PROFILE = {
640
+ ...BASE_LIVE_EGRESS_PROFILE,
641
+ video: {
642
+ ...BASE_LIVE_EGRESS_PROFILE.video,
643
+ bitrateKbps: 3e3,
644
+ gopFrames: 30,
645
+ preset: "ultrafast"
646
+ },
647
+ audio: "passthrough"
648
+ };
649
+ /**
650
+ * Alexa's egress profile. Alexa's audio ALSO rides the WebRTC session's own
651
+ * audio plane (out-of-band), so this is `passthrough` exactly like the browser
652
+ * — see the ADR for why the previous in-band Opus was encoded and discarded.
653
+ */
654
+ /**
655
+ * Alexa's egress asks for OPUS, and that is a change with a history.
656
+ *
657
+ * The previous profile encoded Opus IN-BAND into an MPEG-TS, where it was
658
+ * discarded: Opus is `stream_type 0x06` and the broker's demuxer maps only
659
+ * `0x0f` (aac), `0x03`/`0x04` (mp2) and `0x81` (ac3). Every frame it produced
660
+ * died at the demuxer, so the encode was replaced with `passthrough` — correct,
661
+ * because paying libopus for nothing is worse than silence.
662
+ *
663
+ * But `passthrough` means the egress emits NO audio at all, and Alexa's audio
664
+ * does not arrive by magic: it rides the published stream, which is what the
665
+ * WebRTC session dials. So the Echo had video and silence either way.
666
+ *
667
+ * Opus here now reaches the egress AUDIO SIDECAR — a separate RTP leg the
668
+ * restreamer grafts onto its SDP — which never touches the MPEG-TS demuxer that
669
+ * killed the in-band attempt. Same codec, different plane, and this one the
670
+ * consumer can actually negotiate.
671
+ */
672
+ var ALEXA_EGRESS_PROFILE = {
673
+ ...BASE_LIVE_EGRESS_PROFILE,
674
+ audio: { codec: "opus" }
675
+ };
676
+ /** VBV window for a consumer whose budget is enforced per second (HomeKit). */
677
+ var RATE_CONTROL_TIGHT = {
678
+ kind: "cbr",
679
+ vbvSeconds: 1
680
+ };
681
+ /** VBV window for a consumer that tolerates a keyframe spike (browser, Echo). */
682
+ var RATE_CONTROL_RELAXED = {
683
+ kind: "cap",
684
+ vbvSeconds: 2
685
+ };
686
+ /**
687
+ * Transcode-DOWN ceiling: the shape a remote/unhealthy WebRTC session is
688
+ * reduced to. 360p / 400 kbps is the `low` rung of the adaptive ladder — a
689
+ * session steered here is already failing, so the ceiling is chosen to survive
690
+ * a bad link, not to look good.
691
+ */
692
+ var TRANSCODE_DOWN_MAX_HEIGHT = 360;
693
+ var TRANSCODE_DOWN_MAX_BITRATE_KBPS = 400;
694
+ /**
695
+ * HomeKit's two-way audio leg. `lowdelay` + a short frame duration because the
696
+ * leg is interactive; `globalHeader` because HAP owns the SDP and ffmpeg must
697
+ * not put extradata in-band. `sampleRateHz` and `frameDurationMs` are
698
+ * NEGOTIATED per session — the controller picks them — so they are absent here
699
+ * and filled in by the caller.
700
+ */
701
+ var HAP_AUDIO_BITRATE_KBPS = 24;
702
+ var HAP_AUDIO_BASE = {
703
+ kind: "encode",
704
+ codec: "opus",
705
+ bitrateKbps: 24,
706
+ channels: 1,
707
+ application: "lowdelay",
708
+ globalHeader: true,
709
+ filter: "aresample=async=1000:first_pts=0"
710
+ };
711
+ /** HomeKit's `-bufsize` for the audio plane — four seconds of its 24 kbps. */
712
+ var HAP_AUDIO_VBV_KBITS = 96;
713
+ /** Seconds between forced IDRs on a HomeKit transcode. */
714
+ var HAP_KEYFRAME_INTERVAL_SEC = 4;
715
+ //#endregion
716
+ //#region src/ffmpeg/hwaccel.ts
717
+ function createHwAccelCache(options) {
718
+ const now = options.now ?? (() => Date.now());
719
+ let value = null;
720
+ let writtenAt = Number.NEGATIVE_INFINITY;
721
+ return {
722
+ read() {
723
+ return now() - writtenAt < options.ttlMs ? value : void 0;
724
+ },
725
+ write(next) {
726
+ value = next;
727
+ writtenAt = now();
728
+ }
729
+ };
730
+ }
731
+ /** `true` when a value means "decode in software". */
732
+ function meansSoftware(value) {
733
+ return !value || value === "none" || value === "copy";
734
+ }
735
+ /**
736
+ * Resolve the `-hwaccel` value for an egress transcode. Hardware is the
737
+ * DEFAULT — an egress that decodes in software on a hub with working vaapi is
738
+ * paying for nothing — and every software outcome is announced.
739
+ *
740
+ * Returns a concrete backend name, the literal `'auto'`, or `null` for
741
+ * software decode (⇒ {@link buildFfmpegArgs} emits no `-hwaccel` at all).
742
+ */
743
+ async function resolveEgressDecodeHwAccel(deps) {
744
+ const override = deps.override;
745
+ if (override !== void 0 && override !== null && override !== "") return meansSoftware(override) ? null : override;
746
+ const cached = deps.cache?.read();
747
+ if (cached !== void 0) return cached;
748
+ let backend;
749
+ try {
750
+ backend = await deps.readDecoderBackend();
751
+ } catch (err) {
752
+ const kernelPreferred = await deps.readKernelPreferred().catch(() => []);
753
+ deps.onFallback({
754
+ reason: "decoder-unreadable",
755
+ kernelPreferred,
756
+ error: err instanceof Error ? err.message : String(err)
757
+ });
758
+ deps.cache?.write(null);
759
+ return null;
760
+ }
761
+ if (backend === "") {
762
+ const kernelPreferred = await deps.readKernelPreferred().catch(() => []);
763
+ deps.onFallback({
764
+ reason: "decoder-unprobed",
765
+ kernelPreferred
766
+ });
767
+ deps.cache?.write(null);
768
+ return null;
769
+ }
770
+ const resolved = meansSoftware(backend) ? null : backend;
771
+ deps.cache?.write(resolved);
772
+ return resolved;
773
+ }
774
+ //#endregion
775
+ //#region src/ffmpeg/sharing-key.ts
776
+ /**
777
+ * The sharing key for `streamBroker.acquireEgressTranscode`.
778
+ *
779
+ * **Two requesters asking for exactly the same argument set reach the same
780
+ * process.** Exact match, never fuzzy: a quantised ladder that merged
781
+ * NEARLY-identical requests would make the stream a consumer receives depend
782
+ * on who else is watching and in what order they arrived — unpredictable in
783
+ * precisely the way a debugging session cannot tolerate. Same arguments ⇒ same
784
+ * process. Different arguments ⇒ different process, and that is fine; the
785
+ * operator accepted the cost ("it's fine to have several processes").
786
+ *
787
+ * Derived from the STRUCTURED plan, not from a flag array. `pipelineKeyFor`
788
+ * (`transcode-pipeline.ts`) folds `getStreamWithCodec`'s raw `outputArgs` into
789
+ * its key, so that method's extensibility hatch and its sharing key are the
790
+ * same field — a consumer needing one extra flag silently forks the child, and
791
+ * two consumers that want the same thing but spell it differently never share.
792
+ * Here every knob is a named field, and defaults are APPLIED before hashing so
793
+ * an omitted field and its explicit default land on the same key.
794
+ */
795
+ /**
796
+ * The transport a CAP request describes. `fragments` is deliberately
797
+ * unreachable from here — the request schema has no way to ask for it, so the
798
+ * cap path can never be handed a fragment child by accident.
799
+ */
800
+ function egressTransportFromRequest(request) {
801
+ return { transport: request.publishLocally === true ? "push" : "dial" };
802
+ }
803
+ /** Absent optional ⇒ this sentinel, so `undefined` and "not set" agree. */
804
+ var UNSET = "\0unset";
805
+ function canonicalVideo(video) {
806
+ return {
807
+ codec: video.codec,
808
+ profile: video.profile ?? UNSET,
809
+ level: video.level ?? UNSET,
810
+ width: video.width ?? -1,
811
+ height: video.height ?? -1,
812
+ fps: video.fps ?? -1,
813
+ bitrateKbps: video.bitrateKbps ?? -1,
814
+ gopFrames: video.gopFrames ?? -1,
815
+ bf: video.bf ?? -1,
816
+ preset: video.preset ?? UNSET,
817
+ tune: video.tune ?? UNSET
818
+ };
819
+ }
820
+ function canonicalAudio(audio) {
821
+ if (audio === "passthrough") return { codec: "passthrough" };
822
+ return {
823
+ codec: audio.codec,
824
+ bitrateKbps: audio.bitrateKbps ?? -1,
825
+ sampleRateHz: audio.sampleRateHz ?? -1,
826
+ channels: audio.channels ?? -1
827
+ };
828
+ }
829
+ /**
830
+ * The normalised plan a key is computed from. Exported so a test — and a
831
+ * future operator-facing "why are these two not sharing?" surface — can diff
832
+ * two requests without reversing a hash.
833
+ */
834
+ function canonicalEgressPlan(request, delivery = egressTransportFromRequest(request)) {
835
+ return {
836
+ deviceId: request.deviceId,
837
+ source: request.source.kind === "profile" ? `profile:${request.source.profile}` : `cam-stream:${request.source.camStreamId}`,
838
+ transport: delivery.transport,
839
+ fragmentMs: delivery.fragmentMs ?? -1,
840
+ video: canonicalVideo(request.encode.video),
841
+ audio: canonicalAudio(request.encode.audio),
842
+ rateControl: request.rateControl ?? "relaxed",
843
+ bitstreamFilter: request.bitstreamFilter ?? UNSET,
844
+ pixelFormat: request.pixelFormat ?? UNSET,
845
+ decodeHwAccel: request.decodeHwAccel ?? UNSET
846
+ };
847
+ }
848
+ /**
849
+ * The refcount / dedup key. `canonicalHash` sorts object keys at every depth,
850
+ * so a request built with a different field order produces the same digest.
851
+ *
852
+ * The handle this keys is IMMUTABLE: there is no `reconfigure`. A consumer
853
+ * whose requirements change RELEASES and re-acquires; the refcount does the
854
+ * rest. That is what stops co-tenants disturbing each other — the co-tenant
855
+ * hazard that made Alexa's shared `derived:alexa-<id>` stream a hazard was
856
+ * exactly a mutable shared object, where one consumer's downgrade dragged
857
+ * every other consumer to 360p.
858
+ */
859
+ function egressTranscodeSharingKey(request, delivery = egressTransportFromRequest(request)) {
860
+ return `egress:${canonicalHash(canonicalEgressPlan(request, delivery))}`;
861
+ }
862
+ //#endregion
224
863
  //#region src/health/wiring-health.ts
225
864
  /**
226
865
  * Deep wiring healthcheck — snapshot of active reachability probes across
@@ -330,6 +969,395 @@ var DEFAULT_RETENTION = {
330
969
  snapshotsDays: 14
331
970
  };
332
971
  //#endregion
972
+ //#region src/interfaces/camera-switches.ts
973
+ /**
974
+ * Per-camera FUNCTION SWITCHES — the one coherent on/off surface over the
975
+ * pipeline functions an operator thinks in terms of.
976
+ *
977
+ * ## This file adds no state
978
+ *
979
+ * Every switch here is a VIEW onto an authority that already existed
980
+ * ([D62](../../../../docs/decisions/adr-0062.md)). The whole point of the
981
+ * group is that there is exactly one place each function is turned off, and
982
+ * the group routes to it:
983
+ *
984
+ * | Switch | Authority | Proven "off stops the work" gate |
985
+ * | --- | --- | --- |
986
+ * | `stream-broker` | `deviceManager.setDisabled` | `StreamBrokerManager.reconcileAllCatalogs` releases the brokers; `ensureBroker` refuses re-creation |
987
+ * | `object-detection` | `deviceManager.setWrapperActive('detection-pipeline')` | `PipelineSettingsStore.resolvePipelineForDevice` returns `{ steps: [], audio: null }` |
988
+ * | `audio-analysis` | `deviceManager.setWrapperActive('audio-analysis')` | `AudioSubscriptionController.subscribeAudioStream` returns `null` before opening the stream |
989
+ * | `recording` | `recording.setDeviceConfig` → `RecordingConfig.enabled` | `band-decision.shouldRecord` returns false; the controller detaches the device |
990
+ * | `notifications` | `notificationRules.setDeviceMuted` | `NotificationCenter.evaluateAndEnqueue` returns before any rule is evaluated |
991
+ * | `privacy-mask` | `privacyMask.setMask({ enabled })` → the CAMERA | the camera blanks the masked regions itself; every stream and recording carries the black boxes |
992
+ * | `device-audio` | `privacyMask.setAudioEnabled` → the CAMERA | the camera stops encoding an audio track at all; every consumer sees silent video |
993
+ *
994
+ * ## The two switches whose authority is not on this server
995
+ *
996
+ * `privacy-mask` and `device-audio` write the CAMERA. That is not a loophole
997
+ * in "the group stores nothing" — it is the purest form of it: the camera
998
+ * holds the fact, every read is a read-through, and there is no server-side
999
+ * copy that could drift. Their availability therefore cannot come from
1000
+ * `listBindableCapsForDeviceType` (a device-NATIVE cap carries no wrappers and
1001
+ * is filtered out there); it comes from the cap's own camera-probed
1002
+ * `privacyMask.getOptions()`, which is strictly more honest — it answers for
1003
+ * THIS camera rather than for the device type
1004
+ * ([D74](../../../../docs/decisions/adr-0074.md)).
1005
+ *
1006
+ * ## `privacy-mask` is the one row whose ON is not "the function is working"
1007
+ *
1008
+ * Every other switch means *this camera's function is doing its job*, so
1009
+ * `enabled: false` is a thing an operator took away. `privacy-mask` means **the
1010
+ * MASK is active** — `enabled: true` is video deliberately obscured. The
1011
+ * polarity is not a choice made here: `addon-export-hap`'s privacy `Switch`
1012
+ * (`builders/privacy-switch.ts`) already mirrors `patch.enabled` verbatim, and
1013
+ * a HomeKit toggle that disagreed with the app's toggle for the same camera is
1014
+ * worse than either surface not having one.
1015
+ *
1016
+ * Two consequences follow and both are load-bearing:
1017
+ *
1018
+ * - **It never counts as `switchedOff`.** `countsAsSwitchedOff` is `false` for
1019
+ * exactly this row. With the polarity above, every camera that has NOT drawn
1020
+ * a privacy mask would otherwise report `switchedOff: ['privacy-mask']` — the
1021
+ * normal, healthy state of most cameras rendered as an operator disablement.
1022
+ * - **Its cost line names BOTH directions.** `costWhenOff` is rendered
1023
+ * unconditionally by both clients, so for this row it has to read correctly
1024
+ * whichever way the switch is sitting.
1025
+ *
1026
+ * The wrapper-binding pair is not a new idea: `legacy-migrations.ts` already
1027
+ * migrated the legacy `audioEnabled` / `pipelineEnabled` /
1028
+ * `motionDetectionEnabled` booleans ONTO `setWrapperActive`. The group is the
1029
+ * surface that decision never got.
1030
+ *
1031
+ * ## Two rules that are load-bearing
1032
+ *
1033
+ * - **Recording's switch is `enabled`, never the bands.** `bands` is the only
1034
+ * authored intent and `mode` is derived from it (`deriveRecordingMode`).
1035
+ * Expressing "off" by clearing bands destroys the operator's schedule and
1036
+ * turning the camera back on would then silently record nothing.
1037
+ * - **A switch that is off must be reported as off**, not merely produce
1038
+ * nothing. {@link CameraSwitch.enabled} is what a status surface renders as
1039
+ * "disabled by an operator" instead of "broken" — see
1040
+ * `CameraStatus.switchedOff`.
1041
+ */
1042
+ /**
1043
+ * The functions the operator named — five on 2026-08-05, plus the camera's own
1044
+ * microphone on 2026-08-07. Deliberately NOT one id per pipeline step: face
1045
+ * recognition and plate/LPR are per-step toggles on
1046
+ * `pipelineOrchestrator.setCameraStepToggle` and belong in the pipeline
1047
+ * editor, not in a safety group.
1048
+ */
1049
+ var CameraSwitchIdSchema = z.enum([
1050
+ "stream-broker",
1051
+ "object-detection",
1052
+ "privacy-mask",
1053
+ "device-audio",
1054
+ "audio-analysis",
1055
+ "recording",
1056
+ "notifications"
1057
+ ]);
1058
+ /**
1059
+ * Stable render order — broadest blast radius first, and a source before the
1060
+ * thing that consumes it. `device-audio` sits ABOVE `audio-analysis` because
1061
+ * turning the microphone off leaves the analyzer with nothing to analyse; the
1062
+ * reverse is not true.
1063
+ *
1064
+ * `privacy-mask` sits directly ABOVE `device-audio` because they are literal
1065
+ * siblings — one cap, one device plane, video then audio — and NOT above
1066
+ * `object-detection` despite feeding it: a mask blanks REGIONS, so its blast
1067
+ * radius is partial, and the "broadest first" rule does not rank a partial
1068
+ * control above a whole-function one.
1069
+ */
1070
+ var CAMERA_SWITCH_ORDER = [
1071
+ "stream-broker",
1072
+ "object-detection",
1073
+ "privacy-mask",
1074
+ "device-audio",
1075
+ "audio-analysis",
1076
+ "recording",
1077
+ "notifications"
1078
+ ];
1079
+ /**
1080
+ * WHERE the switch's state actually lives. A discriminated union rather than a
1081
+ * string so both the writer (the orchestrator's `setCameraSwitch`) and any
1082
+ * reader can exhaustively narrow — and so "the group added a parallel map" is
1083
+ * a compile error rather than a review comment.
1084
+ */
1085
+ var CameraSwitchAuthoritySchema = z.discriminatedUnion("kind", [
1086
+ z.object({ kind: z.literal("device-disabled") }),
1087
+ z.object({
1088
+ kind: z.literal("wrapper-binding"),
1089
+ capName: z.string()
1090
+ }),
1091
+ z.object({ kind: z.literal("recording-config") }),
1092
+ z.object({ kind: z.literal("notification-mute") }),
1093
+ z.object({
1094
+ kind: z.literal("camera-audio"),
1095
+ capName: z.string()
1096
+ }),
1097
+ z.object({
1098
+ kind: z.literal("camera-mask"),
1099
+ capName: z.string()
1100
+ })
1101
+ ]);
1102
+ /**
1103
+ * Why a switch is not offered for this camera. Rendered instead of the
1104
+ * control, never as a dead control — an absent function and a broken one must
1105
+ * not look the same.
1106
+ */
1107
+ var CameraSwitchUnavailableReasonSchema = z.enum([
1108
+ "no-provider",
1109
+ "source-unreachable",
1110
+ "not-configured"
1111
+ ]);
1112
+ /**
1113
+ * One switch, resolved for one camera.
1114
+ *
1115
+ * `label` and `costWhenOff` travel ON THE WIRE rather than being looked up
1116
+ * client-side: the viewer is a separate repository that does not import
1117
+ * `@camstack/types`, and a cost line duplicated in two clients is a cost line
1118
+ * that will disagree with itself. Five rows per camera is nothing.
1119
+ */
1120
+ var CameraSwitchSchema = z.object({
1121
+ id: CameraSwitchIdSchema,
1122
+ label: z.string(),
1123
+ /**
1124
+ * What the operator LOSES while this is off, in one sentence. Required, not
1125
+ * optional: a switch that cannot say what it costs should not ship.
1126
+ */
1127
+ costWhenOff: z.string(),
1128
+ /** False = do not render a control. `unavailableReason` says why. */
1129
+ available: z.boolean(),
1130
+ unavailableReason: CameraSwitchUnavailableReasonSchema.optional(),
1131
+ /** Current state. Meaningless when `available` is false — read it as `true`. */
1132
+ enabled: z.boolean(),
1133
+ authority: CameraSwitchAuthoritySchema
1134
+ });
1135
+ /** The whole group for one camera. */
1136
+ var CameraSwitchGroupSchema = z.object({
1137
+ deviceId: z.number().int(),
1138
+ switches: z.array(CameraSwitchSchema).readonly(),
1139
+ /** Unix ms when the group was composed server-side. */
1140
+ fetchedAt: z.number()
1141
+ });
1142
+ /**
1143
+ * The wrapper capability each wrapper-backed switch controls. Named constants
1144
+ * because the same strings appear in `legacy-migrations.ts`, in
1145
+ * `isCapActiveForDevice` call sites and in the fake harness — a typo in any of
1146
+ * them is a switch that silently writes a binding nobody reads.
1147
+ */
1148
+ var DETECTION_PIPELINE_CAP_NAME = "detection-pipeline";
1149
+ var AUDIO_ANALYSIS_CAP_NAME = "audio-analysis";
1150
+ /**
1151
+ * The device-NATIVE cap that owns what the camera does not capture — masked
1152
+ * video regions and, since 2026-08-07, the microphone. Named here because the
1153
+ * `camera-audio` authority, the orchestrator's gather and the fake harness all
1154
+ * have to agree on it.
1155
+ */
1156
+ var PRIVACY_MASK_CAP_NAME = "privacy-mask";
1157
+ /**
1158
+ * THE catalog. One entry per switch; the cost lines are the operator-facing
1159
+ * contract and are written to be true rather than reassuring.
1160
+ */
1161
+ var CAMERA_SWITCH_CATALOG = {
1162
+ "stream-broker": {
1163
+ id: "stream-broker",
1164
+ label: "Camera",
1165
+ costWhenOff: "Off: the whole camera stops. No live view, no recording, no detection and no notifications — its streams are released and nothing dials it again until you turn it back on.",
1166
+ authority: { kind: "device-disabled" },
1167
+ countsAsSwitchedOff: true
1168
+ },
1169
+ "object-detection": {
1170
+ id: "object-detection",
1171
+ label: "Object detection & tracking",
1172
+ costWhenOff: "Off: nothing is detected or tracked on this camera, so it produces no events — and with no events there are no object notifications and no event-triggered recording. Live view and continuous recording are unaffected.",
1173
+ authority: {
1174
+ kind: "wrapper-binding",
1175
+ capName: DETECTION_PIPELINE_CAP_NAME
1176
+ },
1177
+ countsAsSwitchedOff: true
1178
+ },
1179
+ "privacy-mask": {
1180
+ id: "privacy-mask",
1181
+ label: "Privacy mask",
1182
+ costWhenOff: "On: the zones you drew on this camera are blacked out by the camera itself — live view, playback, exports and detection all see the black boxes, and nothing behind them was ever recorded. Off: the camera captures the whole frame, and your zones are kept for when you turn it back on.",
1183
+ authority: {
1184
+ kind: "camera-mask",
1185
+ capName: PRIVACY_MASK_CAP_NAME
1186
+ },
1187
+ countsAsSwitchedOff: false
1188
+ },
1189
+ "device-audio": {
1190
+ id: "device-audio",
1191
+ label: "Camera microphone",
1192
+ costWhenOff: "Off: the camera captures no sound at all. Live view and recordings become silent video, and audio detection and classification have nothing left to analyse — turning them back on will not recover it. The picture, motion and object detection are unaffected, and two-way talk still works. Applies at the camera, so every consumer sees the same silence.",
1193
+ authority: {
1194
+ kind: "camera-audio",
1195
+ capName: PRIVACY_MASK_CAP_NAME
1196
+ },
1197
+ countsAsSwitchedOff: true
1198
+ },
1199
+ "audio-analysis": {
1200
+ id: "audio-analysis",
1201
+ label: "Audio detection & classification",
1202
+ costWhenOff: "Off: no audio is decoded or classified for this camera. Tracks carry no audio labels and no audio-triggered rule can fire. Audio recorded alongside video is unaffected.",
1203
+ authority: {
1204
+ kind: "wrapper-binding",
1205
+ capName: AUDIO_ANALYSIS_CAP_NAME
1206
+ },
1207
+ countsAsSwitchedOff: true
1208
+ },
1209
+ recording: {
1210
+ id: "recording",
1211
+ label: "Recording",
1212
+ costWhenOff: "Off: nothing new is written to disk. Footage already recorded stays, but retention keeps deleting it — so this camera’s history shrinks and is not replaced. Your recording schedule is kept and resumes when you turn it back on.",
1213
+ authority: { kind: "recording-config" },
1214
+ countsAsSwitchedOff: true
1215
+ },
1216
+ notifications: {
1217
+ id: "notifications",
1218
+ label: "Notifications",
1219
+ costWhenOff: "Off: this camera never notifies anyone, on any rule, with no expiry. Detection, events and recording carry on exactly as before — you simply stop being told about them.",
1220
+ authority: { kind: "notification-mute" },
1221
+ countsAsSwitchedOff: true
1222
+ }
1223
+ };
1224
+ /** Resolve one switch's `{ available, enabled }` pair. */
1225
+ function resolveState(descriptor, input) {
1226
+ switch (descriptor.authority.kind) {
1227
+ case "device-disabled": return {
1228
+ available: true,
1229
+ enabled: !input.deviceDisabled
1230
+ };
1231
+ case "wrapper-binding": {
1232
+ const capName = descriptor.authority.capName;
1233
+ if (input.bindableCapNames === null || input.activeWrapperCapNames === null) return {
1234
+ available: false,
1235
+ enabled: true,
1236
+ unavailableReason: "source-unreachable"
1237
+ };
1238
+ if (!input.bindableCapNames.includes(capName)) return {
1239
+ available: false,
1240
+ enabled: true,
1241
+ unavailableReason: "no-provider"
1242
+ };
1243
+ return {
1244
+ available: true,
1245
+ enabled: input.activeWrapperCapNames.includes(capName)
1246
+ };
1247
+ }
1248
+ case "recording-config":
1249
+ if (input.recordingEnabled === null) return {
1250
+ available: false,
1251
+ enabled: true,
1252
+ unavailableReason: "source-unreachable"
1253
+ };
1254
+ return {
1255
+ available: true,
1256
+ enabled: input.recordingEnabled
1257
+ };
1258
+ case "notification-mute":
1259
+ if (input.notificationsMuted === null) return {
1260
+ available: false,
1261
+ enabled: true,
1262
+ unavailableReason: "source-unreachable"
1263
+ };
1264
+ return {
1265
+ available: true,
1266
+ enabled: !input.notificationsMuted
1267
+ };
1268
+ case "camera-audio": {
1269
+ const audio = input.deviceAudio;
1270
+ if (audio === null) return {
1271
+ available: false,
1272
+ enabled: true,
1273
+ unavailableReason: "source-unreachable"
1274
+ };
1275
+ if (!audio.supported) return {
1276
+ available: false,
1277
+ enabled: true,
1278
+ unavailableReason: "no-provider"
1279
+ };
1280
+ if (audio.enabled === null) return {
1281
+ available: false,
1282
+ enabled: true,
1283
+ unavailableReason: "source-unreachable"
1284
+ };
1285
+ return {
1286
+ available: true,
1287
+ enabled: audio.enabled
1288
+ };
1289
+ }
1290
+ case "camera-mask": {
1291
+ const mask = input.privacyMask;
1292
+ if (mask === null) return {
1293
+ available: false,
1294
+ enabled: false,
1295
+ unavailableReason: "source-unreachable"
1296
+ };
1297
+ if (!mask.supported) return {
1298
+ available: false,
1299
+ enabled: false,
1300
+ unavailableReason: "no-provider"
1301
+ };
1302
+ if (mask.configuredRegions === null || mask.enabled === null) return {
1303
+ available: false,
1304
+ enabled: false,
1305
+ unavailableReason: "source-unreachable"
1306
+ };
1307
+ if (mask.configuredRegions === 0) return {
1308
+ available: false,
1309
+ enabled: false,
1310
+ unavailableReason: "not-configured"
1311
+ };
1312
+ return {
1313
+ available: true,
1314
+ enabled: mask.enabled
1315
+ };
1316
+ }
1317
+ }
1318
+ }
1319
+ /**
1320
+ * Pure derivation of the whole group. No I/O — the orchestrator gathers, this
1321
+ * decides, so the decision is testable without a hub.
1322
+ *
1323
+ * Order is {@link CAMERA_SWITCH_ORDER}; unavailable switches are RETURNED
1324
+ * rather than filtered out, so a client can explain the gap instead of
1325
+ * silently rendering four buttons where another camera shows five.
1326
+ */
1327
+ function deriveCameraSwitches(input) {
1328
+ return CAMERA_SWITCH_ORDER.map((id) => {
1329
+ const descriptor = CAMERA_SWITCH_CATALOG[id];
1330
+ const state = resolveState(descriptor, input);
1331
+ return {
1332
+ id: descriptor.id,
1333
+ label: descriptor.label,
1334
+ costWhenOff: descriptor.costWhenOff,
1335
+ available: state.available,
1336
+ ...state.unavailableReason !== void 0 ? { unavailableReason: state.unavailableReason } : {},
1337
+ enabled: state.enabled,
1338
+ authority: descriptor.authority
1339
+ };
1340
+ });
1341
+ }
1342
+ /**
1343
+ * The ids an operator has switched OFF, for a status surface.
1344
+ *
1345
+ * This is the answer to "a disabled function must be visible as DISABLED, not
1346
+ * merely quiet": a camera reporting zero detections with
1347
+ * `switchedOff: ['object-detection']` was turned off; the same camera with an
1348
+ * empty list is broken. Unavailable switches never appear — a function nobody
1349
+ * provides was not switched off by anyone.
1350
+ *
1351
+ * `privacy-mask` never appears either, whichever way it is sitting, because its
1352
+ * ON means "the mask is active" rather than "the function works"
1353
+ * ({@link CameraSwitchDescriptor.countsAsSwitchedOff}). Without that filter the
1354
+ * ordinary state of every camera nobody has masked would carry a
1355
+ * "switched off" badge, and the badge that matters would be lost in it.
1356
+ */
1357
+ function switchedOffIds(switches) {
1358
+ return switches.filter((s) => CAMERA_SWITCH_CATALOG[s.id].countsAsSwitchedOff && s.available && !s.enabled).map((s) => s.id);
1359
+ }
1360
+ //#endregion
333
1361
  //#region src/interfaces/device-capabilities/camera.ts
334
1362
  /** Friendly display labels for stream quality IDs. */
335
1363
  var STREAM_QUALITY_LABELS = {
@@ -525,14 +1553,16 @@ var OpsLogOpSchema = z.enum([
525
1553
  "manual-delete",
526
1554
  "rescan",
527
1555
  "retention-run",
528
- "relocate"
1556
+ "relocate",
1557
+ "orphan-audit"
529
1558
  ]);
530
1559
  /** Why the operation ran. */
531
1560
  var OpsLogReasonSchema = z.enum([
532
1561
  "retention",
533
1562
  "quota",
534
1563
  "manual",
535
- "operator"
1564
+ "operator",
1565
+ "maintenance"
536
1566
  ]);
537
1567
  /** One audit row, shared verbatim by both domains. */
538
1568
  var OpsLogEntrySchema = z.object({
@@ -3313,6 +4343,126 @@ var RtpSourceSchema = z.object({
3313
4343
  encoder: z.string(),
3314
4344
  pipelineKey: z.string()
3315
4345
  });
4346
+ /**
4347
+ * The encode request — **structured and serialisable, with NO raw-flag escape
4348
+ * hatch.** This is deliberate and it is the one lesson taken from
4349
+ * `getStreamWithCodec`: that method's `outputArgs: string[]` is simultaneously
4350
+ * its extensibility mechanism AND part of `pipelineKeyFor`'s sharing key, so
4351
+ * adding a flag silently forks the shared child, and two consumers that mean
4352
+ * the same thing but spell it differently never share. Here every knob is a
4353
+ * NAMED field: a new requirement becomes a schema field (and a codegen run),
4354
+ * never an opaque array.
4355
+ *
4356
+ * `inputArgs` / `outputArgs` are omitted from the profile for the same reason.
4357
+ * The operator-facing derived-stream transform editor still has them — that is
4358
+ * a different surface (`publishCameraStream({ kind: 'derived' })`) with a
4359
+ * different purpose (reshaping a badly-behaved SOURCE), and it is unchanged.
4360
+ */
4361
+ var EgressEncodeSchema = EncodeProfileSchema.omit({
4362
+ inputArgs: true,
4363
+ outputArgs: true
4364
+ });
4365
+ /**
4366
+ * How the encoder is bounded. `'tight'` is a one-second VBV window for a
4367
+ * consumer whose budget is enforced per second (HomeKit); `'relaxed'` is two
4368
+ * seconds, letting a keyframe spike borrow from the next second (a browser,
4369
+ * an Echo). Named rather than numeric so the INTENT survives.
4370
+ */
4371
+ var EgressRateControlSchema = z.enum(["tight", "relaxed"]);
4372
+ var EgressTranscodeRequestSchema = z.object({
4373
+ deviceId: z.number().int().nonnegative(),
4374
+ /** Which published stream to read. */
4375
+ source: z.discriminatedUnion("kind", [z.object({
4376
+ kind: z.literal("profile"),
4377
+ profile: CamProfileSchema
4378
+ }), z.object({
4379
+ kind: z.literal("cam-stream"),
4380
+ camStreamId: z.string().min(1)
4381
+ })]),
4382
+ encode: EgressEncodeSchema,
4383
+ rateControl: EgressRateControlSchema.optional(),
4384
+ /**
4385
+ * `-bsf:v`. A consumer that negotiates its OWN SDP (HomeKit) cannot carry
4386
+ * out-of-band extradata and needs `dump_extra` on both the copy and encode
4387
+ * branches. Enumerated, not free text.
4388
+ */
4389
+ bitstreamFilter: z.enum([
4390
+ "dump_extra",
4391
+ "h264_mp4toannexb",
4392
+ "hevc_mp4toannexb"
4393
+ ]).optional(),
4394
+ /**
4395
+ * Publish the transcode as a LOCAL push cam stream, instead of leaving the
4396
+ * consumer to dial the returned url. The broker picks the id and returns it
4397
+ * as `camStreamId` — a caller-supplied one would be circular, since the
4398
+ * sharing key is computed FROM this request.
4399
+ *
4400
+ * The url is still returned and still the contract for a transcode pinned to
4401
+ * another node. But dialling it locally costs an RTSP round trip that changes
4402
+ * the transport underneath the consumer: a dialled stream is an RTP source,
4403
+ * so `isRtpSource()` is true and the session takes the RTP-passthrough +
4404
+ * repacketizer branch. The push branch — the one the derived mechanism has
4405
+ * live hours on — is never reached. Measured on Alexa: broker registered, RTP
4406
+ * arriving, key frame arriving, black screen, on a chain healthy at every
4407
+ * other point.
4408
+ *
4409
+ * Same idea the transport already applies to CALLS, where `classifyCapRoute`
4410
+ * gives priority to `hub-in-process` so a local call never leaves the node.
4411
+ * This is that rule for media.
4412
+ */
4413
+ publishLocally: z.boolean().optional(),
4414
+ pixelFormat: z.enum(["yuv420p", "nv12"]).optional(),
4415
+ /**
4416
+ * Operator/consumer override for decode hardware. ABSENT is the normal case
4417
+ * and the one that matters: the broker then resolves the backend from the
4418
+ * DECODER ADDON's per-node `probedBestHwaccel` (see
4419
+ * `@camstack/types` `ffmpeg/hwaccel.ts`), which is the ranking known to work
4420
+ * on this hardware — never the raw kernel resolver's qsv-first order.
4421
+ */
4422
+ decodeHwAccel: z.enum([
4423
+ "auto",
4424
+ "none",
4425
+ "videotoolbox",
4426
+ "vaapi",
4427
+ "qsv",
4428
+ "cuda"
4429
+ ]).optional(),
4430
+ /**
4431
+ * Host to embed in the returned restream `url`. The broker mints hub-local
4432
+ * `127.0.0.1` URLs; a consumer on another node passes a cluster-resolvable
4433
+ * host (`NodeTopologyService.reachableHostByNode`) so the returned URL is
4434
+ * dialable from there. Same contract as `getStreamWithCodec.hostname` —
4435
+ * `substituteRtspHost` rewrites only the dial address, never the restreamer.
4436
+ */
4437
+ hostname: z.string().optional(),
4438
+ /** Attribution for the broker panel. Never part of the sharing key. */
4439
+ tag: z.string().optional()
4440
+ });
4441
+ var EgressTranscodeSchema = z.object({
4442
+ /** Dial-able RTSP url (host-substituted when `hostname` was supplied). */
4443
+ url: z.string(),
4444
+ /** Release handle. Refcounted — the child dies when the last holder releases. */
4445
+ pipelineKey: z.string(),
4446
+ videoCodec: z.enum(["H264", "H265"]),
4447
+ resolution: z.object({
4448
+ width: z.number().int().positive(),
4449
+ height: z.number().int().positive()
4450
+ }),
4451
+ transcoded: z.boolean(),
4452
+ encoder: z.string(),
4453
+ /**
4454
+ * The decode backend the child ACTUALLY ran with — `null` for software.
4455
+ * Returned rather than assumed: a consumer that asked for hardware and got
4456
+ * software needs to be able to see that without reading the broker's logs.
4457
+ */
4458
+ decodeHwAccel: z.string().nullable(),
4459
+ /**
4460
+ * Set when `publishLocally` was honoured: attach to THIS instead of dialling
4461
+ * `url`, and the session takes the push/deframe transport rather than the
4462
+ * RTP-passthrough one. `null` means the consumer must dial.
4463
+ */
4464
+ camStreamId: z.string().nullable()
4465
+ });
3316
4466
  var streamBrokerCapability = {
3317
4467
  name: "stream-broker",
3318
4468
  scope: "system",
@@ -3484,6 +4634,40 @@ var streamBrokerCapability = {
3484
4634
  auth: "admin"
3485
4635
  }),
3486
4636
  /**
4637
+ * THE ffmpeg primitive. Acquire an encoded stream matching a structured
4638
+ * encode plan; the broker builds the argv through the ONE builder
4639
+ * (`@camstack/types` `ffmpeg/invocation.ts`), resolves decode hardware from
4640
+ * the DECODER ADDON's per-node ranking, and returns a dialable RTSP url.
4641
+ *
4642
+ * **Refcounted and deduplicated on EXACT match.** Two requesters whose
4643
+ * requests produce the same `egressTranscodeSharingKey` receive the SAME
4644
+ * `pipelineKey` and the same child process. Nearly-identical requests are
4645
+ * NOT merged.
4646
+ *
4647
+ * **The handle is immutable.** There is deliberately no `reconfigure`
4648
+ * method and this one never accepts a `pipelineKey` alongside encode
4649
+ * parameters: a consumer whose requirements change releases and
4650
+ * re-acquires. A mutable shared handle is exactly what made Alexa's old
4651
+ * `derived:alexa-<id>` stream a co-tenant hazard — one consumer's
4652
+ * downgrade dragged every other consumer down with it.
4653
+ *
4654
+ * Placement: unpinned, `classifyCapRoute` serves this hub-in-process
4655
+ * (Priority 1). A caller that wants the transcode elsewhere passes
4656
+ * `nodePin(nodeId)` and a `hostname` it can dial.
4657
+ */
4658
+ acquireEgressTranscode: method(EgressTranscodeRequestSchema, EgressTranscodeSchema, {
4659
+ kind: "mutation",
4660
+ auth: "admin"
4661
+ }),
4662
+ /** Drop one reference. The child dies when the last holder releases. */
4663
+ releaseEgressTranscode: method(z.object({ pipelineKey: z.string() }), z.object({
4664
+ released: z.boolean(),
4665
+ refcount: z.number().int().nonnegative()
4666
+ }), {
4667
+ kind: "mutation",
4668
+ auth: "admin"
4669
+ }),
4670
+ /**
3487
4671
  * ── Decoded audio-chunk plane (Phase 5 / D9) ──────────────────────
3488
4672
  *
3489
4673
  * The serialisable replacement for the live-object `IStreamBroker.
@@ -10028,12 +11212,13 @@ var NcConditionsSchema = z.object({
10028
11212
  * source; otherwise the subject's source must equal it. Legacy records
10029
11213
  * with no stamped source are treated as `pipeline`. The union spans both
10030
11214
  * record kinds — object events carry `pipeline` | `onboard`, synthetic
10031
- * tracks carry `sensor`.
11215
+ * tracks carry `sensor` (a linked device) or `audio` (a D62 audio marker).
10032
11216
  */
10033
11217
  source: z.enum([
10034
11218
  "pipeline",
10035
11219
  "onboard",
10036
11220
  "sensor",
11221
+ "audio",
10037
11222
  "any"
10038
11223
  ]).optional(),
10039
11224
  /**
@@ -10587,6 +11772,10 @@ var NC_CONDITION_CATALOG = [
10587
11772
  {
10588
11773
  value: "sensor",
10589
11774
  label: "Sensor"
11775
+ },
11776
+ {
11777
+ value: "audio",
11778
+ label: "Audio marker"
10590
11779
  }
10591
11780
  ],
10592
11781
  operator: "in",
@@ -10597,7 +11786,7 @@ var NC_CONDITION_CATALOG = [
10597
11786
  "package-event"
10598
11787
  ],
10599
11788
  phase: "P1",
10600
- description: "pipeline / onboard / sensor; a record with no stamped source counts as pipeline."
11789
+ description: "pipeline / onboard / sensor / audio; a record with no stamped source counts as pipeline."
10601
11790
  },
10602
11791
  {
10603
11792
  id: "deviceState",
@@ -10949,6 +12138,35 @@ var notificationRulesCapability = {
10949
12138
  auth: "admin"
10950
12139
  }),
10951
12140
  /**
12141
+ * PERMANENT per-camera mute — the notifications half of the per-camera
12142
+ * function switch group ([D61](../../../../docs/decisions/adr-0067.md)).
12143
+ *
12144
+ * Deliberately NOT a snooze. A snooze is bounded at
12145
+ * {@link NC_SNOOZE_MAX_MINUTES} on purpose — "a snooze that could not
12146
+ * expire would be an outage the operator asked for once and forgot" — and
12147
+ * widening it to express "this camera never notifies" would destroy that
12148
+ * property for every snooze. A mute is the other thing: an explicit,
12149
+ * indefinite, admin-only decision, visible in the switch group next to the
12150
+ * other four, and reported on `CameraStatus.switchedOff` so a silent
12151
+ * camera never reads as a working one.
12152
+ *
12153
+ * Returned as ONE list rather than a per-camera query: the group's reader
12154
+ * needs every camera's state, and a per-camera fan-out over the viewer's
12155
+ * single WebSocket is N frames serialised on one socket.
12156
+ */
12157
+ listDeviceMutes: method(z.object({}), z.object({ mutedDeviceIds: z.array(z.number().int()).readonly() }), { auth: "admin" }),
12158
+ /**
12159
+ * Mute or unmute one camera. Idempotent; an unmute of a camera that was
12160
+ * never muted succeeds.
12161
+ */
12162
+ setDeviceMuted: method(z.object({
12163
+ deviceId: z.number().int(),
12164
+ muted: z.boolean()
12165
+ }), z.object({ success: z.literal(true) }), {
12166
+ kind: "mutation",
12167
+ auth: "admin"
12168
+ }),
12169
+ /**
10952
12170
  * Dry-run a rule against recently persisted records (object events for
10953
12171
  * `immediate`, closed tracks for `track-end`). Mutation kind only to
10954
12172
  * carry the full rule object safely; no side effects.
@@ -11366,12 +12584,60 @@ var TrackAudioLabelSchema = z.object({
11366
12584
  });
11367
12585
  /**
11368
12586
  * How a track was produced. `pipeline` (default / absent) = the spatial
11369
- * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
11370
- * linked sensor/control state change (no positions; carries a snapshot). The
11371
- * spatial subsystems (tracker association, occupancy count, re-id/embedding,
11372
- * resurrection) MUST skip `sensor` tracks they have no bbox trajectory.
12587
+ * detection+tracking pipeline. Every OTHER value is a SYNTHETIC projection
12588
+ * no positions, a single snapshot, and no bbox trajectory at all:
12589
+ *
12590
+ * - `sensor` — a linked sensor/control device state change.
12591
+ * - `audio` — an audio event on the camera itself that was anomalous for
12592
+ * THAT camera, loud, and heard while nothing visual was happening (D62).
12593
+ *
12594
+ * The spatial subsystems (tracker association, occupancy count, re-id /
12595
+ * embedding, resurrection) MUST skip every synthetic source. Test for that
12596
+ * with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
12597
+ * check silently readmits every source added after it was written.
11373
12598
  */
11374
- var TrackSourceSchema = z.enum(["pipeline", "sensor"]);
12599
+ var TrackSourceSchema = z.enum([
12600
+ "pipeline",
12601
+ "sensor",
12602
+ "audio"
12603
+ ]);
12604
+ /**
12605
+ * Per-track OPERATOR flags — set by hand from the admin UI or the viewer, never
12606
+ * by the pipeline. Spread into `TrackSchema` and `KeyEventSchema` from one place
12607
+ * so the two surfaces cannot drift.
12608
+ *
12609
+ * **Absent ≠ false.** A track that has never been touched omits the field; an
12610
+ * explicitly un-flagged track carries `false`. Legacy rows written before the
12611
+ * columns existed read as absent, and a consumer that needs a boolean should say
12612
+ * `flag === true`, not `flag !== false`.
12613
+ *
12614
+ * What the flags DO is deliberately UNDEFINED at the time of writing: they are
12615
+ * operator curation, and the behaviour they drive will be specified separately.
12616
+ * In particular a `markForTrain` track is NOT pinned against retention — see
12617
+ * `docs/decisions/adr-0059.md` for why that is a store-level change, not a flag.
12618
+ */
12619
+ var TrackFlagFields = {
12620
+ /** Operator marked this track as training material. */
12621
+ markForTrain: z.boolean().optional(),
12622
+ /** Operator marked this track for diagnostic attention. */
12623
+ debug: z.boolean().optional()
12624
+ };
12625
+ /**
12626
+ * The write half: a PARTIAL patch. An omitted key is left untouched, so setting
12627
+ * one flag can never clear the other — the toggles are independent and are
12628
+ * driven from three surfaces that do not know about each other.
12629
+ */
12630
+ var TrackFlagsPatchSchema = z.object(TrackFlagFields);
12631
+ /**
12632
+ * The resolved flag state after a write. Both fields are REQUIRED here (absent
12633
+ * collapses to `false`) so a caller can drive a toggle's checked state off the
12634
+ * mutation result without a re-fetch.
12635
+ */
12636
+ var TrackFlagsSchema = z.object({
12637
+ trackId: z.string(),
12638
+ markForTrain: z.boolean(),
12639
+ debug: z.boolean()
12640
+ });
11375
12641
  var TrackSchema = z.object({
11376
12642
  trackId: z.string(),
11377
12643
  deviceId: z.number(),
@@ -11414,7 +12680,8 @@ var TrackSchema = z.object({
11414
12680
  /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
11415
12681
  * Populated from the persisted envelope columns on historical reads;
11416
12682
  * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
11417
- envelope: TrackEnvelopeSchema.optional()
12683
+ envelope: TrackEnvelopeSchema.optional(),
12684
+ ...TrackFlagFields
11418
12685
  });
11419
12686
  var BaseEventFields = {
11420
12687
  id: z.string(),
@@ -11635,7 +12902,8 @@ var KeyEventSchema = z.object({
11635
12902
  /** Highest-confidence ObjectEvent id for the track (empty when none). */
11636
12903
  bestEventId: z.string(),
11637
12904
  /** Track lifetime in ms (lastSeen - firstSeen). */
11638
- windowMs: z.number().optional()
12905
+ windowMs: z.number().optional(),
12906
+ ...TrackFlagFields
11639
12907
  });
11640
12908
  var TrackedDetectionSchema = z.object({
11641
12909
  trackId: z.string(),
@@ -11721,7 +12989,31 @@ var RebuildObjectEmbeddingsInput = z.object({
11721
12989
  since: z.number().optional(),
11722
12990
  until: z.number().optional(),
11723
12991
  /** Stop after this many tracks; the result reports whether more remain. */
11724
- maxTracks: z.number().int().positive().optional()
12992
+ maxTracks: z.number().int().positive().optional(),
12993
+ /**
12994
+ * Run every embedding on THIS node instead of round-robining the fleet.
12995
+ *
12996
+ * Named `executeOnNodeId` and not `nodeId` on purpose: an inline `nodeId`
12997
+ * field in cap args is read by `parent-unowned-call.ts` as a ROUTING PIN, so
12998
+ * calling it that would pin the rebuild REQUEST itself to that node — the
12999
+ * rebuild orchestration lives on the hub, and only the per-track step runs
13000
+ * remotely. This field is data; the per-track pin is applied inside.
13001
+ *
13002
+ * Absent ⇒ round-robin over every online node whose runner can serve the
13003
+ * pinned model.
13004
+ */
13005
+ executeOnNodeId: z.string().optional(),
13006
+ /**
13007
+ * Milliseconds to wait between tracks; omit for the built-in default, `0` to
13008
+ * run flat out.
13009
+ *
13010
+ * A rebuild is bulk maintenance on hub-main's single thread. Measured
13011
+ * 2026-08-06, an unpaced pass held that thread busy 82.2 s out of 120 and
13012
+ * pushed `nodes.topology` from 0.25 s to 26 s for 43 minutes. The value in
13013
+ * force is logged at start and finish so a deliberately slow pass reads
13014
+ * differently from a stalled one.
13015
+ */
13016
+ pacingMs: z.number().int().nonnegative().optional()
11725
13017
  });
11726
13018
  /**
11727
13019
  * Result of emptying the CLIP index.
@@ -11755,13 +13047,23 @@ var RebuildStatusSchema = z.object({
11755
13047
  /** Tracks with no usable detection box. */
11756
13048
  missingBbox: z.number(),
11757
13049
  /**
11758
- * Tracks the pipeline REFUSED rather than broke on: the camera is not
11759
- * attached, or `clip-embedding` is not enabled in its step tree. Separate
11760
- * from `failed` because the remedy is a configuration change, not an engine
11761
- * investigation and because a pass over decommissioned cameras would
11762
- * otherwise read as a total engine outage.
13050
+ * Tracks an executing node REFUSED rather than broke on an unreadable key
13051
+ * frame, a step that threw. Separate from `failed` because the remedy is
13052
+ * different, and because a whole camera silently contributing zero vectors
13053
+ * is the shape of failure a rebuild must never hide.
11763
13054
  */
11764
13055
  notRunnable: z.number(),
13056
+ /**
13057
+ * The pass stopped because NO node could serve the pinned model.
13058
+ *
13059
+ * Distinct from `notRunnable` on purpose: that one says "this track was
13060
+ * refused", this one says "the cluster cannot do this work at all" — every
13061
+ * candidate node either lacks the `clip-embedding` step, lacks a build of the
13062
+ * pinned model for its engine format, or dropped out. The remedy is a model /
13063
+ * engine change, not a per-camera one. Non-zero here always comes with
13064
+ * `complete: false`.
13065
+ */
13066
+ noCapableNode: z.number(),
11765
13067
  failed: z.number(),
11766
13068
  /** Set once a pass ends: true only when EVERYTHING was covered. */
11767
13069
  complete: z.boolean().nullable(),
@@ -11951,6 +13253,31 @@ var pipelineAnalyticsCapability = {
11951
13253
  auth: "admin"
11952
13254
  }),
11953
13255
  /**
13256
+ * Set the per-track operator flags (`markForTrain`, `debug`) on ONE track.
13257
+ * The patch is PARTIAL — an omitted key is left untouched — because the
13258
+ * three surfaces that write it (admin Events grid, viewer track detail,
13259
+ * viewer cluster detail) each own one toggle and must not clobber the other.
13260
+ *
13261
+ * Writes the track ROW: `markForTrain`/`debug` are per-TRACK state, so they
13262
+ * live where `label` and `importance` live, not in any per-device settings
13263
+ * store. Updates the in-RAM active track too, so a flag set on a live track
13264
+ * survives its expiry-time persist.
13265
+ *
13266
+ * `auth: 'protected'` (the default), NOT `admin`: the viewer is an
13267
+ * authenticated non-admin surface and two of the three call sites are
13268
+ * there. Revisit if a flag ever gains an effect that costs storage —
13269
+ * `deleteTracks` next door is admin for exactly that reason.
13270
+ *
13271
+ * Returns the RESOLVED state of both flags (absent → `false`) so a caller
13272
+ * can drive its toggle without a re-fetch. Rejects an unknown track.
13273
+ */
13274
+ setTrackFlags: method(z.object({
13275
+ /** Log/audit scope only — the trackId is globally unique on its own. */
13276
+ deviceId: z.number(),
13277
+ trackId: z.string(),
13278
+ flags: TrackFlagsPatchSchema
13279
+ }), TrackFlagsSchema, { kind: "mutation" }),
13280
+ /**
11954
13281
  * Durable event-store footprint for the management UI: event rows
11955
13282
  * (motion + object + audio) counted per camera + total, plus the
11956
13283
  * event-owned media bytes on disk per camera + total. Stat/count-based,
@@ -12839,6 +14166,53 @@ var DetailResultSchema = z.object({
12839
14166
  nativeFaceShortSidePx: z.number().optional()
12840
14167
  });
12841
14168
  /**
14169
+ * Why an executing node REFUSED a stateless step run (`runStatelessStep`).
14170
+ *
14171
+ * A refusal is a first-class answer, not an error, because the caller's next
14172
+ * move depends on WHICH one it is — and because "the pass produced nothing"
14173
+ * must never be reachable without a named, counted cause. The two tiers:
14174
+ *
14175
+ * - **node-level** (`unknown-step`, `model-not-servable`) — this node can
14176
+ * never serve this (step, model) pair. The caller drops it from its rotation
14177
+ * and retries the same work elsewhere; nothing about the work changes.
14178
+ * - **work-level** (`unreadable-frame`, `execution-failed`) — this node is
14179
+ * fine, this one request is not. Retrying it on another node would only
14180
+ * spread the same failure.
14181
+ */
14182
+ var StatelessStepRefusalSchema = z.enum([
14183
+ "unknown-step",
14184
+ "model-not-servable",
14185
+ "unreadable-frame",
14186
+ "execution-failed"
14187
+ ]);
14188
+ /**
14189
+ * Answer to `runStatelessStep` — a discriminated union rather than a nullable
14190
+ * result, because `null` is exactly what made the camera-bound detail path
14191
+ * unable to tell "refused" from "never asked".
14192
+ */
14193
+ var RunStatelessStepResultSchema = z.discriminatedUnion("kind", [z.object({
14194
+ kind: z.literal("ran"),
14195
+ /** The node that actually executed it — the pin, echoed back for the log. */
14196
+ nodeId: z.string(),
14197
+ /**
14198
+ * The model the step ran with.
14199
+ *
14200
+ * The node verified this exact id has a build for the format it dispatched
14201
+ * on BEFORE running, so the executor's format resolution returns it
14202
+ * unchanged. A caller that pinned a model must compare this field and
14203
+ * treat a mismatch as a refusal — the whole point of the pin is that a
14204
+ * pass writes one feature space.
14205
+ */
14206
+ modelId: z.string(),
14207
+ details: z.array(DetailResultSchema)
14208
+ }), z.object({
14209
+ kind: z.literal("refused"),
14210
+ nodeId: z.string(),
14211
+ reason: StatelessStepRefusalSchema,
14212
+ /** Human-readable specifics — the format tried, the formats shipped, etc. */
14213
+ detail: z.string()
14214
+ })]);
14215
+ /**
12842
14216
  * Per-camera tunable ranges + defaults. Single source of truth used
12843
14217
  * by both the Zod data schema (validation + default fallback) and
12844
14218
  * the device settings UI (slider min/max/step). Touch one place and
@@ -13422,7 +14796,77 @@ var pipelineRunnerCapability = {
13422
14796
  cropJpeg: z.string().optional(),
13423
14797
  parent: DetailParentSchema,
13424
14798
  steps: z.array(z.string()).optional()
13425
- }), z.object({ details: z.array(DetailResultSchema) }).nullable(), { kind: "mutation" })
14799
+ }), z.object({ details: z.array(DetailResultSchema) }).nullable(), { kind: "mutation" }),
14800
+ /**
14801
+ * Run ONE enrichment step against caller-supplied pixels, with NO camera
14802
+ * session — no attach, no frame handle, no device affinity.
14803
+ *
14804
+ * ## Why this exists next to `runDetailSubtree` and not inside it
14805
+ *
14806
+ * `runDetailSubtree` resolves its step tree from the LIVE ATTACH STATE. That
14807
+ * is correct for it: its reason to exist is a frame HANDLE, and a handle is
14808
+ * only resolvable while a decode session is open. But it makes the method
14809
+ * useless to anything walking history — cameras run `detectionMode:
14810
+ * 'on-motion'` and detach ~30 s after motion stops, so a fleet-wide
14811
+ * embedding rebuild found most cameras detached and rebuilt only whatever
14812
+ * happened to be attached at that second (measured 2026-08-06: 1125 tracks
14813
+ * scanned, 121 rebuilt, 601 refused as "camera is not attached").
14814
+ *
14815
+ * A caller that ships its own pixels needs none of that machinery. So this
14816
+ * method takes the three things a step actually needs — an image, a box, a
14817
+ * step id — resolves the step from the CATALOG rather than from an
14818
+ * attachment, and runs it on whichever node the caller pinned.
14819
+ *
14820
+ * ## What it deliberately keeps
14821
+ *
14822
+ * The crop rectangle is derived by the SAME `deriveDetailCropRect` call the
14823
+ * live detail plane uses, from the same cluster-wide convention
14824
+ * ([D52](../../../docs/decisions/adr-0052.md)). `frameJpeg` is a FULL frame,
14825
+ * never a pre-cut tile, for exactly the reason `runDetailSubtree` documents:
14826
+ * a caller that cuts is a second feature space.
14827
+ *
14828
+ * ## What it deliberately drops
14829
+ *
14830
+ * No device jump and no `deviceKey`: the jump roster comes from the attach
14831
+ * config, which does not exist here. The step runs on the node's default
14832
+ * engine. This is a maintenance path, not a latency-sensitive one.
14833
+ *
14834
+ * ## Routing
14835
+ *
14836
+ * NOT device-bound. `sourceDeviceId` is DIAGNOSTIC — the camera whose track
14837
+ * these pixels came from, so every log line on the executing node can carry
14838
+ * `tags: { deviceId }`. It is deliberately not named `deviceId`: that field
14839
+ * is a routing hint that would send the call to the camera's owning node,
14840
+ * which is the placement constraint this method exists to remove. Callers
14841
+ * choose the node with `nodePin(nodeId)`; unpinned, an unowned call is
14842
+ * served hub-in-process like any other singleton.
14843
+ */
14844
+ runStatelessStep: method(z.object({
14845
+ /** Catalog step id, e.g. `clip-embedding`. */
14846
+ stepId: z.string(),
14847
+ /**
14848
+ * REQUIRED model pin. The node runs this exact model or refuses with
14849
+ * `model-not-servable` — it never substitutes a format default, because
14850
+ * a fleet pass that round-robins across nodes would then fill one index
14851
+ * from several encoders.
14852
+ */
14853
+ modelId: z.string(),
14854
+ /** FULL FRAME, base64 JPEG. The runner cuts — do NOT pre-crop. */
14855
+ frameJpeg: z.string(),
14856
+ /**
14857
+ * The subject box, NORMALISED [0,1] against `frameJpeg`. Normalised on
14858
+ * purpose: the caller stores boxes against a downscaled analysis frame
14859
+ * while the stored key frame is native-resolution, and the only side
14860
+ * that reliably knows the image's pixel dimensions is the side that
14861
+ * decodes it. Denormalising here removes a second reader of the
14862
+ * dimensions and the class of mismatch that comes with it.
14863
+ */
14864
+ bbox: NativeCropBboxSchema,
14865
+ /** Parent class of the subject (`person`, `vehicle`, …) — carried into the result. */
14866
+ className: z.string(),
14867
+ /** Camera the pixels came from. Diagnostics + log tags ONLY — never routing. */
14868
+ sourceDeviceId: z.number()
14869
+ }), RunStatelessStepResultSchema, { kind: "mutation" })
13426
14870
  }
13427
14871
  };
13428
14872
  //#endregion
@@ -13732,6 +15176,20 @@ var CameraStatusSchema = z.object({
13732
15176
  detection: CameraDetectionStatusSchema.nullable(),
13733
15177
  audio: CameraAudioStatusSchema.nullable(),
13734
15178
  recording: CameraRecordingStatusSchema.nullable(),
15179
+ /**
15180
+ * Per-camera function switches an OPERATOR has turned off
15181
+ * ([D61](../../../../docs/decisions/adr-0067.md)).
15182
+ *
15183
+ * This is the difference between DISABLED and BROKEN. A camera whose
15184
+ * `detection` block reports zero fps and whose `switchedOff` contains
15185
+ * `'object-detection'` was switched off by a person; the same camera with an
15186
+ * empty list is failing. Every status surface must render the two
15187
+ * differently — a quiet camera that looks identical to a dead one is the
15188
+ * silence-reads-as-never-happened trap this repo keeps paying for.
15189
+ *
15190
+ * Empty when nothing is off. Never contains a switch no provider offers.
15191
+ */
15192
+ switchedOff: z.array(CameraSwitchIdSchema).readonly(),
13735
15193
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
13736
15194
  fetchedAt: z.number()
13737
15195
  });
@@ -14131,6 +15589,43 @@ var pipelineOrchestratorCapability = {
14131
15589
  agentNodeId: z.string().optional()
14132
15590
  }), CameraPipelineConfigSchema),
14133
15591
  /**
15592
+ * The whole per-camera function switch group, DERIVED — never a stored
15593
+ * list ([D61](../../../../docs/decisions/adr-0067.md)).
15594
+ *
15595
+ * The group adds no state. Each switch is a view onto the authority that
15596
+ * already owned it (`deviceManager.setDisabled`,
15597
+ * `deviceManager.setWrapperActive`, `RecordingConfig.enabled`,
15598
+ * `notificationRules.setDeviceMuted`), and `switch.authority` says which.
15599
+ * Availability comes from `deviceManager.listBindableCapsForDeviceType`,
15600
+ * so a deployment with no audio analyzer renders no audio switch.
15601
+ *
15602
+ * `auth: 'view'` deliberately — a NON-admin must be able to see that a
15603
+ * camera is quiet because somebody switched it off. Only the mutation is
15604
+ * admin-gated.
15605
+ */
15606
+ getCameraSwitches: method(z.object({ deviceId: z.number() }), CameraSwitchGroupSchema),
15607
+ /**
15608
+ * Flip ONE switch, routed to its existing authority.
15609
+ *
15610
+ * Never writes a parallel map: `recording` patches `RecordingConfig.enabled`
15611
+ * and leaves `bands` byte-identical (clearing bands to express "off"
15612
+ * destroys the operator's authored schedule and turning the camera back on
15613
+ * would silently record nothing), and the two pipeline switches write the
15614
+ * SAME wrapper binding the legacy `pipelineEnabled` / `audioEnabled`
15615
+ * booleans were migrated onto.
15616
+ *
15617
+ * Rejects a switch this camera does not offer rather than persisting a
15618
+ * write nothing reads.
15619
+ */
15620
+ setCameraSwitch: method(z.object({
15621
+ deviceId: z.number(),
15622
+ switchId: CameraSwitchIdSchema,
15623
+ enabled: z.boolean()
15624
+ }), CameraSwitchGroupSchema, {
15625
+ kind: "mutation",
15626
+ auth: "admin"
15627
+ }),
15628
+ /**
14134
15629
  * Server-composed aggregated status for a single camera.
14135
15630
  *
14136
15631
  * Fans out in parallel (bounded, per-stage graceful degradation) to
@@ -14618,9 +16113,15 @@ var snapshotCapability = {
14618
16113
  * Bypass the cache freshness check and fetch directly from the
14619
16114
  * native (or stream-broker fallback). Triggered by the UI's
14620
16115
  * "refresh" button so an operator can force a fresh frame
14621
- * even when the cache is well within `snapshotMaxAgeMs`.
14622
- * On battery cams this WILL wake the camera — accept the
14623
- * cost only when the user explicitly asks for it.
16116
+ * even when the cache is well within the device's
16117
+ * `snapshotMaxAgeS` window.
16118
+ *
16119
+ * **`force` is an OPERATOR signal, not a freshness preference.** On a
16120
+ * battery camera it is the one thing that walks past the wrapper's
16121
+ * sleep gate and wakes the camera, so a background caller — a poller,
16122
+ * an event handler, a thumbnail — must NEVER set it. Every such caller
16123
+ * gets the cached frame, which on a sleeping battery camera is the
16124
+ * correct answer: stale but honest beats woken.
14624
16125
  */
14625
16126
  force: z.boolean().optional()
14626
16127
  }), SnapshotImageSchema.nullable()),
@@ -21243,12 +22744,30 @@ var pressureSensorCapability = {
21243
22744
  //#endregion
21244
22745
  //#region src/capabilities/privacy-mask.cap.ts
21245
22746
  /**
21246
- * Privacy mask = up to `maxRegions` SHAPES the camera blanks out (NOT a
21247
- * cell grid). Reolink `<shelterList>` zones are rectangles; Hikvision
21248
- * ISAPI `<RegionCoordinatesList>` zones are free polygons (this camera:
21249
- * exactly 4 vertices, not necessarily axis-aligned). The cap composes the
21250
- * shared rect|polygon subset of the MaskShape vocabulary. All coords are
21251
- * normalized 0..1 (top-left origin).
22747
+ * PRIVACY what the camera deliberately does not capture. Two planes:
22748
+ *
22749
+ * - **video**: up to `maxRegions` SHAPES the camera blanks out (NOT a cell
22750
+ * grid). Reolink `<shelterList>` zones are rectangles; Hikvision ISAPI
22751
+ * `<RegionCoordinatesList>` zones are free polygons (this camera: exactly
22752
+ * 4 vertices, not necessarily axis-aligned). The cap composes the shared
22753
+ * rect|polygon subset of the MaskShape vocabulary. All coords are
22754
+ * normalized 0..1 (top-left origin).
22755
+ * - **audio**: the camera's microphone. `setAudioEnabled(false)` stops the
22756
+ * camera encoding an audio track at all, so EVERY consumer — live view,
22757
+ * recording, the audio analyzer, an export — sees silent video. There is
22758
+ * no server-side copy of this fact; the camera is the store and every read
22759
+ * is a read-through, which is why a switch over it cannot drift
22760
+ * ([D62](../../../../docs/decisions/adr-0062.md)).
22761
+ *
22762
+ * Both belong here for one reason: they are the two things an operator turns
22763
+ * off when the answer to "what is this camera allowed to record" changes, and
22764
+ * both are applied ON the device, before anything leaves it.
22765
+ *
22766
+ * **The audio flag has exactly one writer.** `stream-params` used to carry a
22767
+ * per-profile `audio` in its patch schema — reachable from no UI and honoured
22768
+ * by one provider — and it was removed when this landed. A second writer onto
22769
+ * one device register is the shape of every knob this repo has shipped that
22770
+ * disagreed with the one the reader read.
21252
22771
  */
21253
22772
  /** A privacy-mask region's geometry — rectangle or free polygon. */
21254
22773
  var PrivacyMaskShapeSchema = z.discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
@@ -21260,21 +22779,45 @@ var PrivacyMaskRegionSchema = z.object({
21260
22779
  enabled: z.boolean(),
21261
22780
  shape: PrivacyMaskShapeSchema
21262
22781
  });
21263
- /** Current on-camera privacy-mask state — master enable + zones. */
22782
+ /** Current on-camera privacy state — mask master enable + zones + microphone. */
21264
22783
  var PrivacyMaskStatusSchema = z.object({
21265
22784
  enabled: z.boolean(),
21266
22785
  /** Active zones (normalized 0..1). Length ≤ maxRegions. */
21267
22786
  regions: z.array(PrivacyMaskRegionSchema),
22787
+ /**
22788
+ * Is the camera capturing sound right now? Read from the camera, never from
22789
+ * a server-side mirror.
22790
+ *
22791
+ * `null` means "no answer" — either this camera exposes no controllable
22792
+ * microphone (`getOptions().supportsAudioMute === false`) or the read
22793
+ * failed. A consumer must render `null` as UNKNOWN and never as `false`:
22794
+ * "the microphone is off" and "we could not ask" look identical to an
22795
+ * operator only until one of them is wrong.
22796
+ *
22797
+ * On a camera whose profiles carry the flag independently (Reolink writes
22798
+ * it per stream), `true` means AT LEAST ONE profile still carries audio —
22799
+ * privacy is only satisfied when every one of them is silent.
22800
+ */
22801
+ audioEnabled: z.boolean().nullable(),
21268
22802
  lastFetchedAt: z.number()
21269
22803
  });
21270
- /** Per-camera availability. */
22804
+ /** Per-camera availability. Probed, never assumed from the model name. */
21271
22805
  var PrivacyMaskOptionsSchema = z.object({
21272
22806
  /** Maximum number of supported zones. */
21273
22807
  maxRegions: z.number(),
21274
22808
  /** Shape kinds this camera accepts — Reolink: ['rect']; Hikvision: ['rect','polygon']. */
21275
22809
  supportedShapes: z.array(MaskShapeKindSchema),
21276
22810
  /** Polygon vertex bounds when 'polygon' is supported (Hikvision: {min:4,max:4}). */
21277
- polygonVertices: MaskPolygonVerticesSchema.optional()
22811
+ polygonVertices: MaskPolygonVerticesSchema.optional(),
22812
+ /**
22813
+ * Does this camera expose a microphone switch we can actually write?
22814
+ *
22815
+ * Camera-probed: `true` only when the firmware answered with an audio flag
22816
+ * we know how to patch. A camera that never answered is `false` — a control
22817
+ * the operator can press that changes nothing is worse than no control, and
22818
+ * the switch group renders "not available" instead.
22819
+ */
22820
+ supportsAudioMute: z.boolean()
21278
22821
  });
21279
22822
  /** Partial change — every field optional. */
21280
22823
  var PrivacyMaskPatchSchema = z.object({
@@ -21301,6 +22844,27 @@ var privacyMaskCapability = {
21301
22844
  }), z.void(), {
21302
22845
  kind: "mutation",
21303
22846
  auth: "admin"
22847
+ }),
22848
+ /**
22849
+ * Turn the camera's microphone on or off, at the camera.
22850
+ *
22851
+ * Deliberately its OWN mutation rather than a field on
22852
+ * {@link PrivacyMaskPatchSchema}: `patch.enabled` already means "the video
22853
+ * mask master switch", and overloading it would make one boolean mean two
22854
+ * unrelated things on the same call. It is also the only method here whose
22855
+ * write leaves the device in a state a later `getStatus` reads back
22856
+ * verbatim, which is what makes it safe as a switch authority.
22857
+ *
22858
+ * A camera whose `getOptions().supportsAudioMute` is false must REJECT
22859
+ * this rather than silently accept it — a write nothing applies is exactly
22860
+ * what the switch group exists to remove.
22861
+ */
22862
+ setAudioEnabled: method(z.object({
22863
+ deviceId: z.number(),
22864
+ enabled: z.boolean()
22865
+ }), z.void(), {
22866
+ kind: "mutation",
22867
+ auth: "admin"
21304
22868
  })
21305
22869
  },
21306
22870
  status: {
@@ -21309,6 +22873,26 @@ var privacyMaskCapability = {
21309
22873
  },
21310
22874
  runtimeState: PrivacyMaskStatusSchema
21311
22875
  };
22876
+ /**
22877
+ * Collapse a camera's PER-PROFILE audio flags into the one answer
22878
+ * {@link PrivacyMaskStatusSchema.shape.audioEnabled} promises.
22879
+ *
22880
+ * Both firmwares this cap talks to store the flag per stream profile, and
22881
+ * both let those profiles disagree. The rule is `some`, not `every`: privacy
22882
+ * is only satisfied when NOTHING is carrying sound, so a camera whose sub
22883
+ * stream is still audible must read as `true` and be switchable off — not as
22884
+ * `false` because the main stream happens to be muted already.
22885
+ *
22886
+ * An empty list is `null` ("this camera reported no audio flag at all"),
22887
+ * never `false`.
22888
+ *
22889
+ * Lives here rather than in each provider so the rule the schema documents
22890
+ * and the rule the providers apply cannot drift apart.
22891
+ */
22892
+ function summarisePrivacyAudio(profiles) {
22893
+ if (profiles.length === 0) return null;
22894
+ return profiles.some((p) => p.audioEnabled);
22895
+ }
21312
22896
  //#endregion
21313
22897
  //#region src/capabilities/ptz.cap.ts
21314
22898
  var PtzPresetSchema = z.object({
@@ -21693,6 +23277,21 @@ var LocateSegmentResultSchema = z.discriminatedUnion("kind", [z.object({
21693
23277
  })]);
21694
23278
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
21695
23279
  var ReadSegmentBytesResultSchema = z.object({ data: z.instanceof(Uint8Array) });
23280
+ /**
23281
+ * One GOP of a finalized segment, cut by byte range through the segment's own
23282
+ * `mfra` (D31 on the D42 feeder path). `data` is the `ftyp`+`moov` head plus
23283
+ * the single `moof`+`mdat` covering the requested instant — standalone-
23284
+ * demuxable, never the whole file. When the segment's index cannot be parsed
23285
+ * the provider degrades INSIDE the mechanism to the whole segment (still one
23286
+ * `data`, `gopStartMs` = the segment start) — a worse read, not another path.
23287
+ */
23288
+ var ReadGopBytesResultSchema = z.object({
23289
+ data: z.instanceof(Uint8Array),
23290
+ /** Absolute epoch ms of the returned fragment's first sample. */
23291
+ gopStartMs: z.number(),
23292
+ /** Media ms the returned fragment covers. */
23293
+ gopDurMs: z.number()
23294
+ });
21696
23295
  var recordingCapability = {
21697
23296
  name: "recording",
21698
23297
  scope: "system",
@@ -21760,6 +23359,18 @@ var recordingCapability = {
21760
23359
  kind: "query",
21761
23360
  auth: "admin"
21762
23361
  }),
23362
+ /** Read the single GOP of segment `startMs` covering `epochMs`, by mfra
23363
+ * byte range — the scrub-granular read (D31 letter on the feeder path).
23364
+ * See {@link ReadGopBytesResultSchema} for the degradation contract. */
23365
+ readGopBytes: method(z.object({
23366
+ deviceId: z.number(),
23367
+ profile: z.string(),
23368
+ startMs: z.number(),
23369
+ epochMs: z.number()
23370
+ }), ReadGopBytesResultSchema, {
23371
+ kind: "query",
23372
+ auth: "admin"
23373
+ }),
21763
23374
  setDeviceConfig: method(z.object({
21764
23375
  deviceId: z.number(),
21765
23376
  config: RecordingConfigSchema
@@ -22421,6 +24032,16 @@ var StreamProfileConfigSchema = z.object({
22421
24032
  "baseline"
22422
24033
  ]).optional(),
22423
24034
  gop: z.number().optional(),
24035
+ /**
24036
+ * Whether THIS profile currently carries an audio track. READ-ONLY here.
24037
+ *
24038
+ * There is no matching field on {@link StreamProfilePatchSchema}: the
24039
+ * camera's microphone is owned by `privacy-mask` (`setAudioEnabled`), which
24040
+ * writes every profile at once so "audio off" means silent everywhere. A
24041
+ * per-profile writer beside it would let a camera be half-muted and would be
24042
+ * a second knob onto one device register — the failure D62 exists to
24043
+ * prevent. Absent when the firmware does not report the flag.
24044
+ */
22424
24045
  audio: z.boolean().optional()
22425
24046
  });
22426
24047
  var StreamParamsStatusSchema = z.object({
@@ -22461,7 +24082,13 @@ var StreamParamsOptionsSchema = z.object({
22461
24082
  ext: StreamProfileOptionsSchema.optional()
22462
24083
  });
22463
24084
  /** A partial change to one profile — every field optional; a provider
22464
- * ignores fields it doesn't support. */
24085
+ * ignores fields it doesn't support.
24086
+ *
24087
+ * There is deliberately NO `audio` here. It existed until 2026-08-07,
24088
+ * reachable from no form and honoured by exactly one provider, while the
24089
+ * camera's microphone is a whole-device fact. It now has one writer,
24090
+ * `privacyMask.setAudioEnabled`, which writes every profile — see
24091
+ * `privacy-mask.cap.ts`. */
22465
24092
  var StreamProfilePatchSchema = z.object({
22466
24093
  width: z.number().optional(),
22467
24094
  height: z.number().optional(),
@@ -22474,8 +24101,7 @@ var StreamProfilePatchSchema = z.object({
22474
24101
  "main",
22475
24102
  "baseline"
22476
24103
  ]).optional(),
22477
- gop: z.number().optional(),
22478
- audio: z.boolean().optional()
24104
+ gop: z.number().optional()
22479
24105
  });
22480
24106
  var streamParamsCapability = {
22481
24107
  name: "stream-params",
@@ -25394,10 +27020,7 @@ var BATTERY_DEVICE_PROFILE = {
25394
27020
  audioMode: "disabled",
25395
27021
  detectionMode: "on-motion"
25396
27022
  },
25397
- settings: {
25398
- "snapshot.minRefreshIntervalSec": 3600,
25399
- "streamBroker.preBufferEnabled": false
25400
- }
27023
+ settings: {}
25401
27024
  };
25402
27025
  /**
25403
27026
  * Profile registry — order matters when multiple profiles match the
@@ -26249,7 +27872,7 @@ var SystemMirror = class {
26249
27872
  }
26250
27873
  async refreshDeviceMetadata(deviceId, kind) {
26251
27874
  try {
26252
- const info = (await this.api.deviceManager.listAll.query({})).find((d) => d.id === deviceId);
27875
+ const info = await this.api.deviceManager.getDevice.query({ deviceId });
26253
27876
  if (!info) return;
26254
27877
  const wasNew = !this.devices.has(deviceId);
26255
27878
  this.devices.set(deviceId, info);
@@ -30151,6 +31774,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30151
31774
  addonId: null,
30152
31775
  access: "view"
30153
31776
  },
31777
+ "notificationRules.listDeviceMutes": {
31778
+ capName: "notification-rules",
31779
+ capScope: "system",
31780
+ addonId: null,
31781
+ access: "view"
31782
+ },
30154
31783
  "notificationRules.listRules": {
30155
31784
  capName: "notification-rules",
30156
31785
  capScope: "system",
@@ -30169,6 +31798,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30169
31798
  addonId: null,
30170
31799
  access: "create"
30171
31800
  },
31801
+ "notificationRules.setDeviceMuted": {
31802
+ capName: "notification-rules",
31803
+ capScope: "system",
31804
+ addonId: null,
31805
+ access: "create"
31806
+ },
30172
31807
  "notificationRules.setRuleEnabled": {
30173
31808
  capName: "notification-rules",
30174
31809
  capScope: "system",
@@ -30445,6 +32080,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30445
32080
  addonId: null,
30446
32081
  access: "view"
30447
32082
  },
32083
+ "pipelineAnalytics.setTrackFlags": {
32084
+ capName: "pipeline-analytics",
32085
+ capScope: "device",
32086
+ addonId: null,
32087
+ access: "create"
32088
+ },
30448
32089
  "pipelineAnalytics.wipeAllAnalytics": {
30449
32090
  capName: "pipeline-analytics",
30450
32091
  capScope: "device",
@@ -30751,6 +32392,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30751
32392
  addonId: null,
30752
32393
  access: "view"
30753
32394
  },
32395
+ "pipelineOrchestrator.getCameraSwitches": {
32396
+ capName: "pipeline-orchestrator",
32397
+ capScope: "system",
32398
+ addonId: null,
32399
+ access: "view"
32400
+ },
30754
32401
  "pipelineOrchestrator.getCapabilityBindings": {
30755
32402
  capName: "pipeline-orchestrator",
30756
32403
  capScope: "system",
@@ -30883,6 +32530,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30883
32530
  addonId: null,
30884
32531
  access: "create"
30885
32532
  },
32533
+ "pipelineOrchestrator.setCameraSwitch": {
32534
+ capName: "pipeline-orchestrator",
32535
+ capScope: "system",
32536
+ addonId: null,
32537
+ access: "create"
32538
+ },
30886
32539
  "pipelineOrchestrator.setCapabilityBinding": {
30887
32540
  capName: "pipeline-orchestrator",
30888
32541
  capScope: "system",
@@ -30973,6 +32626,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30973
32626
  addonId: null,
30974
32627
  access: "create"
30975
32628
  },
32629
+ "pipelineRunner.runStatelessStep": {
32630
+ capName: "pipeline-runner",
32631
+ capScope: "system",
32632
+ addonId: null,
32633
+ access: "create"
32634
+ },
30976
32635
  "plateGallery.assignPlate": {
30977
32636
  capName: "plate-gallery",
30978
32637
  capScope: "system",
@@ -31105,6 +32764,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31105
32764
  addonId: null,
31106
32765
  access: "view"
31107
32766
  },
32767
+ "privacyMask.setAudioEnabled": {
32768
+ capName: "privacy-mask",
32769
+ capScope: "device",
32770
+ addonId: null,
32771
+ access: "create"
32772
+ },
31108
32773
  "privacyMask.setMask": {
31109
32774
  capName: "privacy-mask",
31110
32775
  capScope: "device",
@@ -31273,6 +32938,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31273
32938
  addonId: null,
31274
32939
  access: "create"
31275
32940
  },
32941
+ "recording.readGopBytes": {
32942
+ capName: "recording",
32943
+ capScope: "system",
32944
+ addonId: null,
32945
+ access: "view"
32946
+ },
31276
32947
  "recording.readSegmentBytes": {
31277
32948
  capName: "recording",
31278
32949
  capScope: "system",
@@ -31789,6 +33460,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31789
33460
  addonId: null,
31790
33461
  access: "create"
31791
33462
  },
33463
+ "streamBroker.acquireEgressTranscode": {
33464
+ capName: "stream-broker",
33465
+ capScope: "system",
33466
+ addonId: null,
33467
+ access: "create"
33468
+ },
31792
33469
  "streamBroker.assignProfile": {
31793
33470
  capName: "stream-broker",
31794
33471
  capScope: "system",
@@ -31897,6 +33574,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31897
33574
  addonId: null,
31898
33575
  access: "create"
31899
33576
  },
33577
+ "streamBroker.releaseEgressTranscode": {
33578
+ capName: "stream-broker",
33579
+ capScope: "system",
33580
+ addonId: null,
33581
+ access: "create"
33582
+ },
31900
33583
  "streamBroker.releaseStreamWithCodec": {
31901
33584
  capName: "stream-broker",
31902
33585
  capScope: "system",
@@ -33309,7 +34992,8 @@ function createSystemProxy(api) {
33309
34992
  getLocalMetrics: (input) => dispatch("pipelineRunner", "getLocalMetrics", "query", input),
33310
34993
  getAllCameraMetrics: (input) => dispatch("pipelineRunner", "getAllCameraMetrics", "query", input),
33311
34994
  getLocalCameras: (input) => dispatch("pipelineRunner", "getLocalCameras", "query", input),
33312
- getNativeCrop: (input) => dispatch("pipelineRunner", "getNativeCrop", "query", input)
34995
+ getNativeCrop: (input) => dispatch("pipelineRunner", "getNativeCrop", "query", input),
34996
+ runStatelessStep: (input) => dispatch("pipelineRunner", "runStatelessStep", "mutation", input)
33313
34997
  },
33314
34998
  plateGallery: {
33315
34999
  getPlateMedia: (input) => dispatch("plateGallery", "getPlateMedia", "query", input),
@@ -33396,6 +35080,8 @@ function createSystemProxy(api) {
33396
35080
  getStreamUrl: (input) => dispatch("streamBroker", "getStreamUrl", "query", input),
33397
35081
  getStreamWithCodec: (input) => dispatch("streamBroker", "getStreamWithCodec", "mutation", input),
33398
35082
  releaseStreamWithCodec: (input) => dispatch("streamBroker", "releaseStreamWithCodec", "mutation", input),
35083
+ acquireEgressTranscode: (input) => dispatch("streamBroker", "acquireEgressTranscode", "mutation", input),
35084
+ releaseEgressTranscode: (input) => dispatch("streamBroker", "releaseEgressTranscode", "mutation", input),
33399
35085
  subscribeAudioChunks: (input) => dispatch("streamBroker", "subscribeAudioChunks", "mutation", input),
33400
35086
  pullAudioChunks: (input) => dispatch("streamBroker", "pullAudioChunks", "query", input),
33401
35087
  unsubscribeAudioChunks: (input) => dispatch("streamBroker", "unsubscribeAudioChunks", "mutation", input),
@@ -34276,6 +35962,44 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
34276
35962
  return best;
34277
35963
  }
34278
35964
  //#endregion
35965
+ //#region src/utils/addon-id.ts
35966
+ /**
35967
+ * The addon id as the REGISTRY, the wire and the durable stores spell it.
35968
+ *
35969
+ * `AddonContext.id` does not agree with itself across the two context
35970
+ * factories:
35971
+ *
35972
+ * - a FORKED addon gets the bare manifest id
35973
+ * (`kernel/moleculer/addon-context-factory.ts` → `id: addonId`);
35974
+ * - an addon co-located in hub-main gets it PREFIXED
35975
+ * (`server/backend/src/core/addon/addon-registry.service.ts` →
35976
+ * ``id: `addon:${addonId}` ``).
35977
+ *
35978
+ * Everything an addon might compare `ctx.id` AGAINST carries the bare form:
35979
+ * `DeviceBindingEntry.providerAddonId`, the `device-manager` bindings store's
35980
+ * `wrapperAddonId`, `CapabilityRegistry` provider keys, manifest ids.
35981
+ *
35982
+ * So `entry.providerAddonId === this.ctx.id` is silently, permanently false
35983
+ * for a builtin — and only for a builtin, which is why it survives a green
35984
+ * suite whose fake supplies the bare id. That is exactly how camera 615's
35985
+ * virtual doorbell latched `unbound` for twelve hours on 2026-08-07 while its
35986
+ * binding was intact ([D72](../../../../docs/decisions/adr-0072.md)).
35987
+ *
35988
+ * Route every comparison between `ctx.id` and a registry/store addon id
35989
+ * through {@link isSameAddonId}. `scripts/check-addon-id-comparison.ts`
35990
+ * enforces it in the processes where the prefix exists.
35991
+ */
35992
+ /** The prefix the hub-main context factory prepends to the manifest id. */
35993
+ var ADDON_ID_PREFIX = "addon:";
35994
+ /** The manifest id, whichever spelling of `ctx.id` you were handed. */
35995
+ function bareAddonId(id) {
35996
+ return id.startsWith(ADDON_ID_PREFIX) ? id.slice(6) : id;
35997
+ }
35998
+ /** True when both ids name the same addon, prefixed or not. */
35999
+ function isSameAddonId(a, b) {
36000
+ return bareAddonId(a) === bareAddonId(b);
36001
+ }
36002
+ //#endregion
34279
36003
  //#region src/utils/cosine-similarity.ts
34280
36004
  /** Cosine similarity between two embedding vectors */
34281
36005
  function cosineSimilarity(a, b) {
@@ -35002,7 +36726,7 @@ function readDetailCropConvention(config) {
35002
36726
  square: square.success ? square.data : DEFAULT_DETAIL_CROP_CONVENTION.square
35003
36727
  };
35004
36728
  }
35005
- function isHydratedField(entry) {
36729
+ function isHydratedField$1(entry) {
35006
36730
  return typeof entry === "object" && entry !== null && "key" in entry;
35007
36731
  }
35008
36732
  /**
@@ -35017,7 +36741,7 @@ function pickDetailCropConvention(view) {
35017
36741
  if (view === null) return DEFAULT_DETAIL_CROP_CONVENTION;
35018
36742
  const flat = {};
35019
36743
  for (const section of view.sections) for (const entry of section.fields) {
35020
- if (!isHydratedField(entry) || typeof entry.key !== "string") continue;
36744
+ if (!isHydratedField$1(entry) || typeof entry.key !== "string") continue;
35021
36745
  if (entry.key === "detailCropPaddingRatio" || entry.key === "detailCropSquare") flat[entry.key] = entry.value;
35022
36746
  }
35023
36747
  return readDetailCropConvention(flat);
@@ -35104,6 +36828,245 @@ function slideInsideFrame(rect, frameWidth, frameHeight) {
35104
36828
  };
35105
36829
  }
35106
36830
  //#endregion
36831
+ //#region src/pipeline/native-lease.ts
36832
+ /**
36833
+ * THE native-frame **lease** knobs — TTL, RAM budget and demand window for the
36834
+ * decode worker's native-resolution frame retention.
36835
+ *
36836
+ * ## Why they live here and not in the addon that reads them
36837
+ *
36838
+ * The WRITER is `pipeline-orchestrator` (the cluster-wide settings authority);
36839
+ * the READER is a private child process of `addon-pipeline`'s pipeline-runner.
36840
+ * Addons never import each other, so a key owned by either side would have to
36841
+ * be hand-copied by the other — and a hand-copied key is how a setting silently
36842
+ * stops arriving while both sides still look correct. Same reasoning, same
36843
+ * placement as `detail-crop.ts` (D52's "one cluster-wide orchestrator setting").
36844
+ *
36845
+ * ## Why cluster-wide and not per-node
36846
+ *
36847
+ * The lease is a per-decode-worker RAM window. Its purpose — the late
36848
+ * cross-process native crop landing on a full-resolution frame rather than the
36849
+ * ≤640 detection fallback — is a property of the PIPELINE, not of a node's
36850
+ * hardware: a per-node TTL would mean the same camera produces different crop
36851
+ * quality depending on which node the balancer placed it on, and nobody could
36852
+ * tell that from the stored media. Node-level RAM pressure is already handled
36853
+ * by the per-session budget ceiling, which is itself one of these knobs.
36854
+ *
36855
+ * ## What each knob costs
36856
+ *
36857
+ * A retained frame is a full NATIVE-resolution copy in system RAM. With the
36858
+ * default pinned-RGB24 lease path (`CAMSTACK_SESSION_PINNED_RGB_CROP`, on):
36859
+ * 4K ≈ 24.9 MB/frame, 1080p ≈ 6.2 MB/frame. On the YUV420P path (flag off, and
36860
+ * for software-decoded sessions): 4K ≈ 12.4 MB, 1080p ≈ 3.1 MB. Worst-case
36861
+ * resident RAM for ONE busy camera ≈ frameBytes × deliveredFps × ttlSeconds,
36862
+ * clamped by the budget ceiling. See `docs/design/decode-path.md` → "Lease
36863
+ * admission" for what actually gets admitted.
36864
+ */
36865
+ /**
36866
+ * Store identity of the lease knobs in `pipeline-orchestrator`'s GLOBAL
36867
+ * (cluster-wide) settings. Keys are unique across that addon's whole schema, so
36868
+ * the reader can walk every section instead of trusting the section id.
36869
+ */
36870
+ var NATIVE_LEASE_SECTION_ID = "native-lease";
36871
+ var NATIVE_LEASE_TTL_KEY = "nativeLeaseTtlMs";
36872
+ var NATIVE_LEASE_BUDGET_KEY = "nativeLeaseBudgetMb";
36873
+ var NATIVE_LEASE_ACTIVITY_KEY = "nativeLeaseActivityMs";
36874
+ var NATIVE_LEASE_ADMISSION_KEY = "nativeLeaseAdmission";
36875
+ /**
36876
+ * WHICH delivered frames the decode worker retains a native copy of.
36877
+ *
36878
+ * - `all` — every frame the worker delivered to the runner. The shipped
36879
+ * behaviour, and the only correct one if something can ask for a crop of a
36880
+ * frame the runner never sent to inference.
36881
+ * - `inferred` — only the frames the runner ADMITTED to its detection queue.
36882
+ * A native-crop request always names a `frameId` that rode an inference
36883
+ * result, so that is the only set a request can name. How much it drops is
36884
+ * the two-plane governor's admit ratio and nothing else: measured at ~50% on
36885
+ * this cluster, not the ~80% the design sketch assumed, because the governor
36886
+ * was not throttling as hard as the sketch supposed. Read
36887
+ * `leaseAdmitted`/`leaseOffered` off the metrics line for the camera in front
36888
+ * of you rather than quoting a number from here. The newest delivered frame is
36889
+ * croppable regardless — it is still the worker's reserved slot, not a lease —
36890
+ * which covers the one-frame race between a mark and the supersede that
36891
+ * consumes it.
36892
+ */
36893
+ var NativeLeaseAdmissionSchema = z.enum(["all", "inferred"]);
36894
+ /**
36895
+ * Operator-tunable native-lease settings. Bounds are enforced HERE (not only in
36896
+ * the slider) because the value also travels to a forked child process, where a
36897
+ * junk number would silently become a 0-length or unbounded retention window.
36898
+ */
36899
+ var NativeLeaseSettingsSchema = z.object({
36900
+ /**
36901
+ * How long a retained native frame is served before it counts as a miss.
36902
+ *
36903
+ * Must cover the FULL late-crop horizon: detection inference + the
36904
+ * cross-process inference-result hop to hub post-analysis + tracking + the
36905
+ * tRPC crop round-trip back. Below ~500 ms the busiest cameras' subject crops
36906
+ * outrun it and fall back to the ≤640 detection frame; above ~3 s the resident
36907
+ * RAM per busy camera grows linearly with no measured hit-rate gain.
36908
+ */
36909
+ ttlMs: z.number().int().min(250).max(1e4),
36910
+ /**
36911
+ * Hard per-decode-worker RAM ceiling for retained native frames, in MB.
36912
+ *
36913
+ * Intended as a SAFETY ceiling with the TTL as the effective cap — but check
36914
+ * which one is actually binding before reasoning from that. At the shipped
36915
+ * 1024 MB and a 2 800 ms TTL, a 4K camera hits the CEILING first (~43 frames
36916
+ * at ~24 MB each) and the TTL never gets to expire anything; `leaseMb` /
36917
+ * `leaseFrames` on the metrics line say which. When the ceiling binds, a
36918
+ * change that admits fewer frames buys retention WINDOW at constant RAM
36919
+ * rather than giving RAM back — lower this knob if RAM is what you wanted.
36920
+ * `0` DISABLES the lease entirely and falls the worker back to the tiny
36921
+ * leak-prone GPU surface ring (~85% crop miss; that is what the lease exists
36922
+ * to replace).
36923
+ */
36924
+ budgetMb: z.number().int().min(0).max(4096),
36925
+ /**
36926
+ * Demand window: eager per-frame native retention runs only within this many
36927
+ * ms of the last native-crop request (or of the dial starting).
36928
+ *
36929
+ * `0` means ALWAYS ON — it disables the gate, it does not disable retention.
36930
+ * That is the legacy behaviour that saturated an N100 (24 native-4K downloads
36931
+ * per second on a camera with zero crop demand), so leave it non-zero unless
36932
+ * you are reproducing that.
36933
+ */
36934
+ activityMs: z.number().int().min(0).max(12e4),
36935
+ /**
36936
+ * Which delivered frames are retained at all — see
36937
+ * {@link NativeLeaseAdmissionSchema}. This is the only knob of the four that
36938
+ * changes WHAT is kept rather than for how long, so it is also the only one
36939
+ * that can turn a crop that used to hit into a miss. The worker counts every
36940
+ * crop request naming a frame it did NOT see marked
36941
+ * (`leaseUnmarkedCrops` on the session-decode metrics line): a non-zero value
36942
+ * there is the signal that some caller names frames outside the inference set
36943
+ * and that this must go back to `all`.
36944
+ */
36945
+ admission: NativeLeaseAdmissionSchema
36946
+ });
36947
+ /**
36948
+ * The values in force when the operator has set nothing — byte-for-byte the
36949
+ * constants the decode worker shipped with as env-var defaults, so making these
36950
+ * settings changed no behaviour on the day it landed.
36951
+ */
36952
+ var DEFAULT_NATIVE_LEASE_SETTINGS = {
36953
+ ttlMs: 1200,
36954
+ budgetMb: 1024,
36955
+ activityMs: 15e3,
36956
+ admission: "inferred"
36957
+ };
36958
+ /** Slider bounds for the operator-facing knobs (orchestrator settings UI). */
36959
+ var NATIVE_LEASE_TTL_FIELD = {
36960
+ min: 250,
36961
+ max: 1e4,
36962
+ step: 50,
36963
+ default: DEFAULT_NATIVE_LEASE_SETTINGS.ttlMs
36964
+ };
36965
+ var NATIVE_LEASE_BUDGET_FIELD = {
36966
+ min: 0,
36967
+ max: 4096,
36968
+ step: 64,
36969
+ default: DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb
36970
+ };
36971
+ var NATIVE_LEASE_ACTIVITY_FIELD = {
36972
+ min: 0,
36973
+ max: 12e4,
36974
+ step: 1e3,
36975
+ default: DEFAULT_NATIVE_LEASE_SETTINGS.activityMs
36976
+ };
36977
+ /** Select options for the admission knob (orchestrator settings UI). */
36978
+ var NATIVE_LEASE_ADMISSION_FIELD = {
36979
+ options: [{
36980
+ value: "all",
36981
+ label: "Every delivered frame"
36982
+ }, {
36983
+ value: "inferred",
36984
+ label: "Only frames sent to inference"
36985
+ }],
36986
+ default: DEFAULT_NATIVE_LEASE_SETTINGS.admission
36987
+ };
36988
+ /**
36989
+ * Parse one knob, reporting `null` for absent, junk, out-of-bounds — AND for a
36990
+ * value equal to the shipped default.
36991
+ *
36992
+ * That last rule is not tidiness, it is the difference between the documented
36993
+ * precedence being true and being a lie. `addon-settings.getGlobalSettings`
36994
+ * returns a HYDRATED payload, and `hydrateField` fills an unstored field with
36995
+ * the schema's own `default` (verified live on the hub: a cluster that has never
36996
+ * opened the form still reports `nativeLeaseTtlMs = 1200`). A reader that took
36997
+ * that at face value would report all three knobs as "set" on every cluster on
36998
+ * the day this shipped, permanently retiring the `CAMSTACK_SESSION_NATIVE_LEASE_*`
36999
+ * emergency override that the precedence promises. There is no raw-store read on
37000
+ * this cap to distinguish the two, so the default value itself is treated as
37001
+ * "the operator has expressed no preference" — which is also what leaving a
37002
+ * slider untouched means.
37003
+ *
37004
+ * The cost is one honest edge: an operator who deliberately selects the default
37005
+ * value in order to overrule an env var does not get it. Clear the env var
37006
+ * instead; the worker's spawn line names the source, so this is visible rather
37007
+ * than mysterious.
37008
+ */
37009
+ function readKnob(knob, raw) {
37010
+ const parsed = NativeLeaseSettingsSchema.shape[knob].safeParse(raw);
37011
+ if (!parsed.success) return null;
37012
+ return parsed.data === DEFAULT_NATIVE_LEASE_SETTINGS[knob] ? null : parsed.data;
37013
+ }
37014
+ /** {@link readKnob} for the one non-numeric knob. Same default-means-unset rule. */
37015
+ function readAdmissionKnob(raw) {
37016
+ const parsed = NativeLeaseAdmissionSchema.safeParse(raw);
37017
+ if (!parsed.success) return null;
37018
+ return parsed.data === DEFAULT_NATIVE_LEASE_SETTINGS.admission ? null : parsed.data;
37019
+ }
37020
+ /**
37021
+ * Narrow a FLAT settings record to the knobs the operator set.
37022
+ *
37023
+ * Per-FIELD parse, deliberately: a junk TTL must not also discard a valid
37024
+ * budget. An absent, out-of-bounds or default-valued knob is OMITTED (not
37025
+ * clamped, not defaulted) so the caller can still fall through to the env
37026
+ * override — clamping here would turn a typo into a value nobody chose. See
37027
+ * {@link readKnob} for why the default counts as unset.
37028
+ */
37029
+ function readNativeLeaseOverride(config) {
37030
+ const ttlMs = readKnob("ttlMs", config[NATIVE_LEASE_TTL_KEY]);
37031
+ const budgetMb = readKnob("budgetMb", config[NATIVE_LEASE_BUDGET_KEY]);
37032
+ const activityMs = readKnob("activityMs", config[NATIVE_LEASE_ACTIVITY_KEY]);
37033
+ const admission = readAdmissionKnob(config[NATIVE_LEASE_ADMISSION_KEY]);
37034
+ return {
37035
+ ...ttlMs === null ? {} : { ttlMs },
37036
+ ...budgetMb === null ? {} : { budgetMb },
37037
+ ...activityMs === null ? {} : { activityMs },
37038
+ ...admission === null ? {} : { admission }
37039
+ };
37040
+ }
37041
+ function isHydratedField(entry) {
37042
+ return typeof entry === "object" && entry !== null && "key" in entry;
37043
+ }
37044
+ var LEASE_KEYS = [
37045
+ NATIVE_LEASE_TTL_KEY,
37046
+ NATIVE_LEASE_BUDGET_KEY,
37047
+ NATIVE_LEASE_ACTIVITY_KEY,
37048
+ NATIVE_LEASE_ADMISSION_KEY
37049
+ ];
37050
+ /**
37051
+ * Extract the operator's lease overrides from an
37052
+ * `addon-settings.getGlobalSettings` payload.
37053
+ *
37054
+ * Walks EVERY section rather than looking inside {@link NATIVE_LEASE_SECTION_ID}
37055
+ * alone: the keys are unique across the addon's schema, and a section rename
37056
+ * must not silently revert the whole cluster to the defaults. A `null` payload
37057
+ * (addon mid-boot) means "operator set nothing" — the env/default fallback then
37058
+ * applies, which is the correct read of "I could not ask".
37059
+ */
37060
+ function pickNativeLeaseOverride(view) {
37061
+ if (view === null) return {};
37062
+ const flat = {};
37063
+ for (const section of view.sections) for (const entry of section.fields) {
37064
+ if (!isHydratedField(entry) || typeof entry.key !== "string") continue;
37065
+ if (LEASE_KEYS.includes(entry.key)) flat[entry.key] = entry.value;
37066
+ }
37067
+ return readNativeLeaseOverride(flat);
37068
+ }
37069
+ //#endregion
35107
37070
  //#region src/helpers/bind-addon-actions.ts
35108
37071
  /**
35109
37072
  * Bind an addon's custom-action catalog to its tRPC surface, returning a
@@ -35355,4 +37318,4 @@ function enumerateInferenceDevices(hw) {
35355
37318
  return out;
35356
37319
  }
35357
37320
  //#endregion
35358
- 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, AttachmentMediaTypeSchema, AttachmentSchema, 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, BacklightModeSchema, 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_NODE_PIN_CONTEXT_KEY, 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, ConvertArtifactSchema, ConvertResultSchema, ConvertTargetSchema, CoreBlockCompileResultSchema, CoreBlockInputSchema, CoreBlockPlacementSchema, CoreBlockSchema, CoreBlockStatusSchema, CoverStateSchema, CoverStatusSchema, CreateApiKeyInputSchema, CreateApiKeyResultSchema, CreateIntegrationInputSchema, CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, CreateUserInputSchema, CustomActionInputSchema, CustomModelDescriptorSchema, DATAPLANE_SECRET_HEADER, DEFAULT_ADDON_PLACEMENT, DEFAULT_AUDIO_ANALYZER_CONFIG, DEFAULT_DECODER_HWACCEL_CONFIG, DEFAULT_DETAIL_CROP_CONVENTION, DEFAULT_EVENT_COLOR, DEFAULT_FEATURES, DEFAULT_RETENTION, DEFAULT_SCRUB_THUMBNAIL_PRESET, DETAIL_CROP_PADDING_FIELD, DETAIL_CROP_PADDING_KEY, DETAIL_CROP_SECTION_ID, DETAIL_CROP_SQUARE_KEY, DEVICE_BACKEND_TO_FORMAT, DEVICE_CAP_NAMES, DEVICE_PROFILES, DEVICE_SCOPED_CAPS, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATE_READERS, DEVICE_STATUS_METHOD, DEVICE_TYPE_CONTROL_KIND, DEVICE_TYPE_INFO, EngineInfoSchema as DataStoreEngineInfoSchema, DayNightModeSchema, DayNightOptionsSchema, DayNightSettingsPatchSchema, DayNightStatusSchema, DecodedAudioChunkSchema, DecodedFrameSchema, DecoderSessionConfigSchema, DecoderStatsSchema, DeleteIntegrationResultSchema, DetailCropConventionSchema, DetectionSourceSchema, DeviceCodeSeveritySchema, DeviceConfig, DeviceDiscoveryStatusSchema, ExposeInputSchema as DeviceExportExposeInputSchema, DeviceExportStatusSchema, UnexposeInputSchema as DeviceExportUnexposeInputSchema, DeviceFeature, DeviceInfoSchema, DeviceLinkModeSchema, DeviceNetworkStatsSchema, DeviceRole, DeviceRuntimeState, DeviceStatusSchema, DeviceType, DiscoveredChildDeviceSchema, DiscoveredChildStatusSchema, DiscoveredDeviceSchema, DiscoveredTargetSchema, DisposerChain, DoorbellPressEventSchema, DoorbellStatusSchema, EVENTFUL_CAP_NAMES, EVENT_KIND_BY_CAP, EVENT_PAD_MS, EVENT_TAXONOMY, EXPRESSION_BUILTINS, EXPRESSION_BUILTIN_NAMES, EXPRESSION_COMPILE_CACHE_CAPACITY, EXPRESSION_IDENTIFIER_RE, EXPRESSION_INJECTED_NOW, ElementConfigStore, EmbeddingInfoSchema, EmbeddingResultSchema, EncodeProfileSchema, EncodedPacketSchema, EnrichedWidgetMetadataSchema, EnumSensorDateTimeFormatSchema, EnumSensorStatusSchema, EventCategory, EventEmitterStatusSchema, EventFireSchema, EventItemSchema, EventKindCategorySchema, EventKindDescriptorSchema, EventKindIconSchema, EventKindSchema, EventKindsForDeviceSchema, EventSourceType, ExportDownloadSchema, ExportOptionsSchema, ExportRecordSchema, ExportSetupFieldSchema, ExportSetupSchema, ExportSpeedSchema, ExportStateSchema, ExportTimelapseSchema, ExposedDeviceSchema, ExposureModeSchema, ExpressionEvalError, ExpressionParseError, FanControlStatusSchema, FanDirectionSchema, FeatureManifestSchema, FeatureProbeStatusSchema, FloodStatusSchema, FrameHandleFormatSchema, FrameHandleSchema, FrameInputSchema, GasStatusSchema, GetStreamWithCodecInputSchema, GlobalMetricsSchema, HF_BASE_URL, HF_REPO, HWACCEL_OPTIONS, HealthStatusSchema, HistoryPointSchema, HistoryResolutionEnum, HumidifierStatusSchema, HumiditySensorStatusSchema, HvacModeSchema, ImageContractSchema, ImageContractStateSchema, ImageRotateSchema, ImageSettingsOptionsSchema, ImageSettingsPatchSchema, ImageSettingsStatusSchema, ImageStatusSchema, IngestOwnerSchema, InstalledPackageSchema, IntegrationLiteSchema, IntegrationWithStateSchema, IntercomAbilitySchema, IntercomStatusSchema, KNOWN_CAP_NAMES, KeyEventSchema, LOG_LEVEL_RANK, LabelDefinitionSchema, LawnMowerActivitySchema, LawnMowerControlStatusSchema, LinkedDeviceSchema, LlmDefaultSchema, LlmDefaultSelectorSchema, LlmErrorCodeSchema, LlmGenerateBaseInputSchema, LlmGenerateErrSchema, LlmGenerateOkSchema, LlmGenerateResultSchema, LlmImageSchema, LlmNodeModelSchema, LlmProfileKindDescriptorSchema, LlmProfileKindSchema, LlmProfileSchema, LlmRuntimeCompleteInputSchema, LlmRuntimeDiskUsageSchema, LlmRuntimeNodeSchema, LlmRuntimeStatusSchema, LlmUsageRollupSchema, LlmUsageSchema, LocateSegmentResultSchema, LocationStatSchema, LockControlStatusSchema, LockStateSchema, LogEntrySchema, LogLevelSchema, LogStreamEntrySchema, LoginMethodContributionSchema, LoginStageEnum, MACRO_LABELS, MAX_EXPRESSION_AST_NODES, MAX_EXPRESSION_BINDINGS, MAX_EXPRESSION_CALL_ARGS, MAX_EXPRESSION_EVAL_STEPS, MAX_EXPRESSION_SOURCE_LENGTH, METHOD_ACCESS_MAP, MODEL_FORMATS, MOTION_TRIGGER_FEATURE, ManagedModelCatalogEntrySchema, ManagedModelRefSchema, ManagedRuntimeConfigSchema, MaskGridDimsSchema, MaskGridShapeSchema, MaskLineShapeSchema, MaskPointSchema, MaskPolygonShapeSchema, MaskPolygonVerticesSchema, MaskRectShapeSchema, MaskShapeKindSchema, MaskShapeSchema, MediaFileInfoSchema, MediaFileSchema, MediaPlayerRepeatSchema, MediaPlayerStateSchema, MediaPlayerStatusSchema, MeshPeerSchema, MeshStatusSchema, MethodAccessSchema, ModelCatalogEntrySchema, ModelConvertInputSchema, ModelConvertMetadataSchema, ModelDistributeInputSchema, ModelDistributeResultSchema, ModelExtraFileSchema, ModelFormatEntrySchema, ModelFormatsSchema, ModelSubstitutionSchema, ModelVariantGroupSchema, MotionAnalysisResultSchema, MotionEventSchema, MotionOnMotionChangedDataSchema, MotionRegionSchema, MotionSourceEnum, MotionSourcesSchema, MotionStatusSchema, MotionTriggerRuntimeStateSchema, MotionTriggerStatusSchema, MotionZoneOptionsSchema, MotionZonePatchSchema, MotionZoneRegionSchema, MotionZoneStatusSchema, StatusSchema as MqttBrokerStatusSchema, MutationFilterSchema, NC_BASE_CONDITION_KEYS, NC_CONDITION_CATALOG, NC_HISTORY_LIMIT_DEFAULT, NC_HISTORY_LIMIT_MAX, NC_MAX_PER_TRACK_IMMEDIATE, NC_SNOOZE_MAX_MINUTES, NC_TAXONOMY, NativeCropBboxSchema, NativeCropRefSchema, NativeCropResultSchema, NativeDetectionSchema, NativeObjectClassEnum, NativeObjectDetectionRuntimeStateSchema, NativeObjectDetectionStatusSchema, NcAlarmConfigSchema, NcAlarmModeCoverageSchema, NcAlarmSettingsPatchSchema, NcAlarmSettingsSchema, NcConditionDescriptorSchema, NcConditionsSchema, NcCrossingSchema, NcDeliverySchema, NcDeviceStateConditionSchema, NcHistoryEntrySchema, NcHistoryFilterSchema, NcHistoryRecordKindSchema, NcHistoryStatusSchema, NcHistorySubjectSchema, NcMediaFrameSchema, NcMediaPolicySchema, NcOccupancyConditionSchema, NcPlateMatcherSchema, NcRuleActionSchema, NcRuleActionSequenceSchema, NcRuleActionsSchema, NcRuleInputSchema, NcRuleNotificationButtonSchema, NcRulePatchSchema, NcRuleSchema, NcRuleTargetSchema, NcScheduleSchema, NcScheduleWindowSchema, NcSnoozeInputSchema, NcSnoozeSchema, NcSnoozeScopeSchema, NcSnoozeSuppressedSchema, NcTaxonomyEntrySchema, NcTaxonomySchema, NcTestResultSchema, NcThrottleGranularitySchema, NcThrottleSchema, NcZoneConditionSchema, NetworkAccessStatusSchema, NetworkAddressSchema, NetworkEndpointSchema, NotificationActionIconSchema, NotificationActionSchema, NotificationFormatSchema, NotificationSchema, NotifierStatusSchema, NumericSensorStatusSchema, OPS_LOG_DEFAULT_LIMIT, OPS_LOG_RING_DEFAULT_MAX, OauthIntegrationDescriptorSchema, ObjectEventSchema, OpsLogDomainSchema, OpsLogEntrySchema, OpsLogOpSchema, OpsLogQueryInputSchema, OpsLogReasonSchema, OrchestratorMetricsSchema, OsdOverlayKindEnum, OsdOverlayPatchSchema, OsdOverlaySchema, OsdPositionEnum, OsdStatusSchema, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, PIPELINE_FLOW_CAPABILITY_NAMES, PIPELINE_OWNER_CAPABILITY_NAMES, PROVIDER_KIND_CAP_NAMES, PYTHON_SCRIPT, PackageUpdateSchema, PackageVersionInfoSchema, PasskeyLoginMethodSchema, PasskeySummarySchema, PcmSampleFormatSchema, PerScopeBreakdownSchema, PetFeederStatusSchema, PickStreamPreferencesSchema, PickStreamRequirementsSchema, PickedCamStreamSchema, PipelineAssignmentSchema, PipelineDefaultStepSchema, PipelineEngineChoiceSchema, PipelineRunResultBridge, PipelineStepInputSchema, PipelineValidationIssueSchema, PipelineValidationResultSchema, PlaceholderReasonSchema, PolygonPointSchema, PowerMeterStatusSchema, PresenceStatusSchema, PressureSensorStatusSchema, PrivacyMaskOptionsSchema, PrivacyMaskPatchSchema, PrivacyMaskRegionSchema, PrivacyMaskShapeSchema, PrivacyMaskStatusSchema, ProfileRtspEntrySchema, ProfileSlotSchema, ProfileSlotStatusSchema, ProviderStatusSchema, PtzAutotrackRuntimeStateSchema, PtzAutotrackSettingsSchema, PtzAutotrackStatusSchema, PtzAutotrackTargetOptionSchema, PtzMoveCommandSchema, PtzOptionsSchema, PtzPositionSchema, PtzPresetSchema, PtzStatusSchema, QueryFilterSchema, REACHABILITY_FAILURES_TO_OFFLINE, REACHABILITY_POLL_INTERVAL_MS, REACHABILITY_PROBE_TIMEOUT_MS, RECOGNITION_TYPES, RESERVED_BINDING_NAMES, RUNTIME_DEFAULTS, RUNTIME_TO_FORMAT, RawStateResultSchema, ReadSegmentBytesResultSchema, ReadinessRegistry, ReadinessTimeoutError, RecentTracksPageSchema, RecentTracksQueryInput, RecordingAvailabilitySchema, RecordingBandModeSchema, RecordingBandSchema, RecordingBandTriggersSchema, RecordingConfigSchema, RecordingDaysSchema, RecordingDeviceUsageSchema, RecordingLocationUsageSchema, RecordingManifestSchema, RecordingRangeSchema, RecordingRetentionSchema, RecordingStatusSchema, RecordingStorageModeSchema, RecordingStorageUsageSchema, RecordingTriggersSchema, RecordingWeekdaySchema, RedirectLoginMethodSchema, RelocateFootageInputSchema, RelocateJobSchema, RelocateJobStateSchema, RelocateMediaInputSchema, RenderedAsSchema, ReportMotionInputSchema, RingBuffer, RtpSourceSchema, RtspRestreamEntrySchema, RunnerCameraConfigSchema, RunnerCameraDeviceUIFields, RunnerFrameSourceSchema, RunnerInferenceDeviceSchema, RunnerLocalLoadSchema, RunnerLocalMetricsSchema, SCOPE_PRESETS, SCRUB_THUMBNAIL_PRESETS, SCRUB_THUMBNAIL_PRESET_LABELS, SCRUB_THUMBNAIL_PRESET_ORDER, SENSOR_FEATURES, SENSOR_MAP, SOURCE_INFO_METADATA_KEY, STREAM_PROFILE_META, STREAM_QUALITY_LABELS, SUB_DETECTION_TYPES, SYSTEM_CAP_NAMES, SceneCheckSchema, SceneConditionSchema, SceneMonitorSchema, SceneMonitorStateSchema, SceneMonitorStatusSchema, SceneReferenceSchema, ScopedTokenSchema, ScopedTokenSummarySchema, ScoredObjectEventSchema, ScriptRunnerStatusSchema, ScrubThumbnailPresetSchema, SearchResultSchema, SendEmailInputSchema, SendEmailResultSchema, SendResultSchema, SensorEventSchema, ServerBootModeSchema, ServerPackageStatusSchema, ServerRollbackInfoSchema, ServerUpdateActionResultSchema, ServerUpdateCheckResultSchema, ServerUpdateStateSchema, SettingsPatchSchema, SettingsRecordSchema, SettingsSchemaWithValuesSchema, SettingsUpdateResultSchema, ShmRingStatsSchema, SmokeStatusSchema, SmtpStatusSchema, SnapshotImageSchema, SourceInfoSchema, SpatialDetectionSchema, SsoBridgeClaimsSchema, StartEmbeddedInputSchema, StationaryObjectSchema, 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, StreamNetworkStatsSchema, StreamParamsOptionsSchema, StreamParamsStatusSchema, StreamProfileConfigSchema, StreamProfileOptionsSchema, StreamProfilePatchSchema, StreamProfileSchema, StreamSourceEntrySchema, StreamSourceSchema, SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, SubscribeFramesInputSchema, SubscribeFramesResultSchema, SwitchStatusSchema, SystemMetricsSchema, SystemMirror, TAXONOMY_COLORS, TIMEZONES, TamperStatusSchema, TankStatusSchema, TargetKindCapsSchema, TargetKindLevelSchema, TargetKindSchema, TargetSchema, TemperatureSensorStatusSchema, TerminalProfileInfoSchema, TerminalSessionInfoSchema, TestConnectionResultSchema$1 as TestConnectionResultSchema, TestResultSchema, TimelapseRuleInputSchema, TimelapseRulePatchSchema, TimelapseRuleSchema, TimelapseTemplateSchema, ToastSchema, TokenScopeSchema, TopologyNodeSchema, TopologyProcessSchema, TopologyServiceSchema, TrackCascadeCountsSchema, TrackEnvelopeSchema, TrackProjectionSchema, TrackSchema, TrackStateSchema, TrackZoneFilterSchema, TrackedDetectionSchema, TurnServerSchema, UNIT_TABLE, BrokerInfoSchema$1 as UnifiedBrokerInfoSchema, UnitConversionError, UpdateIntegrationInputSchema, UpdateStatusSchema, UpdateUserInputSchema, UserRecordSchema, UserSummarySchema, VacuumControlStatusSchema, VacuumStateSchema, ValveStateSchema, ValveStatusSchema, VectorDeclareIndexInputSchema, VectorDeleteByFilterInputSchema, VectorDeleteInputSchema, VectorDeleteResultSchema, VectorFilterSchema, VectorGetInputSchema, VectorGetResultSchema, VectorItemSchema, VectorMatchSchema, VectorMetadataSchema, VectorMetricSchema, VectorQueryInputSchema, VectorQueryResultSchema, VectorStatsInputSchema, VectorStatsResultSchema, VectorUpsertInputSchema, VectorUpsertResultSchema, VibrationStatusSchema, VideoEncodeSchema, WELL_KNOWN_TABS, WELL_KNOWN_TAB_MAP, WaterHeaterStatusSchema, WeatherStatusSchema, WebrtcStreamChoiceSchema, WebrtcStreamTargetSchema, WhiteBalanceModeSchema, WidgetHostEnum, WidgetLoginMethodSchema, WidgetMetadataSchema, WidgetRemoteSchema, WidgetSizeEnum, YAMNET_TO_MACRO, ZoneCrossingDirectionSchema, ZoneCrossingSchema, ZoneKindEnum, ZoneRuleModeEnum, ZoneRuleSchema, ZoneRuleStageEnum, ZoneRulesArraySchema, ZoneSchema, ZoneScopeBreakdownSchema, accessoriesCapability, accessoryStableId, addonPagesCapability, addonPagesSourceCapability, addonRoutesCapability, addonSettingsCapability, addonWidgetsCapability, addonWidgetsSourceCapability, addonsCapability, adminUiCapability, 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, buildEventKindDescriptor, buildModelVariantGroups, buildNcTaxonomy, buildStreamParamsConfigSchema, buttonCapability, cameraCredentialsCapability, cameraPipelineConfigCapability, cameraStreamsCapability, canConvertUnit, carbonMonoxideCapability, cellsToRects, classifyStream, classifyStreams, climateControlCapability, collectHydratedFieldEntries, collectHydratedFieldValues, colorCapability, colorForKind, compileExpression, compileExpressionSafe, connectivityCapability, consumablesCapability, contactCapability, controlCapability, convertUnit, coreBlocksCapability, cosineSimilarity, coverCapability, createDeviceProxy, createDurableState, createEvent, createExpressionScope, createLazyTrpcSource, createMirrorSource, createRuntimeStateBridge, createSliceHandle, createSystemProxy, customAction, customModelRegistryCapability, dataStoreProviderCapability, dayNightCapability, decodeVectorBase64, decoderCapability, defaultDeviceFor, defineCustomActions, deriveDetailCropRect, deriveRecordingMode, describeModelVariant, detectionPipelineCapability, deviceAdoptionCapability, deviceBackendToFormat, deviceCustomAction, deviceDiscoveryCapability, deviceExportCapability, deviceManagerCapability, deviceMatchesProfile, deviceOpsCapability, deviceProviderCapability, deviceStateCapability, deviceStatusCapability, doorbellCapability, embeddingEncoderCapability, emitDownForOwnedCaps, emitReadiness, encodeProfileFromStreamShape, encodeVectorBase64, enumSensorCapability, enumerateInferenceDevices, enumerateItemArrayFields, enumerateSchemaFields, errMsg, evaluateAst, evaluateLinkExpression, evaluateZoneRules, event, eventEmitterCapability, eventsCapability, expandCapMethods, extractNestedAddonId, extractSourceInfoFromMetadata, faceGalleryCapability, fanControlCapability, featureProbeCapability, filesystemBrowseCapability, findTimezone, floodCapability, formatForBackend, formatForRuntime, gasCapability, getAudioMacroClassIds, getByPath, getCapsByProviderKind, getTaxonomyEntry, hasMotionTrigger, hfModelUrl, htmlToText, humidifierCapability, humiditySensorCapability, hydrateSchema, imageCapability, imageSettingsCapability, integrationsCapability, intercomCapability, isAgentOnlyPlacement, isArrayOutputSchema, isBaseConditionKey, isCollectionArrayMethod, isDeployableToAgent, isDeviceConfigCap, isDeviceScopedCap, isEvent, isNode, isObjectInput, isVoidInput, jobKindSchema, kebabToCamel, knownValues, lawnMowerControlCapability, lifecycleJobSchema, lifecycleJobScopeSchema, lifecycleJobStateSchema, lifecycleTaskSchema, llmCapability, llmRuntimeCapability, localNetworkCapability, locationSimilarity, lockControlCapability, logDestinationCapability, logLevelAtMost, loginMethodCapability, looseSchema, makeProfileBrokerId, makeSourceBrokerId, mapAudioLabelToMacro, markdownToHtmlLite, markdownToText, maskUrlCredentials, mediaPlayerCapability, mergeSourceInfo, meshNetworkCapability, method, metricsProviderCapability, modelConvertCapability, modelDistributorCapability, modelFormatForRuntime, motionCapability, motionDetectionCapability, motionTriggerCapability, motionZonesCapability, mqttBrokerCapability, nativeObjectDetectionCapability, networkAccessCapability, networkQualityCapability, nodePin, nodesCapability, normalizeAddonInitResult, normalizeUnit, notificationOutputCapability, notificationRulesCapability, notifierCapability, numericSensorCapability, oauthIntegrationCapability, objectInputDeclaresAddonId, osdCapability, parseCameraStreamConfig, parseExpression, parseJsonArray, parseJsonObject, parseJsonUnknown, parseProfileBrokerId, parseStreamParamsFormPatch, petFeederCapability, pickAccessoryControl, pickDetailCropConvention, pickPreferredRtspEntry, pickerForCondition, pipelineAnalyticsCapability, pipelineExecutorCapability, pipelineOrchestratorCapability, pipelineRunnerCapability, plateGalleryCapability, platformProbeCapability, powerMeterCapability, prepareNotification, presenceCapability, pressureSensorCapability, privacyMaskCapability, procedureAuthKey, ptzAutotrackCapability, ptzCapability, pythonScriptForBackend, readDetailCropConvention, readDeviceStateFrom, readNodePin, readinessKey, rebootCapability, recordingCapability, recordingExportCapability, rectsToCells, requiresPython, resolveAddonExecution, resolveAddonGroup, resolveAddonPlacement, resolveAddonRuntime, resolveCapMount, resolveDetectionRuntime, resolveDeviceControlKind, resolveDeviceProfile, resolveFormat, resolveHydratedFieldValue, resolveModelFormat, resolveMutate, resolveRunnerId, resolveScrubThumbnailGeometry, resolveVariantModelId, runInferenceStep, runtimeDevices, sceneMonitorCapability, scopeKey, scoreRuntimes, scriptRunnerCapability, selectAssignedProfileSlots, serverManagementCapability, setByPath, settingsStoreCapability, sleep, sleepCancellable, smokeCapability, smtpProviderCapability, snapshotCapability, ssoBridgeCapability, startReachabilityPoll, stateVocabularyFor, storageCapability, storageEvictableCapability, storageProviderCapability, streamBrokerCapability, streamCatalogCapability, streamParamsCapability, streamPixels, streamQualityLabel, subKindsOf, supportedRuntimes, switchCapability, synthesizeSourceInfo, systemCapability, tamperCapability, taskLogEntrySchema, taskPhaseSchema, taskTargetSchema, temperatureSensorCapability, terminalSessionCapability, textToHtml, toDeviceSummary, toExpressionValue, toStreamSourceEntry, toastCapability, tokenize, transcodeBody, tryConvertUnit, turnProviderCapability, unitDimension, unitsForDimension, updateCapability, userManagementCapability, userPasskeysCapability, vacuumControlCapability, validateExpressionSource, valveCapability, vectorDimFromBase64, vectorStoreCapability, vibrationCapability, videoclipsCapability, viewerUiCapability, waterHeaterCapability, weatherCapability, webrtcClientHintsSchema, webrtcSessionCapability, wiringAddonHealthSchema, wiringHealthSnapshotSchema, wiringNodeHealthSchema, wiringProbeKindSchema, wiringProbeResultSchema, zodEntriesToConfigUI, zoneAnalyticsCapability, zoneRulesCapability, zonesCapability };
37321
+ export { ACCESSORY_LABEL, ALEXA_EGRESS_PROFILE, ALL_CAPABILITY_DEFINITIONS, APPLE_SA_TO_MACRO, AUDIO_ANALYSIS_CAP_NAME, AUDIO_BACKEND_CHOICES, AUDIO_MACRO_LABELS, AUDIO_PRESETS, 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, AttachmentMediaTypeSchema, AttachmentSchema, AudioAnalysisResultSchema, AudioAnalysisSettingsSchema, AudioChunkInputSchema, AudioClassSummarySchema, AudioClassificationLabelSchema, AudioClassificationResultSchema, AudioCodecInfoSchema, AudioDecodeSessionConfigSchema, AudioEncodeSchema, AudioEncodeSessionConfigSchema, AudioEncodedChunkSchema, AudioEventSchema, AudioLevelSchema, AudioMetricsHistoryPointSchema, AudioMetricsHistorySchema, AudioMetricsSnapshotSchema, AudioPcmChunkSchema, AuthResultSchema, AutoUpdateSettingsSchema, AutomationControlStatusSchema, AvailableIntegrationTypeSchema, BACKEND_TO_FORMAT, BASE_LIVE_EGRESS_PROFILE, BATTERY_DEVICE_PROFILE, BacklightModeSchema, 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, CAMERA_SWITCH_CATALOG, CAMERA_SWITCH_ORDER, CAM_PROFILE_ORDER, CAPABILITY_NAMES, CAPABILITY_ROUTER_KEYS, CAP_NAMES_WITH_STATUS, CAP_NODE_PIN_CONTEXT_KEY, 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, CameraSwitchAuthoritySchema, CameraSwitchGroupSchema, CameraSwitchIdSchema, CameraSwitchSchema, CameraSwitchUnavailableReasonSchema, CandidateQueryFilterSchema, CapScopeSchema, CapabilityBindingsSchema, CarbonMonoxideStatusSchema, ChargingStatus, ClientNetworkStatsSchema, ClimateControlStatusSchema, ClipPlaybackSchema, ClipSchema, ClusterAddonNodeDeploymentSchema, ClusterAddonStatusEntrySchema, CollectionColumnSchema, CollectionIndexSchema, ColorStatusSchema, ConfigEntrySchema, ConfigSectionWithValuesSchema, ConfigTabDeclarationSchema, ConnectivityStatusSchema, ConsumableItemSchema, ConsumablesStatusSchema, ContactStatusSchema, ControlKindSchema, ControlStatusSchema, ConvertArtifactSchema, ConvertResultSchema, ConvertTargetSchema, CoreBlockCompileResultSchema, CoreBlockInputSchema, CoreBlockPlacementSchema, CoreBlockSchema, CoreBlockStatusSchema, CoverStateSchema, CoverStatusSchema, CreateApiKeyInputSchema, CreateApiKeyResultSchema, CreateIntegrationInputSchema, CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, CreateUserInputSchema, CustomActionInputSchema, CustomModelDescriptorSchema, DATAPLANE_SECRET_HEADER, DEFAULT_ADDON_PLACEMENT, DEFAULT_AUDIO_ANALYZER_CONFIG, DEFAULT_DECODER_HWACCEL_CONFIG, DEFAULT_DETAIL_CROP_CONVENTION, DEFAULT_EVENT_COLOR, DEFAULT_FEATURES, DEFAULT_NATIVE_LEASE_SETTINGS, DEFAULT_RETENTION, DEFAULT_SCRUB_THUMBNAIL_PRESET, DETAIL_CROP_PADDING_FIELD, DETAIL_CROP_PADDING_KEY, DETAIL_CROP_SECTION_ID, DETAIL_CROP_SQUARE_KEY, DETECTION_PIPELINE_CAP_NAME, DEVICE_BACKEND_TO_FORMAT, DEVICE_CAP_NAMES, DEVICE_PROFILES, DEVICE_SCOPED_CAPS, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATE_READERS, DEVICE_STATUS_METHOD, DEVICE_TYPE_CONTROL_KIND, DEVICE_TYPE_INFO, EngineInfoSchema as DataStoreEngineInfoSchema, DayNightModeSchema, DayNightOptionsSchema, DayNightSettingsPatchSchema, DayNightStatusSchema, DecodedAudioChunkSchema, DecodedFrameSchema, DecoderSessionConfigSchema, DecoderStatsSchema, DeleteIntegrationResultSchema, DetailCropConventionSchema, DetectionSourceSchema, DeviceCodeSeveritySchema, DeviceConfig, DeviceDiscoveryStatusSchema, ExposeInputSchema as DeviceExportExposeInputSchema, DeviceExportStatusSchema, UnexposeInputSchema as DeviceExportUnexposeInputSchema, DeviceFeature, DeviceInfoSchema, DeviceLinkModeSchema, DeviceNetworkStatsSchema, DeviceRole, DeviceRuntimeState, DeviceStatusSchema, DeviceType, DiscoveredChildDeviceSchema, DiscoveredChildStatusSchema, DiscoveredDeviceSchema, DiscoveredTargetSchema, DisposerChain, DoorbellPressEventSchema, DoorbellStatusSchema, EVENTFUL_CAP_NAMES, EVENT_KIND_BY_CAP, EVENT_PAD_MS, EVENT_TAXONOMY, EXPRESSION_BUILTINS, EXPRESSION_BUILTIN_NAMES, EXPRESSION_COMPILE_CACHE_CAPACITY, EXPRESSION_IDENTIFIER_RE, EXPRESSION_INJECTED_NOW, EgressEncodeSchema, EgressRateControlSchema, EgressTranscodeRequestSchema, EgressTranscodeSchema, ElementConfigStore, EmbeddingInfoSchema, EmbeddingResultSchema, EncodeProfileSchema, EncodedPacketSchema, EnrichedWidgetMetadataSchema, EnumSensorDateTimeFormatSchema, EnumSensorStatusSchema, EventCategory, EventEmitterStatusSchema, EventFireSchema, EventItemSchema, EventKindCategorySchema, EventKindDescriptorSchema, EventKindIconSchema, EventKindSchema, EventKindsForDeviceSchema, EventSourceType, ExportDownloadSchema, ExportOptionsSchema, ExportRecordSchema, ExportSetupFieldSchema, ExportSetupSchema, ExportSpeedSchema, ExportStateSchema, ExportTimelapseSchema, ExposedDeviceSchema, ExposureModeSchema, ExpressionEvalError, ExpressionParseError, FanControlStatusSchema, FanDirectionSchema, FeatureManifestSchema, FeatureProbeStatusSchema, FloodStatusSchema, FrameHandleFormatSchema, FrameHandleSchema, FrameInputSchema, GasStatusSchema, GetStreamWithCodecInputSchema, GlobalMetricsSchema, HAP_AUDIO_BASE, HAP_AUDIO_BITRATE_KBPS, HAP_AUDIO_VBV_KBITS, HAP_KEYFRAME_INTERVAL_SEC, HF_BASE_URL, HF_REPO, HWACCEL_OPTIONS, HealthStatusSchema, HistoryPointSchema, HistoryResolutionEnum, HumidifierStatusSchema, HumiditySensorStatusSchema, HvacModeSchema, ImageContractSchema, ImageContractStateSchema, ImageRotateSchema, ImageSettingsOptionsSchema, ImageSettingsPatchSchema, ImageSettingsStatusSchema, ImageStatusSchema, IngestOwnerSchema, InstalledPackageSchema, IntegrationLiteSchema, IntegrationWithStateSchema, IntercomAbilitySchema, IntercomStatusSchema, KNOWN_CAP_NAMES, KeyEventSchema, LOG_LEVEL_RANK, LabelDefinitionSchema, LawnMowerActivitySchema, LawnMowerControlStatusSchema, LinkedDeviceSchema, LlmDefaultSchema, LlmDefaultSelectorSchema, LlmErrorCodeSchema, LlmGenerateBaseInputSchema, LlmGenerateErrSchema, LlmGenerateOkSchema, LlmGenerateResultSchema, LlmImageSchema, LlmNodeModelSchema, LlmProfileKindDescriptorSchema, LlmProfileKindSchema, LlmProfileSchema, LlmRuntimeCompleteInputSchema, LlmRuntimeDiskUsageSchema, LlmRuntimeNodeSchema, LlmRuntimeStatusSchema, LlmUsageRollupSchema, LlmUsageSchema, LocateSegmentResultSchema, LocationStatSchema, LockControlStatusSchema, LockStateSchema, LogEntrySchema, LogLevelSchema, LogStreamEntrySchema, LoginMethodContributionSchema, LoginStageEnum, MACRO_LABELS, MAX_EXPRESSION_AST_NODES, MAX_EXPRESSION_BINDINGS, MAX_EXPRESSION_CALL_ARGS, MAX_EXPRESSION_EVAL_STEPS, MAX_EXPRESSION_SOURCE_LENGTH, METHOD_ACCESS_MAP, MODEL_FORMATS, MOTION_TRIGGER_FEATURE, ManagedModelCatalogEntrySchema, ManagedModelRefSchema, ManagedRuntimeConfigSchema, MaskGridDimsSchema, MaskGridShapeSchema, MaskLineShapeSchema, MaskPointSchema, MaskPolygonShapeSchema, MaskPolygonVerticesSchema, MaskRectShapeSchema, MaskShapeKindSchema, MaskShapeSchema, MediaFileInfoSchema, MediaFileSchema, MediaPlayerRepeatSchema, MediaPlayerStateSchema, MediaPlayerStatusSchema, MeshPeerSchema, MeshStatusSchema, MethodAccessSchema, ModelCatalogEntrySchema, ModelConvertInputSchema, ModelConvertMetadataSchema, ModelDistributeInputSchema, ModelDistributeResultSchema, ModelExtraFileSchema, ModelFormatEntrySchema, ModelFormatsSchema, ModelSubstitutionSchema, ModelVariantGroupSchema, MotionAnalysisResultSchema, MotionEventSchema, MotionOnMotionChangedDataSchema, MotionRegionSchema, MotionSourceEnum, MotionSourcesSchema, MotionStatusSchema, MotionTriggerRuntimeStateSchema, MotionTriggerStatusSchema, MotionZoneOptionsSchema, MotionZonePatchSchema, MotionZoneRegionSchema, MotionZoneStatusSchema, StatusSchema as MqttBrokerStatusSchema, MutationFilterSchema, NATIVE_LEASE_ACTIVITY_FIELD, NATIVE_LEASE_ACTIVITY_KEY, NATIVE_LEASE_ADMISSION_FIELD, NATIVE_LEASE_ADMISSION_KEY, NATIVE_LEASE_BUDGET_FIELD, NATIVE_LEASE_BUDGET_KEY, NATIVE_LEASE_SECTION_ID, NATIVE_LEASE_TTL_FIELD, NATIVE_LEASE_TTL_KEY, NC_BASE_CONDITION_KEYS, NC_CONDITION_CATALOG, NC_HISTORY_LIMIT_DEFAULT, NC_HISTORY_LIMIT_MAX, NC_MAX_PER_TRACK_IMMEDIATE, NC_SNOOZE_MAX_MINUTES, NC_TAXONOMY, NativeCropBboxSchema, NativeCropRefSchema, NativeCropResultSchema, NativeDetectionSchema, NativeLeaseAdmissionSchema, NativeLeaseSettingsSchema, NativeObjectClassEnum, NativeObjectDetectionRuntimeStateSchema, NativeObjectDetectionStatusSchema, NcAlarmConfigSchema, NcAlarmModeCoverageSchema, NcAlarmSettingsPatchSchema, NcAlarmSettingsSchema, NcConditionDescriptorSchema, NcConditionsSchema, NcCrossingSchema, NcDeliverySchema, NcDeviceStateConditionSchema, NcHistoryEntrySchema, NcHistoryFilterSchema, NcHistoryRecordKindSchema, NcHistoryStatusSchema, NcHistorySubjectSchema, NcMediaFrameSchema, NcMediaPolicySchema, NcOccupancyConditionSchema, NcPlateMatcherSchema, NcRuleActionSchema, NcRuleActionSequenceSchema, NcRuleActionsSchema, NcRuleInputSchema, NcRuleNotificationButtonSchema, NcRulePatchSchema, NcRuleSchema, NcRuleTargetSchema, NcScheduleSchema, NcScheduleWindowSchema, NcSnoozeInputSchema, NcSnoozeSchema, NcSnoozeScopeSchema, NcSnoozeSuppressedSchema, NcTaxonomyEntrySchema, NcTaxonomySchema, NcTestResultSchema, NcThrottleGranularitySchema, NcThrottleSchema, NcZoneConditionSchema, NetworkAccessStatusSchema, NetworkAddressSchema, NetworkEndpointSchema, NotificationActionIconSchema, NotificationActionSchema, NotificationFormatSchema, NotificationSchema, NotifierStatusSchema, NumericSensorStatusSchema, OPS_LOG_DEFAULT_LIMIT, OPS_LOG_RING_DEFAULT_MAX, OauthIntegrationDescriptorSchema, ObjectEventSchema, OpsLogDomainSchema, OpsLogEntrySchema, OpsLogOpSchema, OpsLogQueryInputSchema, OpsLogReasonSchema, OrchestratorMetricsSchema, OsdOverlayKindEnum, OsdOverlayPatchSchema, OsdOverlaySchema, OsdPositionEnum, OsdStatusSchema, PET_FEEDER_MANUAL_FEED_MAX, PET_FEEDER_MANUAL_FEED_MIN, PIPELINE_FLOW_CAPABILITY_NAMES, PIPELINE_OWNER_CAPABILITY_NAMES, PRIVACY_MASK_CAP_NAME, PROVIDER_KIND_CAP_NAMES, PYTHON_SCRIPT, PackageUpdateSchema, PackageVersionInfoSchema, PasskeyLoginMethodSchema, PasskeySummarySchema, PcmSampleFormatSchema, PerScopeBreakdownSchema, PetFeederStatusSchema, PickStreamPreferencesSchema, PickStreamRequirementsSchema, PickedCamStreamSchema, PipelineAssignmentSchema, PipelineDefaultStepSchema, PipelineEngineChoiceSchema, PipelineRunResultBridge, PipelineStepInputSchema, PipelineValidationIssueSchema, PipelineValidationResultSchema, PlaceholderReasonSchema, PolygonPointSchema, PowerMeterStatusSchema, PresenceStatusSchema, PressureSensorStatusSchema, PrivacyMaskOptionsSchema, PrivacyMaskPatchSchema, PrivacyMaskRegionSchema, PrivacyMaskShapeSchema, PrivacyMaskStatusSchema, ProfileRtspEntrySchema, ProfileSlotSchema, ProfileSlotStatusSchema, ProviderStatusSchema, PtzAutotrackRuntimeStateSchema, PtzAutotrackSettingsSchema, PtzAutotrackStatusSchema, PtzAutotrackTargetOptionSchema, PtzMoveCommandSchema, PtzOptionsSchema, PtzPositionSchema, PtzPresetSchema, PtzStatusSchema, QueryFilterSchema, RATE_CONTROL_RELAXED, RATE_CONTROL_TIGHT, REACHABILITY_FAILURES_TO_OFFLINE, REACHABILITY_POLL_INTERVAL_MS, REACHABILITY_PROBE_TIMEOUT_MS, RECOGNITION_TYPES, RESERVED_BINDING_NAMES, RUNTIME_DEFAULTS, RUNTIME_TO_FORMAT, RawStateResultSchema, ReadGopBytesResultSchema, ReadSegmentBytesResultSchema, ReadinessRegistry, ReadinessTimeoutError, RecentTracksPageSchema, RecentTracksQueryInput, RecordingAvailabilitySchema, RecordingBandModeSchema, RecordingBandSchema, RecordingBandTriggersSchema, RecordingConfigSchema, RecordingDaysSchema, RecordingDeviceUsageSchema, RecordingLocationUsageSchema, RecordingManifestSchema, RecordingRangeSchema, RecordingRetentionSchema, RecordingStatusSchema, RecordingStorageModeSchema, RecordingStorageUsageSchema, RecordingTriggersSchema, RecordingWeekdaySchema, RedirectLoginMethodSchema, RelocateFootageInputSchema, RelocateJobSchema, RelocateJobStateSchema, RelocateMediaInputSchema, RenderedAsSchema, ReportMotionInputSchema, RingBuffer, RtpSourceSchema, RtspRestreamEntrySchema, RunnerCameraConfigSchema, RunnerCameraDeviceUIFields, RunnerFrameSourceSchema, RunnerInferenceDeviceSchema, RunnerLocalLoadSchema, RunnerLocalMetricsSchema, SCOPE_PRESETS, SCRUB_THUMBNAIL_PRESETS, SCRUB_THUMBNAIL_PRESET_LABELS, SCRUB_THUMBNAIL_PRESET_ORDER, SENSOR_FEATURES, SENSOR_MAP, SOURCE_INFO_METADATA_KEY, STREAM_PROFILE_META, STREAM_QUALITY_LABELS, SUB_DETECTION_TYPES, SYSTEM_CAP_NAMES, SceneCheckSchema, SceneConditionSchema, SceneMonitorSchema, SceneMonitorStateSchema, SceneMonitorStatusSchema, SceneReferenceSchema, ScopedTokenSchema, ScopedTokenSummarySchema, ScoredObjectEventSchema, ScriptRunnerStatusSchema, ScrubThumbnailPresetSchema, SearchResultSchema, SendEmailInputSchema, SendEmailResultSchema, SendResultSchema, SensorEventSchema, ServerBootModeSchema, ServerPackageStatusSchema, ServerRollbackInfoSchema, ServerUpdateActionResultSchema, ServerUpdateCheckResultSchema, ServerUpdateStateSchema, SettingsPatchSchema, SettingsRecordSchema, SettingsSchemaWithValuesSchema, SettingsUpdateResultSchema, ShmRingStatsSchema, SmokeStatusSchema, SmtpStatusSchema, SnapshotImageSchema, SourceInfoSchema, SpatialDetectionSchema, SsoBridgeClaimsSchema, StartEmbeddedInputSchema, StationaryObjectSchema, 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, StreamNetworkStatsSchema, StreamParamsOptionsSchema, StreamParamsStatusSchema, StreamProfileConfigSchema, StreamProfileOptionsSchema, StreamProfilePatchSchema, StreamProfileSchema, StreamSourceEntrySchema, StreamSourceSchema, SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, SubscribeFramesInputSchema, SubscribeFramesResultSchema, SwitchStatusSchema, SystemMetricsSchema, SystemMirror, TAXONOMY_COLORS, TIMEZONES, TRANSCODE_DOWN_MAX_BITRATE_KBPS, TRANSCODE_DOWN_MAX_HEIGHT, TamperStatusSchema, TankStatusSchema, TargetKindCapsSchema, TargetKindLevelSchema, TargetKindSchema, TargetSchema, TemperatureSensorStatusSchema, TerminalProfileInfoSchema, TerminalSessionInfoSchema, TestConnectionResultSchema$1 as TestConnectionResultSchema, TestResultSchema, TimelapseRuleInputSchema, TimelapseRulePatchSchema, TimelapseRuleSchema, TimelapseTemplateSchema, ToastSchema, TokenScopeSchema, TopologyNodeSchema, TopologyProcessSchema, TopologyServiceSchema, TrackCascadeCountsSchema, TrackEnvelopeSchema, TrackFlagsPatchSchema, TrackFlagsSchema, TrackProjectionSchema, TrackSchema, TrackSourceSchema, TrackStateSchema, TrackZoneFilterSchema, TrackedDetectionSchema, TurnServerSchema, UNIT_TABLE, BrokerInfoSchema$1 as UnifiedBrokerInfoSchema, UnitConversionError, UpdateIntegrationInputSchema, UpdateStatusSchema, UpdateUserInputSchema, UserRecordSchema, UserSummarySchema, VacuumControlStatusSchema, VacuumStateSchema, ValveStateSchema, ValveStatusSchema, VectorDeclareIndexInputSchema, VectorDeleteByFilterInputSchema, VectorDeleteInputSchema, VectorDeleteResultSchema, VectorFilterSchema, VectorGetInputSchema, VectorGetResultSchema, VectorItemSchema, VectorMatchSchema, VectorMetadataSchema, VectorMetricSchema, VectorQueryInputSchema, VectorQueryResultSchema, VectorStatsInputSchema, VectorStatsResultSchema, VectorUpsertInputSchema, VectorUpsertResultSchema, VibrationStatusSchema, VideoEncodeSchema, WEBRTC_EGRESS_PROFILE, WELL_KNOWN_TABS, WELL_KNOWN_TAB_MAP, WaterHeaterStatusSchema, WeatherStatusSchema, WebrtcStreamChoiceSchema, WebrtcStreamTargetSchema, WhiteBalanceModeSchema, WidgetHostEnum, WidgetLoginMethodSchema, WidgetMetadataSchema, WidgetRemoteSchema, WidgetSizeEnum, YAMNET_TO_MACRO, ZoneCrossingDirectionSchema, ZoneCrossingSchema, ZoneKindEnum, ZoneRuleModeEnum, ZoneRuleSchema, ZoneRuleStageEnum, ZoneRulesArraySchema, ZoneSchema, ZoneScopeBreakdownSchema, accessoriesCapability, accessoryStableId, addonPagesCapability, addonPagesSourceCapability, addonRoutesCapability, addonSettingsCapability, addonWidgetsCapability, addonWidgetsSourceCapability, addonsCapability, adminUiCapability, airQualitySensorCapability, alarmPanelCapability, alertsCapability, ambientLightSensorCapability, applyTransform, asBoolean, asJsonArray, asJsonObject, asNumber, asString, audioAnalysisCapability, audioAnalyzerCapability, audioCodecCapability, audioMetricsCapability, audioPlanFromEncodeProfile, authProviderCapability, autoAssignProfiles, automationControlCapability, backupCapability, bareAddonId, batteryCapability, bestLocationMatch, binaryCapability, bindAddonActions, brightnessCapability, brokerCapability, buildAddonRouteProvider, buildAudioArgs, buildEventKindDescriptor, buildFfmpegArgs, buildInputArgs, buildModelVariantGroups, buildNcTaxonomy, buildStreamParamsConfigSchema, buildVideoArgs, buttonCapability, cameraCredentialsCapability, cameraPipelineConfigCapability, cameraStreamsCapability, canConvertUnit, canonicalEgressPlan, carbonMonoxideCapability, cellsToRects, classifyStream, classifyStreams, climateControlCapability, collectHydratedFieldEntries, collectHydratedFieldValues, colorCapability, colorForKind, compileExpression, compileExpressionSafe, connectivityCapability, consumablesCapability, contactCapability, controlCapability, convertUnit, coreBlocksCapability, cosineSimilarity, coverCapability, createDeviceProxy, createDurableState, createEvent, createExpressionScope, createHwAccelCache, createLazyTrpcSource, createMirrorSource, createRuntimeStateBridge, createSliceHandle, createSystemProxy, customAction, customModelRegistryCapability, dataStoreProviderCapability, dayNightCapability, decodeVectorBase64, decoderCapability, defaultDeviceFor, defineCustomActions, deriveCameraSwitches, deriveDetailCropRect, deriveRecordingMode, describeModelVariant, detectionPipelineCapability, deviceAdoptionCapability, deviceBackendToFormat, deviceCustomAction, deviceDiscoveryCapability, deviceExportCapability, deviceManagerCapability, deviceMatchesProfile, deviceOpsCapability, deviceProviderCapability, deviceStateCapability, deviceStatusCapability, doorbellCapability, egressTranscodeSharingKey, egressTransportFromRequest, embeddingEncoderCapability, emitDownForOwnedCaps, emitReadiness, encodeProfileFromStreamShape, encodeVectorBase64, enumSensorCapability, enumerateInferenceDevices, enumerateItemArrayFields, enumerateSchemaFields, errMsg, evaluateAst, evaluateLinkExpression, evaluateZoneRules, event, eventEmitterCapability, eventsCapability, expandCapMethods, extractNestedAddonId, extractSourceInfoFromMetadata, faceGalleryCapability, fanControlCapability, featureProbeCapability, filesystemBrowseCapability, findTimezone, floodCapability, formatForBackend, formatForRuntime, gasCapability, getAudioMacroClassIds, getByPath, getCapsByProviderKind, getTaxonomyEntry, hasMotionTrigger, hfModelUrl, htmlToText, humidifierCapability, humiditySensorCapability, hydrateSchema, imageCapability, imageSettingsCapability, integrationsCapability, intercomCapability, invocationFromEncodeProfile, isAgentOnlyPlacement, isArrayOutputSchema, isBaseConditionKey, isCollectionArrayMethod, isDeployableToAgent, isDeviceConfigCap, isDeviceScopedCap, isEvent, isNode, isObjectInput, isSameAddonId, isSoftwareDecode, isVoidInput, jobKindSchema, kebabToCamel, knownValues, lawnMowerControlCapability, lifecycleJobSchema, lifecycleJobScopeSchema, lifecycleJobStateSchema, lifecycleTaskSchema, llmCapability, llmRuntimeCapability, localNetworkCapability, locationSimilarity, lockControlCapability, logBannerArgs, logDestinationCapability, logLevelAtMost, loginMethodCapability, looseSchema, makeProfileBrokerId, makeSourceBrokerId, mapAudioLabelToMacro, markdownToHtmlLite, markdownToText, maskUrlCredentials, mediaPlayerCapability, mergeSourceInfo, meshNetworkCapability, method, metricsProviderCapability, modelConvertCapability, modelDistributorCapability, modelFormatForRuntime, motionCapability, motionDetectionCapability, motionTriggerCapability, motionZonesCapability, mqttBrokerCapability, nativeObjectDetectionCapability, networkAccessCapability, networkQualityCapability, nodePin, nodesCapability, normalizeAddonInitResult, normalizeUnit, notificationOutputCapability, notificationRulesCapability, notifierCapability, numericSensorCapability, oauthIntegrationCapability, objectInputDeclaresAddonId, osdCapability, parseCameraStreamConfig, parseExpression, parseJsonArray, parseJsonObject, parseJsonUnknown, parseProfileBrokerId, parseStreamParamsFormPatch, petFeederCapability, pickAccessoryControl, pickDetailCropConvention, pickNativeLeaseOverride, pickPreferredRtspEntry, pickVideoEncoder, pickerForCondition, pipelineAnalyticsCapability, pipelineExecutorCapability, pipelineOrchestratorCapability, pipelineRunnerCapability, plateGalleryCapability, platformProbeCapability, powerMeterCapability, prepareNotification, presenceCapability, pressureSensorCapability, privacyMaskCapability, procedureAuthKey, ptzAutotrackCapability, ptzCapability, pythonScriptForBackend, readDetailCropConvention, readDeviceStateFrom, readNativeLeaseOverride, readNodePin, readinessKey, rebootCapability, recordingCapability, recordingExportCapability, rectsToCells, requiresPython, resolveAddonExecution, resolveAddonGroup, resolveAddonPlacement, resolveAddonRuntime, resolveCapMount, resolveDetectionRuntime, resolveDeviceControlKind, resolveDeviceProfile, resolveEgressDecodeHwAccel, resolveFormat, resolveHydratedFieldValue, resolveModelFormat, resolveMutate, resolveRunnerId, resolveScrubThumbnailGeometry, resolveVariantModelId, runInferenceStep, runtimeDevices, sceneMonitorCapability, scopeKey, scoreRuntimes, scriptRunnerCapability, selectAssignedProfileSlots, serverManagementCapability, setByPath, settingsStoreCapability, sleep, sleepCancellable, smokeCapability, smtpProviderCapability, snapshotCapability, ssoBridgeCapability, startReachabilityPoll, stateVocabularyFor, storageCapability, storageEvictableCapability, storageProviderCapability, streamBrokerCapability, streamCatalogCapability, streamParamsCapability, streamPixels, streamQualityLabel, subKindsOf, summarisePrivacyAudio, supportedRuntimes, switchCapability, switchedOffIds, synthesizeSourceInfo, systemCapability, tamperCapability, taskLogEntrySchema, taskPhaseSchema, taskTargetSchema, temperatureSensorCapability, terminalSessionCapability, textToHtml, toDeviceSummary, toExpressionValue, toNodeId, toStreamSourceEntry, toastCapability, tokenize, transcodeBody, tryConvertUnit, turnProviderCapability, unitDimension, unitsForDimension, updateCapability, userManagementCapability, userPasskeysCapability, vacuumControlCapability, validateExpressionSource, valveCapability, vectorDimFromBase64, vectorStoreCapability, vibrationCapability, videoclipsCapability, viewerUiCapability, waterHeaterCapability, weatherCapability, webrtcClientHintsSchema, webrtcSessionCapability, wiringAddonHealthSchema, wiringHealthSnapshotSchema, wiringNodeHealthSchema, wiringProbeKindSchema, wiringProbeResultSchema, zodEntriesToConfigUI, zoneAnalyticsCapability, zoneRulesCapability, zonesCapability };