@camstack/addon-pipeline 1.1.38 → 1.1.40

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 (40) hide show
  1. package/dist/audio-analyzer/index.js +168 -61
  2. package/dist/audio-analyzer/index.mjs +168 -61
  3. package/dist/detection-pipeline/index.js +2 -2
  4. package/dist/detection-pipeline/index.mjs +2 -2
  5. package/dist/{dist-CdltGhTt.js → dist-BJTPkJFw.js} +283 -82
  6. package/dist/{dist-DdZEBePv.mjs → dist-v6cKLmU3.mjs} +284 -77
  7. package/dist/{frame-handle-plane-zfv8z5a5.js → frame-handle-plane-BP8YV4sF.js} +54 -5
  8. package/dist/{frame-handle-plane-C0Rzay-b.mjs → frame-handle-plane-DKAXTtfn.mjs} +54 -5
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/pipeline-runner/index.js +893 -12
  12. package/dist/pipeline-runner/index.mjs +892 -13
  13. package/dist/recorder/index.js +2 -2
  14. package/dist/recorder/index.mjs +2 -2
  15. package/dist/session-decode/decode-worker-child.js +511 -0
  16. package/dist/session-decode/decode-worker-child.mjs +510 -0
  17. package/dist/stream-broker/_stub.js +44 -44
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CfUpZdH5.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-6I7-nufj.mjs} +3 -3
  19. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DqJvWBKS.mjs +26 -0
  20. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-dGka8yZY.mjs +26 -0
  21. package/dist/stream-broker/{hostInit-YSFfELtG.mjs → hostInit-CUkFGqiI.mjs} +3 -3
  22. package/dist/stream-broker/index.js +268 -38
  23. package/dist/stream-broker/index.mjs +268 -38
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/dist/worker-protocol-BCfO8gUF.js +67 -0
  26. package/dist/worker-protocol-pk7qdYXt.mjs +56 -0
  27. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-D3FtMsgt.js → MaskShapeCanvas-DI4BY7W2-Cr_fCzY5.js} +1 -1
  28. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DidcQOd2.js → MotionZonesSettings-NcxxQN8r-COcvoHDH.js} +1 -1
  29. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DyzMlv2v.js → PrivacyMaskSettings-APgPLF7p-Bv-bbyJl.js} +1 -1
  30. package/embed-dist/assets/index-Ctbq9AXh.css +2 -0
  31. package/embed-dist/assets/index-jMj5PyJk.js +81 -0
  32. package/embed-dist/index.html +2 -2
  33. package/package.json +6 -28
  34. package/python/inference_pool.py +3 -1
  35. package/dist/audio-codec-ffmpeg/index.js +0 -1238
  36. package/dist/audio-codec-ffmpeg/index.mjs +0 -1220
  37. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DhY3MZ2C.mjs +0 -26
  38. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BsrrdOBU.mjs +0 -26
  39. package/embed-dist/assets/index-C5UpuPr8.css +0 -2
  40. package/embed-dist/assets/index-Cr7799kl.js +0 -81
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-BiDFW0E7.mjs
4630
+ //#region ../types/dist/sleep-CZDdRBua.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -7418,7 +7418,16 @@ var DecoderStatsSchema = object({
7418
7418
  inputFps: number(),
7419
7419
  outputFps: number(),
7420
7420
  avgDecodeTimeMs: number(),
7421
- droppedFrames: number()
7421
+ droppedFrames: number(),
7422
+ /**
7423
+ * Pull-mode adaptive-fps telemetry (optional — only pull sessions run the
7424
+ * lag-driven controller; push sessions omit these). `lagMs` is the EWMA of
7425
+ * the decoder's real-time drift (rising = falling behind live); `adaptiveFps`
7426
+ * is the current lag-throttled emit rate (≤ `effectiveFps` ceiling).
7427
+ */
7428
+ lagMs: number().optional(),
7429
+ effectiveFps: number().optional(),
7430
+ adaptiveFps: number().optional()
7422
7431
  });
7423
7432
  var DecoderSessionConfigSchema = object({
7424
7433
  codec: string(),
@@ -7459,7 +7468,15 @@ var DecoderSessionConfigSchema = object({
7459
7468
  * other — `pullFrames` returns nothing for an `'shm'` session and
7460
7469
  * `pullHandles` returns nothing for a `'callback'` session.
7461
7470
  */
7462
- frameSink: _enum(["callback", "shm"]).default("callback")
7471
+ frameSink: _enum(["callback", "shm"]).default("callback"),
7472
+ /**
7473
+ * Per-camera decoder DEBUG facility. When `true`, a pull-mode session emits
7474
+ * a throttled (~1Hz) structured `decoder debug` line (effective/adaptive fps,
7475
+ * real-time lag, dropped-frame delta, avg decode time, hwaccel). Mirrors the
7476
+ * stream-broker's `streamingDebug` gate — off by default so production logs
7477
+ * stay quiet and the emit path pays zero per-frame cost when disabled.
7478
+ */
7479
+ debug: boolean().optional()
7463
7480
  });
7464
7481
  var EncodeProfileSchema = object({
7465
7482
  video: object({
@@ -10331,6 +10348,75 @@ DeviceType.Cover, method(object({ deviceId: number().int().nonnegative() }), _vo
10331
10348
  auth: "admin"
10332
10349
  });
10333
10350
  /**
10351
+ * Vendor-neutral day/night (IR-cut) control — the per-camera config cap
10352
+ * shared by reolink / hikvision / amcrest. Models the common firmware
10353
+ * surface: the IR-cut switching MODE plus the two knobs that gate it
10354
+ * (photocell `sensitivity` + `switchDelaySec`). Each vendor maps these
10355
+ * onto its own ISAPI / Baichuan / Dahua-CGI fields; the cap standardises
10356
+ * the shape so ONE derived-form renders every camera.
10357
+ *
10358
+ * Follows the D14 `deviceConfig` archetype (see `stream-params.cap.ts`):
10359
+ * `getOptions` advertises per-camera availability, `getStatus` (auto-
10360
+ * injected from `status`) reports the live values, and a single
10361
+ * `setSettings` mutation applies a partial change. No hand-written
10362
+ * settings-contribution methods — the framework derives the UI + save
10363
+ * routing from this surface.
10364
+ */
10365
+ /** IR-cut switching mode. `schedule` = time-of-day table configured on the camera. */
10366
+ var DayNightModeSchema = _enum([
10367
+ "auto",
10368
+ "day",
10369
+ "night",
10370
+ "schedule"
10371
+ ]);
10372
+ /** Normalized numeric range descriptor — `{ min, max, step }` per the
10373
+ * getOptions availability convention. Normalized values are 0–100. */
10374
+ var NormalizedRangeSchema$1 = object({
10375
+ min: number(),
10376
+ max: number(),
10377
+ step: number()
10378
+ });
10379
+ object({
10380
+ mode: DayNightModeSchema,
10381
+ /** IR-cut trigger sensitivity, NORMALIZED 0–100 (higher = switches to night sooner). */
10382
+ sensitivity: number().optional(),
10383
+ /** Delay before the IR-cut filter flips, in seconds. */
10384
+ switchDelaySec: number().optional(),
10385
+ lastFetchedAt: number()
10386
+ });
10387
+ /**
10388
+ * Per-camera availability descriptor — drives which controls the admin UI
10389
+ * renders. Booleans as `supportsX`; numeric ranges as `{ min, max, step }`
10390
+ * (normalized 0–100); the mode choice-set as an array. A provider returns
10391
+ * honest, camera-probed values — never hardcoded.
10392
+ */
10393
+ var DayNightOptionsSchema = object({
10394
+ /** Modes this camera accepts. Empty → the camera has no configurable day/night mode. */
10395
+ modes: array(DayNightModeSchema),
10396
+ supportsSensitivity: boolean(),
10397
+ /** Present when `supportsSensitivity` — the normalized 0–100 range. */
10398
+ sensitivity: NormalizedRangeSchema$1.optional(),
10399
+ supportsSwitchDelay: boolean(),
10400
+ /** Present when `supportsSwitchDelay` — the allowed delay range in seconds. */
10401
+ switchDelaySec: NormalizedRangeSchema$1.optional()
10402
+ });
10403
+ /**
10404
+ * Partial change to the day/night config — every field optional. A
10405
+ * provider ignores fields it does not support.
10406
+ */
10407
+ var DayNightSettingsPatchSchema = object({
10408
+ mode: DayNightModeSchema.optional(),
10409
+ sensitivity: number().optional(),
10410
+ switchDelaySec: number().optional()
10411
+ });
10412
+ DeviceType.Camera, method(object({ deviceId: number() }), DayNightOptionsSchema), method(object({
10413
+ deviceId: number(),
10414
+ settings: DayNightSettingsPatchSchema
10415
+ }), _void(), {
10416
+ kind: "mutation",
10417
+ auth: "admin"
10418
+ });
10419
+ /**
10334
10420
  * Identity envelope for a device's upstream-system metadata.
10335
10421
  *
10336
10422
  * Two jobs:
@@ -10686,6 +10772,130 @@ object({
10686
10772
  });
10687
10773
  DeviceType.Image;
10688
10774
  /**
10775
+ * Vendor-neutral image / picture-adjustment cap — the per-camera config
10776
+ * cap shared by reolink / hikvision / amcrest. Models the common ISP
10777
+ * surface: the four picture sliders (brightness / contrast / saturation /
10778
+ * sharpness), orientation (mirror / flip / rotate), white-balance,
10779
+ * exposure and backlight-compensation modes.
10780
+ *
10781
+ * NORMALIZATION: every slider is a normalized int 0–100. Vendors expose
10782
+ * these natively as 0–100 or 0–255 (or other ranges); each provider maps
10783
+ * its native range to/from this normalized 0–100 space so the cap surface
10784
+ * (and the derived form) is identical across cameras. `warmth` (manual
10785
+ * white-balance) is likewise normalized 0–100.
10786
+ *
10787
+ * Follows the D14 `deviceConfig` archetype (see `stream-params.cap.ts`):
10788
+ * `getOptions` advertises per-camera availability, `getStatus` (auto-
10789
+ * injected from `status`) reports the live values, and a single
10790
+ * `setSettings` mutation applies a partial change. No hand-written
10791
+ * settings-contribution methods — the framework derives the UI + save
10792
+ * routing from this surface.
10793
+ */
10794
+ /** Sensor/image rotation, degrees clockwise. */
10795
+ var ImageRotateSchema = _enum([
10796
+ "0",
10797
+ "90",
10798
+ "180",
10799
+ "270"
10800
+ ]);
10801
+ /** White-balance mode. `manual` unlocks the normalized `warmth` knob. */
10802
+ var WhiteBalanceModeSchema = _enum(["auto", "manual"]);
10803
+ /** Exposure mode. */
10804
+ var ExposureModeSchema = _enum(["auto", "manual"]);
10805
+ /**
10806
+ * Backlight-compensation mode:
10807
+ * - `off` — disabled
10808
+ * - `blc` — backlight compensation
10809
+ * - `wdr` — wide dynamic range
10810
+ * - `hlc` — highlight compensation
10811
+ */
10812
+ var BacklightModeSchema = _enum([
10813
+ "off",
10814
+ "blc",
10815
+ "wdr",
10816
+ "hlc"
10817
+ ]);
10818
+ /** Normalized numeric range descriptor — `{ min, max, step }` per the
10819
+ * getOptions availability convention. Slider values are normalized 0–100. */
10820
+ var NormalizedRangeSchema = object({
10821
+ min: number(),
10822
+ max: number(),
10823
+ step: number()
10824
+ });
10825
+ object({
10826
+ /** Normalized 0–100. */
10827
+ brightness: number().optional(),
10828
+ /** Normalized 0–100. */
10829
+ contrast: number().optional(),
10830
+ /** Normalized 0–100. */
10831
+ saturation: number().optional(),
10832
+ /** Normalized 0–100. */
10833
+ sharpness: number().optional(),
10834
+ mirror: boolean().optional(),
10835
+ flip: boolean().optional(),
10836
+ rotate: ImageRotateSchema.optional(),
10837
+ whiteBalance: WhiteBalanceModeSchema.optional(),
10838
+ /** Manual white-balance warmth, NORMALIZED 0–100. Meaningful only when `whiteBalance === 'manual'`. */
10839
+ warmth: number().optional(),
10840
+ exposureMode: ExposureModeSchema.optional(),
10841
+ backlightMode: BacklightModeSchema.optional(),
10842
+ lastFetchedAt: number()
10843
+ });
10844
+ /**
10845
+ * Per-camera availability descriptor — drives which controls the admin UI
10846
+ * renders. Booleans as `supportsX`; numeric ranges as `{ min, max, step }`
10847
+ * (the normalized 0–100 range); enums as arrays of supported values (empty
10848
+ * array → control hidden). A provider returns honest, camera-probed values
10849
+ * — never hardcoded.
10850
+ */
10851
+ var ImageSettingsOptionsSchema = object({
10852
+ supportsBrightness: boolean(),
10853
+ brightness: NormalizedRangeSchema.optional(),
10854
+ supportsContrast: boolean(),
10855
+ contrast: NormalizedRangeSchema.optional(),
10856
+ supportsSaturation: boolean(),
10857
+ saturation: NormalizedRangeSchema.optional(),
10858
+ supportsSharpness: boolean(),
10859
+ sharpness: NormalizedRangeSchema.optional(),
10860
+ supportsMirror: boolean(),
10861
+ supportsFlip: boolean(),
10862
+ /** Supported rotation values. Empty → rotation not configurable. */
10863
+ rotateOptions: array(ImageRotateSchema),
10864
+ /** Supported white-balance modes. Empty → white-balance not configurable. */
10865
+ whiteBalanceModes: array(WhiteBalanceModeSchema),
10866
+ supportsWarmth: boolean(),
10867
+ /** Present when `supportsWarmth` — the normalized 0–100 range. */
10868
+ warmth: NormalizedRangeSchema.optional(),
10869
+ /** Supported exposure modes. Empty → exposure not configurable. */
10870
+ exposureModes: array(ExposureModeSchema),
10871
+ /** Supported backlight modes. Empty → backlight-compensation not configurable. */
10872
+ backlightModes: array(BacklightModeSchema)
10873
+ });
10874
+ /**
10875
+ * Partial change to the image config — every field optional. Slider values
10876
+ * are normalized 0–100. A provider ignores fields it does not support.
10877
+ */
10878
+ var ImageSettingsPatchSchema = object({
10879
+ brightness: number().optional(),
10880
+ contrast: number().optional(),
10881
+ saturation: number().optional(),
10882
+ sharpness: number().optional(),
10883
+ mirror: boolean().optional(),
10884
+ flip: boolean().optional(),
10885
+ rotate: ImageRotateSchema.optional(),
10886
+ whiteBalance: WhiteBalanceModeSchema.optional(),
10887
+ warmth: number().optional(),
10888
+ exposureMode: ExposureModeSchema.optional(),
10889
+ backlightMode: BacklightModeSchema.optional()
10890
+ });
10891
+ DeviceType.Camera, method(object({ deviceId: number() }), ImageSettingsOptionsSchema), method(object({
10892
+ deviceId: number(),
10893
+ settings: ImageSettingsPatchSchema
10894
+ }), _void(), {
10895
+ kind: "mutation",
10896
+ auth: "admin"
10897
+ });
10898
+ /**
10689
10899
  * Robotic lawn-mower cap. Models HA `lawn_mower.*` entities — anything
10690
10900
  * with a mowing lifecycle plus a dock action.
10691
10901
  *
@@ -11728,6 +11938,16 @@ var RunnerCameraConfigSchema = object({
11728
11938
  * this gate is bypassed.
11729
11939
  */
11730
11940
  onboardMotionDrivesAnalyzer: boolean().default(true),
11941
+ /**
11942
+ * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
11943
+ * never arms the periodic recheck timer, regardless of `occupancyRecheckSec` —
11944
+ * this is off by default because the recheck re-subscribes a detection session
11945
+ * every N seconds while `watching`, a major source of pull-decoder re-dial
11946
+ * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
11947
+ * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
11948
+ * (and only render) when this is enabled.
11949
+ */
11950
+ occupancyRecheckEnabled: boolean().default(false),
11731
11951
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
11732
11952
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
11733
11953
  /**
@@ -13781,79 +14001,42 @@ var SessionInventoryEntrySchema = object({
13781
14001
  framesIn: number(),
13782
14002
  framesOut: number()
13783
14003
  });
13784
- /**
13785
- * audio-codec — bidirectional PCM ↔ encoded audio I/O box.
13786
- *
13787
- * Independent per-consumer sessions. The provider runs decode + resample
13788
- * (or resample + encode) inside the session so a 16kHz mono ASA
13789
- * subscriber and a 48kHz stereo WebRTC subscriber on the same source
13790
- * stream don't share resamplers.
13791
- *
13792
- * Singleton on each node. Decoder and encoder live in the same provider
13793
- * because they share the underlying libav contexts (node-av today,
13794
- * pluggable later) — operators always install one or the other together.
13795
- */
13796
- var audioCodecCapability = {
13797
- name: "audio-codec",
13798
- scope: "system",
13799
- mode: "singleton",
13800
- preferredProvider: "audio-codec-ffmpeg",
13801
- methods: {
13802
- /** Probe the local runtime and return the supported codec matrix. */
13803
- listSupportedCodecs: method(_void(), array(AudioCodecInfoSchema).readonly()),
13804
- /** Cheap predicate — does the runtime support `(codec, kind)`? */
13805
- canHandle: method(object({
13806
- codec: string(),
13807
- kind: _enum(["decode", "encode"])
13808
- }), boolean()),
13809
- createDecodeSession: method(AudioDecodeSessionConfigSchema, object({
13810
- sessionId: string(),
13811
- nodeId: string()
13812
- }), { kind: "mutation" }),
13813
- createEncodeSession: method(AudioEncodeSessionConfigSchema, object({
13814
- sessionId: string(),
13815
- nodeId: string()
13816
- }), { kind: "mutation" }),
13817
- closeSession: method(object({
13818
- sessionId: string(),
13819
- nodeId: string().optional()
13820
- }), _void(), { kind: "mutation" }),
13821
- /** Push one encoded audio frame into a decode session. */
13822
- pushEncodedFrame: method(object({
13823
- sessionId: string(),
13824
- nodeId: string().optional(),
13825
- data: _instanceof(Uint8Array),
13826
- /** Source PTS in milliseconds. Synthesised when omitted. */
13827
- pts: number().optional()
13828
- }), _void(), { kind: "mutation" }),
13829
- /** Pull up to `maxCount` PCM chunks from a decode session. */
13830
- pullPcm: method(object({
13831
- sessionId: string(),
13832
- nodeId: string().optional(),
13833
- maxCount: number().int().positive().default(8)
13834
- }), array(AudioPcmChunkSchema)),
13835
- /** Push one PCM chunk into an encode session. */
13836
- pushPcm: method(object({
13837
- sessionId: string(),
13838
- nodeId: string().optional(),
13839
- data: _instanceof(Uint8Array),
13840
- /** Source PTS in milliseconds. */
13841
- pts: number().optional()
13842
- }), _void(), { kind: "mutation" }),
13843
- /** Pull up to `maxCount` encoded chunks from an encode session. */
13844
- pullEncoded: method(object({
13845
- sessionId: string(),
13846
- nodeId: string().optional(),
13847
- maxCount: number().int().positive().default(8)
13848
- }), array(AudioEncodedChunkSchema)),
13849
- /** Flush any pending encoded output (call before close on graceful tear). */
13850
- flushEncode: method(object({
13851
- sessionId: string(),
13852
- nodeId: string().optional()
13853
- }), array(AudioEncodedChunkSchema), { kind: "mutation" }),
13854
- listActiveSessions: method(_void(), array(SessionInventoryEntrySchema).readonly())
13855
- }
13856
- };
14004
+ method(_void(), array(AudioCodecInfoSchema).readonly()), method(object({
14005
+ codec: string(),
14006
+ kind: _enum(["decode", "encode"])
14007
+ }), boolean()), method(AudioDecodeSessionConfigSchema, object({
14008
+ sessionId: string(),
14009
+ nodeId: string()
14010
+ }), { kind: "mutation" }), method(AudioEncodeSessionConfigSchema, object({
14011
+ sessionId: string(),
14012
+ nodeId: string()
14013
+ }), { kind: "mutation" }), method(object({
14014
+ sessionId: string(),
14015
+ nodeId: string().optional()
14016
+ }), _void(), { kind: "mutation" }), method(object({
14017
+ sessionId: string(),
14018
+ nodeId: string().optional(),
14019
+ data: _instanceof(Uint8Array),
14020
+ /** Source PTS in milliseconds. Synthesised when omitted. */
14021
+ pts: number().optional()
14022
+ }), _void(), { kind: "mutation" }), method(object({
14023
+ sessionId: string(),
14024
+ nodeId: string().optional(),
14025
+ maxCount: number().int().positive().default(8)
14026
+ }), array(AudioPcmChunkSchema)), method(object({
14027
+ sessionId: string(),
14028
+ nodeId: string().optional(),
14029
+ data: _instanceof(Uint8Array),
14030
+ /** Source PTS in milliseconds. */
14031
+ pts: number().optional()
14032
+ }), _void(), { kind: "mutation" }), method(object({
14033
+ sessionId: string(),
14034
+ nodeId: string().optional(),
14035
+ maxCount: number().int().positive().default(8)
14036
+ }), array(AudioEncodedChunkSchema)), method(object({
14037
+ sessionId: string(),
14038
+ nodeId: string().optional()
14039
+ }), array(AudioEncodedChunkSchema), { kind: "mutation" }), method(_void(), array(SessionInventoryEntrySchema).readonly());
13857
14040
  var AuthResultSchema = object({
13858
14041
  userId: string(),
13859
14042
  username: string(),
@@ -20443,6 +20626,18 @@ Object.freeze({
20443
20626
  addonId: null,
20444
20627
  access: "view"
20445
20628
  },
20629
+ "dayNight.getOptions": {
20630
+ capName: "day-night",
20631
+ capScope: "device",
20632
+ addonId: null,
20633
+ access: "view"
20634
+ },
20635
+ "dayNight.setSettings": {
20636
+ capName: "day-night",
20637
+ capScope: "device",
20638
+ addonId: null,
20639
+ access: "create"
20640
+ },
20446
20641
  "decoder.createSession": {
20447
20642
  capName: "decoder",
20448
20643
  capScope: "system",
@@ -21373,6 +21568,18 @@ Object.freeze({
21373
21568
  addonId: null,
21374
21569
  access: "create"
21375
21570
  },
21571
+ "imageSettings.getOptions": {
21572
+ capName: "image-settings",
21573
+ capScope: "device",
21574
+ addonId: null,
21575
+ access: "view"
21576
+ },
21577
+ "imageSettings.setSettings": {
21578
+ capName: "image-settings",
21579
+ capScope: "device",
21580
+ addonId: null,
21581
+ access: "create"
21582
+ },
21376
21583
  "integrations.create": {
21377
21584
  capName: "integrations",
21378
21585
  capScope: "system",
@@ -24241,4 +24448,4 @@ function defaultDeviceFor(id) {
24241
24448
  return def(id).defaultDevice;
24242
24449
  }
24243
24450
  //#endregion
24244
- export { boolean as $, pipelineRunnerCapability as A, DeviceFeature as B, hfModelUrl as C, motionDetectionCapability as D, migrateConfigToBands as E, supportedRuntimes as F, makeProfileBrokerId as G, EventCategory as H, webrtcSessionCapability as I, parseProfileBrokerId as J, makeSourceBrokerId as K, errMsg as L, runtimeDevices as M, storageEvictableCapability as N, nodePin as O, streamBrokerCapability as P, array as Q, BaseAddon as R, evaluateZoneRules as S, maskUrlCredentials as T, createEvent as U, DeviceType as V, hydrateSchema as W, sleep as X, selectAssignedProfileSlots as Y, _enum as Z, cameraStreamsCapability as _, COCO_TO_MACRO as a, record as at, defineCustomActions as b, EncodeProfileSchema as c, RingBuffer as d, discriminatedUnion as et, YAMNET_TO_MACRO as f, audioCodecCapability as g, audioAnalyzerCapability as h, COCO_80_LABELS as i, object as it, recordingCapability as j, pipelineExecutorCapability as k, HF_BASE_URL as l, audioAnalysisCapability as m, AUDIO_BACKEND_CHOICES as n, literal as nt, DEFAULT_AUDIO_ANALYZER_CONFIG as o, string as ot, addonWidgetsSourceCapability as p, parseJsonUnknown as q, AUDIO_MACRO_LABELS as r, number as rt, EVENT_PAD_MS as s, union as st, APPLE_SA_TO_MACRO as t, lazy as tt, RecordingConfigSchema as u, customAction as v, mapAudioLabelToMacro as w, detectionPipelineCapability as x, defaultDeviceFor as y, CAM_PROFILE_ORDER as z };
24451
+ export { discriminatedUnion as $, recordingCapability as A, DeviceType as B, mapAudioLabelToMacro as C, nodePin as D, motionDetectionCapability as E, webrtcSessionCapability as F, makeSourceBrokerId as G, createEvent as H, errMsg as I, selectAssignedProfileSlots as J, parseJsonUnknown as K, BaseAddon as L, storageEvictableCapability as M, streamBrokerCapability as N, pipelineExecutorCapability as O, supportedRuntimes as P, boolean as Q, CAM_PROFILE_ORDER as R, hfModelUrl as S, migrateConfigToBands as T, hydrateSchema as U, EventCategory as V, makeProfileBrokerId as W, _enum as X, sleep as Y, array as Z, customAction as _, COCO_TO_MACRO as a, string as at, detectionPipelineCapability as b, EncodeProfileSchema as c, RingBuffer as d, lazy as et, YAMNET_TO_MACRO as f, cameraStreamsCapability as g, audioAnalyzerCapability as h, COCO_80_LABELS as i, record as it, runtimeDevices as j, pipelineRunnerCapability as k, HF_BASE_URL as l, audioAnalysisCapability as m, AUDIO_BACKEND_CHOICES as n, number as nt, DEFAULT_AUDIO_ANALYZER_CONFIG as o, union as ot, addonWidgetsSourceCapability as p, parseProfileBrokerId as q, AUDIO_MACRO_LABELS as r, object as rt, EVENT_PAD_MS as s, APPLE_SA_TO_MACRO as t, literal as tt, RecordingConfigSchema as u, defaultDeviceFor as v, maskUrlCredentials as w, evaluateZoneRules as x, defineCustomActions as y, DeviceFeature as z };
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CdltGhTt.js");
1
+ const require_dist = require("./dist-BJTPkJFw.js");
2
2
  let node_crypto = require("node:crypto");
3
3
  //#region src/stream-broker/stream-broker/decoder-session-proxy.ts
4
4
  /**
@@ -173,6 +173,27 @@ var DECODE_FPS_WINDOW_MS = 2e3;
173
173
  */
174
174
  var SESSION_LINGER_MS = 45e3;
175
175
  /**
176
+ * Short keep-warm grace for a PURE-OCCUPANCY session (every remaining
177
+ * subscriber is a periodic on-motion occupancy recheck, tag `'occupancy'`).
178
+ *
179
+ * Occupancy rechecks subscribe to decoded frames for ~1s every ~30s. The
180
+ * normal `SESSION_LINGER_MS` (45s) EXCEEDS that ~30s interval, so the linger
181
+ * timer is perpetually cancelled by the next recheck before it can fire — the
182
+ * decoder never tears down and decodes continuously at source-rate to serve a
183
+ * 1s/30s consumer (~30x wasted decode; multiplied across cameras this is the
184
+ * iGPU baseline that saturated the decoder). A pure-occupancy session instead
185
+ * tears down between polls: short enough to fire well before the next ~30s
186
+ * recheck, long enough to absorb the ~1s poll plus minor jitter. Re-dial is
187
+ * cheap on GPU-scale decode and the restream burst re-primes the live edge
188
+ * fast, so on-demand re-arm on the next recheck is fine.
189
+ */
190
+ var OCCUPANCY_LINGER_MS = 5e3;
191
+ /** Subscription tag emitted by the periodic on-motion occupancy recheck. A
192
+ * session whose subscribers have only ever carried this tag gets the short
193
+ * `OCCUPANCY_LINGER_MS` teardown; any other subscriber biases to the long
194
+ * `SESSION_LINGER_MS` warm linger (interactive viewers, detection, motion). */
195
+ var OCCUPANCY_TAG = "occupancy";
196
+ /**
176
197
  * Default retry cooldown after a decoder session lands on a NON-LOCAL node
177
198
  * (see `FrameHandlePlaneOptions.sessionRetryCooldownMs`). Long enough to
178
199
  * turn a per-packet storm into a slow probe; short enough that the plane
@@ -190,6 +211,7 @@ var FrameHandlePlane = class {
190
211
  localNodeId;
191
212
  sessionRetryCooldownMs;
192
213
  getSeedPackets;
214
+ isDebugEnabled;
193
215
  subscriptions = /* @__PURE__ */ new Map();
194
216
  sessions = /* @__PURE__ */ new Map();
195
217
  /** Per-format retry gate set when a session lands on a non-local node:
@@ -219,6 +241,7 @@ var FrameHandlePlane = class {
219
241
  this.localNodeId = options.localNodeId;
220
242
  this.sessionRetryCooldownMs = options.sessionRetryCooldownMs ?? SESSION_RETRY_COOLDOWN_MS;
221
243
  this.getSeedPackets = options.getSeedPackets;
244
+ this.isDebugEnabled = options.isDebugEnabled;
222
245
  }
223
246
  /** Number of active handle subscriptions — surfaced for diagnostics. */
224
247
  get subscriberCount() {
@@ -375,15 +398,33 @@ var FrameHandlePlane = class {
375
398
  */
376
399
  scheduleLinger(session) {
377
400
  this.clearLinger(session);
401
+ const occupancyOnly = !session.hasNonOccupancySubscriberEver;
402
+ const lingerMs = occupancyOnly ? OCCUPANCY_LINGER_MS : SESSION_LINGER_MS;
403
+ this.logger?.debug(occupancyOnly ? "frame-handle plane: short occupancy linger scheduled" : "frame-handle plane: keep-warm linger scheduled", { meta: {
404
+ format: session.format,
405
+ lingerMs,
406
+ occupancyOnly
407
+ } });
378
408
  const timer = setTimeout(() => {
379
409
  session.lingerTimer = null;
380
410
  if (session.subscriberIds.size > 0) return;
381
411
  this.sessions.delete(session.format);
382
412
  this.destroySession(session).catch(() => {});
383
- }, SESSION_LINGER_MS);
413
+ }, lingerMs);
384
414
  timer.unref?.();
385
415
  session.lingerTimer = timer;
386
416
  }
417
+ /**
418
+ * Latch `session.hasNonOccupancySubscriberEver` when the subscription
419
+ * `subscriptionId` carries any tag other than `OCCUPANCY_TAG`. Called at
420
+ * every point a subscription is attached to a session so the keep-warm
421
+ * decision only fast-tears-down sessions that have been PURELY on-demand
422
+ * occupancy rechecks. Unknown ids (mid-teardown) are ignored.
423
+ */
424
+ noteSessionSubscriberTag(session, subscriptionId) {
425
+ const subscription = this.subscriptions.get(subscriptionId);
426
+ if (subscription && subscription.tag !== OCCUPANCY_TAG) session.hasNonOccupancySubscriberEver = true;
427
+ }
387
428
  /** Cancel a session's pending keep-warm teardown timer, if any. */
388
429
  clearLinger(session) {
389
430
  if (session.lingerTimer) {
@@ -563,12 +604,17 @@ var FrameHandlePlane = class {
563
604
  if (existing) {
564
605
  this.clearLinger(existing);
565
606
  existing.subscriberIds.add(subscriptionId);
607
+ this.noteSessionSubscriberTag(existing, subscriptionId);
566
608
  return;
567
609
  }
568
610
  const inflight = this.sessionCreating.get(format);
569
611
  if (inflight) {
570
612
  await inflight;
571
- this.sessions.get(format)?.subscriberIds.add(subscriptionId);
613
+ const session = this.sessions.get(format);
614
+ if (session) {
615
+ session.subscriberIds.add(subscriptionId);
616
+ this.noteSessionSubscriberTag(session, subscriptionId);
617
+ }
572
618
  return;
573
619
  }
574
620
  if (Date.now() < (this.sessionRetryCooldownUntil.get(format) ?? 0)) return;
@@ -583,8 +629,10 @@ var FrameHandlePlane = class {
583
629
  bootstrapped: false,
584
630
  pullMode: false,
585
631
  targetFps: this.computeFormatFps(format),
586
- lingerTimer: null
632
+ lingerTimer: null,
633
+ hasNonOccupancySubscriberEver: false
587
634
  };
635
+ for (const id of subscriberIds) this.noteSessionSubscriberTag(session, id);
588
636
  let started = false;
589
637
  try {
590
638
  started = await this.startSessionDecoder(session);
@@ -631,7 +679,8 @@ var FrameHandlePlane = class {
631
679
  scale: 1,
632
680
  frameSink: "shm",
633
681
  ...info.numericDeviceId !== void 0 ? { deviceId: info.numericDeviceId } : {},
634
- tag: `${info.tag}:shm:${format}`
682
+ tag: `${info.tag}:shm:${format}`,
683
+ ...this.isDebugEnabled?.() ? { debug: true } : {}
635
684
  });
636
685
  if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
637
686
  this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);
@@ -1,4 +1,4 @@
1
- import { L as errMsg, d as RingBuffer } from "./dist-DdZEBePv.mjs";
1
+ import { I as errMsg, d as RingBuffer } from "./dist-v6cKLmU3.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  //#region src/stream-broker/stream-broker/decoder-session-proxy.ts
4
4
  /**
@@ -173,6 +173,27 @@ var DECODE_FPS_WINDOW_MS = 2e3;
173
173
  */
174
174
  var SESSION_LINGER_MS = 45e3;
175
175
  /**
176
+ * Short keep-warm grace for a PURE-OCCUPANCY session (every remaining
177
+ * subscriber is a periodic on-motion occupancy recheck, tag `'occupancy'`).
178
+ *
179
+ * Occupancy rechecks subscribe to decoded frames for ~1s every ~30s. The
180
+ * normal `SESSION_LINGER_MS` (45s) EXCEEDS that ~30s interval, so the linger
181
+ * timer is perpetually cancelled by the next recheck before it can fire — the
182
+ * decoder never tears down and decodes continuously at source-rate to serve a
183
+ * 1s/30s consumer (~30x wasted decode; multiplied across cameras this is the
184
+ * iGPU baseline that saturated the decoder). A pure-occupancy session instead
185
+ * tears down between polls: short enough to fire well before the next ~30s
186
+ * recheck, long enough to absorb the ~1s poll plus minor jitter. Re-dial is
187
+ * cheap on GPU-scale decode and the restream burst re-primes the live edge
188
+ * fast, so on-demand re-arm on the next recheck is fine.
189
+ */
190
+ var OCCUPANCY_LINGER_MS = 5e3;
191
+ /** Subscription tag emitted by the periodic on-motion occupancy recheck. A
192
+ * session whose subscribers have only ever carried this tag gets the short
193
+ * `OCCUPANCY_LINGER_MS` teardown; any other subscriber biases to the long
194
+ * `SESSION_LINGER_MS` warm linger (interactive viewers, detection, motion). */
195
+ var OCCUPANCY_TAG = "occupancy";
196
+ /**
176
197
  * Default retry cooldown after a decoder session lands on a NON-LOCAL node
177
198
  * (see `FrameHandlePlaneOptions.sessionRetryCooldownMs`). Long enough to
178
199
  * turn a per-packet storm into a slow probe; short enough that the plane
@@ -190,6 +211,7 @@ var FrameHandlePlane = class {
190
211
  localNodeId;
191
212
  sessionRetryCooldownMs;
192
213
  getSeedPackets;
214
+ isDebugEnabled;
193
215
  subscriptions = /* @__PURE__ */ new Map();
194
216
  sessions = /* @__PURE__ */ new Map();
195
217
  /** Per-format retry gate set when a session lands on a non-local node:
@@ -219,6 +241,7 @@ var FrameHandlePlane = class {
219
241
  this.localNodeId = options.localNodeId;
220
242
  this.sessionRetryCooldownMs = options.sessionRetryCooldownMs ?? SESSION_RETRY_COOLDOWN_MS;
221
243
  this.getSeedPackets = options.getSeedPackets;
244
+ this.isDebugEnabled = options.isDebugEnabled;
222
245
  }
223
246
  /** Number of active handle subscriptions — surfaced for diagnostics. */
224
247
  get subscriberCount() {
@@ -375,15 +398,33 @@ var FrameHandlePlane = class {
375
398
  */
376
399
  scheduleLinger(session) {
377
400
  this.clearLinger(session);
401
+ const occupancyOnly = !session.hasNonOccupancySubscriberEver;
402
+ const lingerMs = occupancyOnly ? OCCUPANCY_LINGER_MS : SESSION_LINGER_MS;
403
+ this.logger?.debug(occupancyOnly ? "frame-handle plane: short occupancy linger scheduled" : "frame-handle plane: keep-warm linger scheduled", { meta: {
404
+ format: session.format,
405
+ lingerMs,
406
+ occupancyOnly
407
+ } });
378
408
  const timer = setTimeout(() => {
379
409
  session.lingerTimer = null;
380
410
  if (session.subscriberIds.size > 0) return;
381
411
  this.sessions.delete(session.format);
382
412
  this.destroySession(session).catch(() => {});
383
- }, SESSION_LINGER_MS);
413
+ }, lingerMs);
384
414
  timer.unref?.();
385
415
  session.lingerTimer = timer;
386
416
  }
417
+ /**
418
+ * Latch `session.hasNonOccupancySubscriberEver` when the subscription
419
+ * `subscriptionId` carries any tag other than `OCCUPANCY_TAG`. Called at
420
+ * every point a subscription is attached to a session so the keep-warm
421
+ * decision only fast-tears-down sessions that have been PURELY on-demand
422
+ * occupancy rechecks. Unknown ids (mid-teardown) are ignored.
423
+ */
424
+ noteSessionSubscriberTag(session, subscriptionId) {
425
+ const subscription = this.subscriptions.get(subscriptionId);
426
+ if (subscription && subscription.tag !== OCCUPANCY_TAG) session.hasNonOccupancySubscriberEver = true;
427
+ }
387
428
  /** Cancel a session's pending keep-warm teardown timer, if any. */
388
429
  clearLinger(session) {
389
430
  if (session.lingerTimer) {
@@ -563,12 +604,17 @@ var FrameHandlePlane = class {
563
604
  if (existing) {
564
605
  this.clearLinger(existing);
565
606
  existing.subscriberIds.add(subscriptionId);
607
+ this.noteSessionSubscriberTag(existing, subscriptionId);
566
608
  return;
567
609
  }
568
610
  const inflight = this.sessionCreating.get(format);
569
611
  if (inflight) {
570
612
  await inflight;
571
- this.sessions.get(format)?.subscriberIds.add(subscriptionId);
613
+ const session = this.sessions.get(format);
614
+ if (session) {
615
+ session.subscriberIds.add(subscriptionId);
616
+ this.noteSessionSubscriberTag(session, subscriptionId);
617
+ }
572
618
  return;
573
619
  }
574
620
  if (Date.now() < (this.sessionRetryCooldownUntil.get(format) ?? 0)) return;
@@ -583,8 +629,10 @@ var FrameHandlePlane = class {
583
629
  bootstrapped: false,
584
630
  pullMode: false,
585
631
  targetFps: this.computeFormatFps(format),
586
- lingerTimer: null
632
+ lingerTimer: null,
633
+ hasNonOccupancySubscriberEver: false
587
634
  };
635
+ for (const id of subscriberIds) this.noteSessionSubscriberTag(session, id);
588
636
  let started = false;
589
637
  try {
590
638
  started = await this.startSessionDecoder(session);
@@ -631,7 +679,8 @@ var FrameHandlePlane = class {
631
679
  scale: 1,
632
680
  frameSink: "shm",
633
681
  ...info.numericDeviceId !== void 0 ? { deviceId: info.numericDeviceId } : {},
634
- tag: `${info.tag}:shm:${format}`
682
+ tag: `${info.tag}:shm:${format}`,
683
+ ...this.isDebugEnabled?.() ? { debug: true } : {}
635
684
  });
636
685
  if (!isDecoderNodeColocated(this.localNodeId, nodeId)) {
637
686
  this.sessionRetryCooldownUntil.set(format, Date.now() + this.sessionRetryCooldownMs);