@camstack/addon-pipeline 1.2.249 → 1.2.251

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 (28) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-DGkkGNxL.js → default-detection-model-C3QhNmlf.js} +1 -1
  4. package/dist/{default-detection-model-Bm04eyB4.mjs → default-detection-model-CaOyxW54.mjs} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-DG74yV1a.js → dist-G5Uib9Bn.js} +54 -2
  8. package/dist/{dist-CGIdW7g5.mjs → dist-UDv0mi6-.mjs} +54 -2
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-C7MbYv9I.mjs → node-5P6235Qa.mjs} +1 -1
  12. package/dist/{node-hnHqauG8.js → node-ByNljnzV.js} +1 -1
  13. package/dist/pipeline-runner/index.js +3 -3
  14. package/dist/pipeline-runner/index.mjs +3 -3
  15. package/dist/{process-memory-D2e6wNF5.js → process-memory-3HWRtZgr.js} +1 -1
  16. package/dist/{process-memory-3qXmcKQn.mjs → process-memory-DBzJCO-_.mjs} +1 -1
  17. package/dist/recorder/index.js +2 -2
  18. package/dist/recorder/index.mjs +2 -2
  19. package/dist/{segment-demux-js-BW5ndPxS.mjs → segment-demux-js-CUFnkl3n.mjs} +1 -1
  20. package/dist/{segment-demux-js-B5Kg4QOx.js → segment-demux-js-CtQrIvn7.js} +1 -1
  21. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BQlBhj7w.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DpxY0D_G.mjs} +3 -3
  22. package/dist/stream-broker/demux-worker-child.js +1 -1
  23. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  24. package/dist/stream-broker/{hostInit-BqE2ZTEl.mjs → hostInit-B6AhXDBP.mjs} +3 -3
  25. package/dist/stream-broker/index.js +176 -40
  26. package/dist/stream-broker/index.mjs +176 -40
  27. package/dist/stream-broker/remoteEntry.js +1 -1
  28. package/package.json +1 -1
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DG74yV1a.js");
7
- const require_process_memory = require("../process-memory-D2e6wNF5.js");
6
+ const require_dist = require("../dist-G5Uib9Bn.js");
7
+ const require_process_memory = require("../process-memory-3HWRtZgr.js");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
10
10
  let node_path = require("node:path");
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $t as hydrateSchema, Jt as audioChunkBytesPerSample, Lt as errMsg, N as audioAnalysisCapability, Ot as resolvePoolMemoryPolicy, P as audioAnalyzerCapability, Qt as expandAudioChunkToF32le, Wt as BaseAddon, bn as EventCategory, n as AUDIO_BACKEND_CHOICES, ot as mapAudioLabelToMacro, rn as nodePin, s as DEFAULT_AUDIO_ANALYZER_CONFIG, w as PoolMemoryWatchdog, y as HF_BASE_URL } from "../dist-CGIdW7g5.mjs";
3
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-3qXmcKQn.mjs";
2
+ import { $t as hydrateSchema, Jt as audioChunkBytesPerSample, Lt as errMsg, N as audioAnalysisCapability, Ot as resolvePoolMemoryPolicy, P as audioAnalyzerCapability, Qt as expandAudioChunkToF32le, Wt as BaseAddon, bn as EventCategory, n as AUDIO_BACKEND_CHOICES, ot as mapAudioLabelToMacro, rn as nodePin, s as DEFAULT_AUDIO_ANALYZER_CONFIG, w as PoolMemoryWatchdog, y as HF_BASE_URL } from "../dist-UDv0mi6-.mjs";
3
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-DBzJCO-_.mjs";
4
4
  import * as fs from "node:fs";
5
5
  import * as path$1 from "node:path";
6
6
  import { downloadFile } from "@camstack/system/addon-utils";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-DG74yV1a.js");
1
+ const require_dist = require("./dist-G5Uib9Bn.js");
2
2
  let node_crypto = require("node:crypto");
3
3
  //#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { F as audioKindId, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS, tt as hfModelUrl } from "./dist-CGIdW7g5.mjs";
1
+ import { F as audioKindId, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS, tt as hfModelUrl } from "./dist-UDv0mi6-.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  //#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
4
4
  /**
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DG74yV1a.js");
7
- const require_node = require("../node-hnHqauG8.js");
8
- const require_default_detection_model = require("../default-detection-model-DGkkGNxL.js");
6
+ const require_dist = require("../dist-G5Uib9Bn.js");
7
+ const require_node = require("../node-ByNljnzV.js");
8
+ const require_default_detection_model = require("../default-detection-model-C3QhNmlf.js");
9
9
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
10
- const require_process_memory = require("../process-memory-D2e6wNF5.js");
10
+ const require_process_memory = require("../process-memory-3HWRtZgr.js");
11
11
  let node_os = require("node:os");
12
12
  node_os = require_chunk.__toESM(node_os);
13
13
  let node_fs = require("node:fs");
@@ -1,9 +1,9 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $t as hydrateSchema, At as runtimeDevices$1, Ft as supportedRuntimes$1, G as detectionPipelineCapability, J as enumerateInferenceDevices, Lt as errMsg, Ot as resolvePoolMemoryPolicy, Tt as resolveClusterStepModelId, V as defaultDeviceFor$1, Wt as BaseAddon, X as evaluateZoneRules, Yt as createEvent, bn as EventCategory, c as DEFAULT_CLUSTER_STEP_MODELS, gt as pickClusterStepSettings, hn as object, ht as pickClusterStepModels, in as parseJsonUnknown, it as loadContributionCapability, j as YAMNET_TO_MACRO, l as DEFAULT_CLUSTER_STEP_SETTINGS, ln as array, m as DEVICE_BACKEND_TO_FORMAT, nt as inferModelProvider, pt as overlayClusterStepSettings, rn as nodePin, sn as sleep, t as APPLE_SA_TO_MACRO, vn as string, w as PoolMemoryWatchdog, yn as union, yt as pipelineExecutorCapability } from "../dist-CGIdW7g5.mjs";
3
- import { o as readProcessCost } from "../node-C7MbYv9I.mjs";
4
- import { a as localFrameRegistry, c as ALL_STEPS, d as getStepDefinition, f as resolveModelForFormat, l as getDefaultModelForFormat, n as resolveDefaultDetectionModel, p as landmarkPrecisionVerdict, r as macroGateVerdict, s as ALL_PIPELINE_STEPS, u as getStep } from "../default-detection-model-Bm04eyB4.mjs";
2
+ import { $t as hydrateSchema, At as runtimeDevices$1, Ft as supportedRuntimes$1, G as detectionPipelineCapability, J as enumerateInferenceDevices, Lt as errMsg, Ot as resolvePoolMemoryPolicy, Tt as resolveClusterStepModelId, V as defaultDeviceFor$1, Wt as BaseAddon, X as evaluateZoneRules, Yt as createEvent, bn as EventCategory, c as DEFAULT_CLUSTER_STEP_MODELS, gt as pickClusterStepSettings, hn as object, ht as pickClusterStepModels, in as parseJsonUnknown, it as loadContributionCapability, j as YAMNET_TO_MACRO, l as DEFAULT_CLUSTER_STEP_SETTINGS, ln as array, m as DEVICE_BACKEND_TO_FORMAT, nt as inferModelProvider, pt as overlayClusterStepSettings, rn as nodePin, sn as sleep, t as APPLE_SA_TO_MACRO, vn as string, w as PoolMemoryWatchdog, yn as union, yt as pipelineExecutorCapability } from "../dist-UDv0mi6-.mjs";
3
+ import { o as readProcessCost } from "../node-5P6235Qa.mjs";
4
+ import { a as localFrameRegistry, c as ALL_STEPS, d as getStepDefinition, f as resolveModelForFormat, l as getDefaultModelForFormat, n as resolveDefaultDetectionModel, p as landmarkPrecisionVerdict, r as macroGateVerdict, s as ALL_PIPELINE_STEPS, u as getStep } from "../default-detection-model-CaOyxW54.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-3qXmcKQn.mjs";
6
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-DBzJCO-_.mjs";
7
7
  import * as os from "node:os";
8
8
  import * as fs from "node:fs";
9
9
  import * as path$1 from "node:path";
@@ -14413,7 +14413,23 @@ var PickStreamRequirementsSchema = object({
14413
14413
  * This is what lets one picker serve both the bypass and the full source
14414
14414
  * choice, instead of a consumer scoring privately when the bypass misses.
14415
14415
  */
14416
- allowTranscode: boolean().optional()
14416
+ allowTranscode: boolean().optional(),
14417
+ /**
14418
+ * Consider ONLY streams the broker has bound to a profile slot.
14419
+ *
14420
+ * A consumer that dials the broker — which is every exporter, since none
14421
+ * of them may open the camera's own URL — can do nothing with a source
14422
+ * that has no slot: there is no restream to dial. Without this the picker
14423
+ * can name such a stream, and each consumer discovers the dead end for
14424
+ * itself: Alexa's `profileForCamStream` returns `null` and it quietly
14425
+ * falls back, and HomeKit would have had to grow the same function again.
14426
+ *
14427
+ * Default `false`, and deliberately so: the BROKER itself picks through
14428
+ * here when it decides which source to BIND to a slot
14429
+ * (`computeInitialAssignment`), and it must see the unbound ones or it
14430
+ * could never bind them.
14431
+ */
14432
+ requireProfileBound: boolean().optional()
14417
14433
  }).readonly();
14418
14434
  var PickStreamPreferencesSchema = object({
14419
14435
  /**
@@ -14454,7 +14470,43 @@ var PickedCamStreamSchema = object({
14454
14470
  */
14455
14471
  transcodes: boolean(),
14456
14472
  /** One-line explanation of why this stream won — for logs / debug UI. */
14457
- reason: string()
14473
+ reason: string(),
14474
+ /**
14475
+ * The broker profile slot this source is bound to, when it is bound.
14476
+ *
14477
+ * Answered HERE because every consumer needs it and none of them can get it
14478
+ * from the `camStreamId` alone. Alexa wrote `profileForCamStream` for it —
14479
+ * a second cap read plus a `null` branch — and HomeKit was about to write
14480
+ * the same function a second time.
14481
+ *
14482
+ * Absent when the source has no slot, which a caller passing
14483
+ * `requireProfileBound` can never see.
14484
+ */
14485
+ profile: CamProfileSchema.optional(),
14486
+ /** The broker id of that slot, for logs that must name what was dialled. */
14487
+ brokerId: string().optional(),
14488
+ /**
14489
+ * The broker restream URL to dial for this stream.
14490
+ *
14491
+ * An exporter must never open the camera's own URL — the broker is the one
14492
+ * that dials the camera, and every consumer goes through its restream. So
14493
+ * the picker answers with the thing a consumer may actually use, instead of
14494
+ * naming a source and leaving each caller to look the URL up.
14495
+ */
14496
+ url: string().optional(),
14497
+ /**
14498
+ * Frames per second this stream really delivers, and where the number came
14499
+ * from.
14500
+ *
14501
+ * It is here because getting it wrong is expensive and silent: a HomeKit
14502
+ * accessory advertised 720p at 24 fps while the slot serving it ran at 9,
14503
+ * and the controller rendered nothing at all. The source is carried with the
14504
+ * value for the same reason the bitrate evidence is: `published` is what the
14505
+ * publisher declares and `measured` is what the broker observed, and they
14506
+ * are not interchangeable.
14507
+ */
14508
+ fps: number().positive().optional(),
14509
+ fpsSource: _enum(["published", "measured"]).optional()
14458
14510
  });
14459
14511
  /**
14460
14512
  * Camera streams — device-scoped facade over the system `stream-broker`.
@@ -14413,7 +14413,23 @@ var PickStreamRequirementsSchema = object({
14413
14413
  * This is what lets one picker serve both the bypass and the full source
14414
14414
  * choice, instead of a consumer scoring privately when the bypass misses.
14415
14415
  */
14416
- allowTranscode: boolean().optional()
14416
+ allowTranscode: boolean().optional(),
14417
+ /**
14418
+ * Consider ONLY streams the broker has bound to a profile slot.
14419
+ *
14420
+ * A consumer that dials the broker — which is every exporter, since none
14421
+ * of them may open the camera's own URL — can do nothing with a source
14422
+ * that has no slot: there is no restream to dial. Without this the picker
14423
+ * can name such a stream, and each consumer discovers the dead end for
14424
+ * itself: Alexa's `profileForCamStream` returns `null` and it quietly
14425
+ * falls back, and HomeKit would have had to grow the same function again.
14426
+ *
14427
+ * Default `false`, and deliberately so: the BROKER itself picks through
14428
+ * here when it decides which source to BIND to a slot
14429
+ * (`computeInitialAssignment`), and it must see the unbound ones or it
14430
+ * could never bind them.
14431
+ */
14432
+ requireProfileBound: boolean().optional()
14417
14433
  }).readonly();
14418
14434
  var PickStreamPreferencesSchema = object({
14419
14435
  /**
@@ -14454,7 +14470,43 @@ var PickedCamStreamSchema = object({
14454
14470
  */
14455
14471
  transcodes: boolean(),
14456
14472
  /** One-line explanation of why this stream won — for logs / debug UI. */
14457
- reason: string()
14473
+ reason: string(),
14474
+ /**
14475
+ * The broker profile slot this source is bound to, when it is bound.
14476
+ *
14477
+ * Answered HERE because every consumer needs it and none of them can get it
14478
+ * from the `camStreamId` alone. Alexa wrote `profileForCamStream` for it —
14479
+ * a second cap read plus a `null` branch — and HomeKit was about to write
14480
+ * the same function a second time.
14481
+ *
14482
+ * Absent when the source has no slot, which a caller passing
14483
+ * `requireProfileBound` can never see.
14484
+ */
14485
+ profile: CamProfileSchema.optional(),
14486
+ /** The broker id of that slot, for logs that must name what was dialled. */
14487
+ brokerId: string().optional(),
14488
+ /**
14489
+ * The broker restream URL to dial for this stream.
14490
+ *
14491
+ * An exporter must never open the camera's own URL — the broker is the one
14492
+ * that dials the camera, and every consumer goes through its restream. So
14493
+ * the picker answers with the thing a consumer may actually use, instead of
14494
+ * naming a source and leaving each caller to look the URL up.
14495
+ */
14496
+ url: string().optional(),
14497
+ /**
14498
+ * Frames per second this stream really delivers, and where the number came
14499
+ * from.
14500
+ *
14501
+ * It is here because getting it wrong is expensive and silent: a HomeKit
14502
+ * accessory advertised 720p at 24 fps while the slot serving it ran at 9,
14503
+ * and the controller rendered nothing at all. The source is carried with the
14504
+ * value for the same reason the bitrate evidence is: `published` is what the
14505
+ * publisher declares and `measured` is what the broker observed, and they
14506
+ * are not interchangeable.
14507
+ */
14508
+ fps: number().positive().optional(),
14509
+ fpsSource: _enum(["published", "measured"]).optional()
14458
14510
  });
14459
14511
  /**
14460
14512
  * Camera streams — device-scoped facade over the system `stream-broker`.
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-DG74yV1a.js");
5
+ const require_dist = require("../dist-G5Uib9Bn.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { $t as hydrateSchema, Wt as BaseAddon, X as evaluateZoneRules, ft as motionDetectionCapability, qt as DeviceType } from "../dist-CGIdW7g5.mjs";
1
+ import { $t as hydrateSchema, Wt as BaseAddon, X as evaluateZoneRules, ft as motionDetectionCapability, qt as DeviceType } from "../dist-UDv0mi6-.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { Bt as buildFfmpegArgs, Ht as isSoftwareDecode, Lt as errMsg, zt as Fmp4BoxSplitter } from "./dist-CGIdW7g5.mjs";
1
+ import { Bt as buildFfmpegArgs, Ht as isSoftwareDecode, Lt as errMsg, zt as Fmp4BoxSplitter } from "./dist-UDv0mi6-.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-DG74yV1a.js");
2
+ const require_dist = require("./dist-G5Uib9Bn.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DG74yV1a.js");
7
- const require_node = require("../node-hnHqauG8.js");
8
- const require_default_detection_model = require("../default-detection-model-DGkkGNxL.js");
6
+ const require_dist = require("../dist-G5Uib9Bn.js");
7
+ const require_node = require("../node-ByNljnzV.js");
8
+ const require_default_detection_model = require("../default-detection-model-C3QhNmlf.js");
9
9
  const require_worker_protocol = require("../worker-protocol-B2MfQLlu.js");
10
10
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
11
11
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
@@ -1,6 +1,6 @@
1
- import { $ as failureContributionCapability, Lt as errMsg, S as NativeLeaseSettingsSchema, U as deriveDetailCropRect, Wt as BaseAddon, Xt as customAction, Yt as createEvent, Zt as defineCustomActions, _ as FailureCounters, _t as pickDetailCropConvention, b as MOTION_CLOSE_AFTER_MS, bn as EventCategory, bt as pipelineRunnerCapability, cn as _enum, fn as lazy, hn as object, it as loadContributionCapability, ln as array, m as DEVICE_BACKEND_TO_FORMAT, mn as number, p as DEFAULT_NATIVE_LEASE_SETTINGS, pn as literal, rn as nodePin, u as DEFAULT_DETAIL_CROP_CONVENTION, un as boolean, vn as string, vt as pickNativeLeaseOverride, x as NativeLeaseAdmissionSchema } from "../dist-CGIdW7g5.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-C7MbYv9I.mjs";
3
- import { a as localFrameRegistry, d as getStepDefinition, i as rootObjectGate, o as resolveFrameViewGeometry, r as macroGateVerdict } from "../default-detection-model-Bm04eyB4.mjs";
1
+ import { $ as failureContributionCapability, Lt as errMsg, S as NativeLeaseSettingsSchema, U as deriveDetailCropRect, Wt as BaseAddon, Xt as customAction, Yt as createEvent, Zt as defineCustomActions, _ as FailureCounters, _t as pickDetailCropConvention, b as MOTION_CLOSE_AFTER_MS, bn as EventCategory, bt as pipelineRunnerCapability, cn as _enum, fn as lazy, hn as object, it as loadContributionCapability, ln as array, m as DEVICE_BACKEND_TO_FORMAT, mn as number, p as DEFAULT_NATIVE_LEASE_SETTINGS, pn as literal, rn as nodePin, u as DEFAULT_DETAIL_CROP_CONVENTION, un as boolean, vn as string, vt as pickNativeLeaseOverride, x as NativeLeaseAdmissionSchema } from "../dist-UDv0mi6-.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-5P6235Qa.mjs";
3
+ import { a as localFrameRegistry, d as getStepDefinition, i as rootObjectGate, o as resolveFrameViewGeometry, r as macroGateVerdict } from "../default-detection-model-CaOyxW54.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-C_W-P_g-.mjs";
5
5
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
6
6
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-DG74yV1a.js");
2
+ const require_dist = require("./dist-G5Uib9Bn.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { mt as parseProcStatus } from "./dist-CGIdW7g5.mjs";
1
+ import { mt as parseProcStatus } from "./dist-UDv0mi6-.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DG74yV1a.js");
7
- const require_node = require("../node-hnHqauG8.js");
6
+ const require_dist = require("../dist-G5Uib9Bn.js");
7
+ const require_node = require("../node-ByNljnzV.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_restream_intent = require("../restream-intent-C4adO0P8.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
@@ -1,5 +1,5 @@
1
- import { $t as hydrateSchema, C as OpsLogEntrySchema, Ct as recordingExportCapability, D as RECORDING_EXPORT_MAX_READ_BYTES, Dt as resolveLocationMode, H as deriveBatteryPresence, I as batteryCapability, Kt as DeviceFeature, Lt as errMsg, Mt as storageOccupancyCapability, O as RecordingConfigSchema, Pt as stripRetiredBandPreBufferSec, Q as evictionPolicyOfLocation, St as recordingCapability, W as deriveRecordingMode, Wt as BaseAddon, Xt as customAction, Y as evaluateSensorEdge, Z as evictionPolicyForMode, Zt as defineCustomActions, _n as record, bn as EventCategory, cn as _enum, ct as mayReadLocation, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, dt as modeMayWrite, et as gbToBytes, g as ExportRecordSchema, gn as preprocess, hn as object, i as BatteryStatusSchema, it as loadContributionCapability, jt as storageEvictableCapability, k as RecordingSignalStatusSchema, kt as resolveRecordingProfiles, ln as array, lt as mayWriteToLocation, mn as number, on as selectAssignedProfileSlots, qt as DeviceType, rn as nodePin, rt as isFirstLevelMacroClass, sn as sleep$1, un as boolean, ut as migrateLegacyDeviceSignalConfig, vn as string, wt as recordingSignalCapability } from "../dist-CGIdW7g5.mjs";
2
- import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-C7MbYv9I.mjs";
1
+ import { $t as hydrateSchema, C as OpsLogEntrySchema, Ct as recordingExportCapability, D as RECORDING_EXPORT_MAX_READ_BYTES, Dt as resolveLocationMode, H as deriveBatteryPresence, I as batteryCapability, Kt as DeviceFeature, Lt as errMsg, Mt as storageOccupancyCapability, O as RecordingConfigSchema, Pt as stripRetiredBandPreBufferSec, Q as evictionPolicyOfLocation, St as recordingCapability, W as deriveRecordingMode, Wt as BaseAddon, Xt as customAction, Y as evaluateSensorEdge, Z as evictionPolicyForMode, Zt as defineCustomActions, _n as record, bn as EventCategory, cn as _enum, ct as mayReadLocation, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, dt as modeMayWrite, et as gbToBytes, g as ExportRecordSchema, gn as preprocess, hn as object, i as BatteryStatusSchema, it as loadContributionCapability, jt as storageEvictableCapability, k as RecordingSignalStatusSchema, kt as resolveRecordingProfiles, ln as array, lt as mayWriteToLocation, mn as number, on as selectAssignedProfileSlots, qt as DeviceType, rn as nodePin, rt as isFirstLevelMacroClass, sn as sleep$1, un as boolean, ut as migrateLegacyDeviceSignalConfig, vn as string, wt as recordingSignalCapability } from "../dist-UDv0mi6-.mjs";
2
+ import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-5P6235Qa.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { c as PREBUFFER_MAX_MS, r as RECORDING_PRE_ROLL_REQUEST_MS, s as withRecordingIntent } from "../restream-intent-MeJhGCK9.mjs";
5
5
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
@@ -1,5 +1,5 @@
1
1
  import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
2
- import { Lt as errMsg } from "./dist-CGIdW7g5.mjs";
2
+ import { Lt as errMsg } from "./dist-UDv0mi6-.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  //#region ../../node_modules/mp4box/dist/mp4box.all.js
5
5
  var require_mp4box_all = /* @__PURE__ */ __commonJSMin(((exports) => {
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-DG74yV1a.js");
2
+ const require_dist = require("./dist-G5Uib9Bn.js");
3
3
  let node_child_process = require("node:child_process");
4
4
  //#region ../../node_modules/mp4box/dist/mp4box.all.js
5
5
  var require_mp4box_all = /* @__PURE__ */ require_chunk.__commonJSMin(((exports) => {
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.2.110",
6
+ version: "1.2.111",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_stream_broker_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.204",
21
+ version: "1.2.205",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_stream_broker_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.169",
36
+ version: "1.2.170",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_stream_broker_widgets",
@@ -1,4 +1,4 @@
1
- const require_segment_demux_js = require("../segment-demux-js-B5Kg4QOx.js");
1
+ const require_segment_demux_js = require("../segment-demux-js-CtQrIvn7.js");
2
2
  let node_worker_threads = require("node:worker_threads");
3
3
  //#region src/stream-broker/recorded/demux-worker-child.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as demuxSegmentFullJs } from "../segment-demux-js-BW5ndPxS.mjs";
1
+ import { t as demuxSegmentFullJs } from "../segment-demux-js-CUFnkl3n.mjs";
2
2
  import { parentPort } from "node:worker_threads";
3
3
  //#region src/stream-broker/recorded/demux-worker-child.ts
4
4
  /**
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.204",
39
+ version: "1.2.205",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -45,7 +45,7 @@ async function r() {
45
45
  }
46
46
  },
47
47
  "@camstack/sdk": {
48
- version: "1.2.110",
48
+ version: "1.2.111",
49
49
  scope: "default",
50
50
  shareConfig: {
51
51
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.169",
84
+ version: "1.2.170",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,
@@ -3,12 +3,12 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DG74yV1a.js");
7
- const require_node = require("../node-hnHqauG8.js");
6
+ const require_dist = require("../dist-G5Uib9Bn.js");
7
+ const require_node = require("../node-ByNljnzV.js");
8
8
  const require_restream_intent = require("../restream-intent-C4adO0P8.js");
9
9
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
11
- const require_segment_demux_js = require("../segment-demux-js-B5Kg4QOx.js");
11
+ const require_segment_demux_js = require("../segment-demux-js-CtQrIvn7.js");
12
12
  let node_os = require("node:os");
13
13
  node_os = require_chunk.__toESM(node_os);
14
14
  let node_crypto = require("node:crypto");
@@ -12484,6 +12484,8 @@ var StreamBroker = class StreamBroker {
12484
12484
  * behaviour, unchanged.
12485
12485
  */
12486
12486
  prestreamPump;
12487
+ /** The manager's hook for "real media started" — see `PrestreamProvider`. */
12488
+ onRealMediaStarted;
12487
12489
  /**
12488
12490
  * Latched once real media has been seen on this session.
12489
12491
  *
@@ -12760,6 +12762,31 @@ var StreamBroker = class StreamBroker {
12760
12762
  /** True while the stream is suspended (ffmpeg killed, no RTSP connection). */
12761
12763
  _suspended = false;
12762
12764
  /**
12765
+ * The LAZY url this broker was started from, remembered for as long as it
12766
+ * lives.
12767
+ *
12768
+ * It exists because the published url and the broker's state disagree for
12769
+ * about sixteen seconds after a suspend. The catalog still carries the real
12770
+ * `tcp://…` socket — the publisher only republishes the sentinel when its
12771
+ * sleep poll notices the camera has gone — while the broker has already torn
12772
+ * that socket down. In that window `resolveCurrentSource()` truthfully
12773
+ * answers with a socket nobody can dial.
12774
+ *
12775
+ * That cost device 640 its prestream on 2026-09-16: the window re-opened
12776
+ * (which `suspend()` does deliberately, for a consumer that never left) with
12777
+ * the real url, `planPrestream` refused it `not-lazy`, and the refusal is
12778
+ * terminal — the operator's client sat dark for ten seconds and gave up six
12779
+ * before the sentinel came back.
12780
+ *
12781
+ * NOT cleared when `start()` is later called with a materialized url: that
12782
+ * happens on every wake, and clearing it there is the bug again. A broker
12783
+ * genuinely reconfigured onto a permanent mains source keeps a stale value,
12784
+ * which costs nothing — `prestreamEligible` (battery + declared signals) is
12785
+ * the authority on whether a camera gets a prestream at all, and it is asked
12786
+ * first, in the manager.
12787
+ */
12788
+ lazySourceUrl;
12789
+ /**
12763
12790
  * Optional demand-transition observer. Fires `true` the instant demand first
12764
12791
  * appears (a consumer attaches) and `false` when the broker actually suspends
12765
12792
  * after the idle grace — aligned with the broker's own reader spawn/teardown
@@ -13031,6 +13058,7 @@ var StreamBroker = class StreamBroker {
13031
13058
  * sees black, which is the outcome every refusal falls back to.
13032
13059
  */
13033
13060
  setPrestreamProvider(provider) {
13061
+ this.onRealMediaStarted = provider.onRealMediaStarted;
13034
13062
  this.prestreamPump = new PrestreamPump({
13035
13063
  prepare: provider.prepare,
13036
13064
  emit: (frame) => {
@@ -13140,13 +13168,26 @@ var StreamBroker = class StreamBroker {
13140
13168
  const fresh = this.sourceProvider();
13141
13169
  if (fresh) {
13142
13170
  this.source = fresh;
13171
+ this.noteLazyUrl(fresh.url);
13143
13172
  return fresh;
13144
13173
  }
13145
13174
  }
13146
13175
  return this.source ?? null;
13147
13176
  }
13177
+ /**
13178
+ * Remember a lazy url ANY time one is seen — see {@link lazySourceUrl}.
13179
+ *
13180
+ * Here and not only in `start()` because a broker that has never dialled is a
13181
+ * real and common state: a battery camera nobody has asked for yet, with a
13182
+ * prestream consumer attached, has a recipe and no socket. Learning it from
13183
+ * the dial alone would leave exactly that broker unable to describe itself.
13184
+ */
13185
+ noteLazyUrl(url) {
13186
+ if (typeof url === "string" && url.startsWith("lazy:rfc4571:")) this.lazySourceUrl = url;
13187
+ }
13148
13188
  async start(source) {
13149
13189
  this.source = source;
13190
+ this.noteLazyUrl(source.url);
13150
13191
  this.manualStop = false;
13151
13192
  this.stopping = false;
13152
13193
  if (source.videoCodec) this.detectedCodec = source.videoCodec;
@@ -13660,7 +13701,7 @@ var StreamBroker = class StreamBroker {
13660
13701
  reconcilePrestreamWindow() {
13661
13702
  if (!this.prestreamPump) return;
13662
13703
  if (this.prestreamWanted && !this.prestreamWindowClosed) {
13663
- const url = this.resolveCurrentSource()?.url ?? this.source?.url;
13704
+ const url = this.lazySourceUrl ?? this.resolveCurrentSource()?.url ?? this.source?.url;
13664
13705
  if (url !== void 0) this.prestreamPump.start(url);
13665
13706
  return;
13666
13707
  }
@@ -13787,6 +13828,14 @@ var StreamBroker = class StreamBroker {
13787
13828
  if (packet.isPlaceholder !== true && !this.prestreamWindowClosed) {
13788
13829
  this.prestreamWindowClosed = true;
13789
13830
  this.prestreamPump?.stop("first real packet");
13831
+ try {
13832
+ this.onRealMediaStarted?.();
13833
+ } catch (err) {
13834
+ this.logger?.debug("prestream: real-media hook threw", {
13835
+ tags: { deviceId: this.numericDeviceId ?? 0 },
13836
+ meta: { error: String(err) }
13837
+ });
13838
+ }
13790
13839
  }
13791
13840
  if (this.source?.type === "push") {
13792
13841
  this.stopErrorPlaceholder();
@@ -17382,44 +17431,89 @@ var StreamBrokerManager = class StreamBrokerManager {
17382
17431
  * synthetic-frame path is shaped around.
17383
17432
  */
17384
17433
  buildPrestreamProvider(deviceId, camStreamId, brokerId, logger) {
17385
- return { prepare: async ({ sourceUrl }) => {
17386
- const gate = prestreamEligible({
17387
- batteryOperated: await this.isBatteryOperated(deviceId),
17388
- declaresSignals: (await this.declaredSignalCodes(deviceId)).length > 0
17389
- });
17390
- if (!gate.eligible) {
17391
- logger.info("prestream: not offered for this camera", {
17392
- tags: { deviceId },
17393
- meta: {
17394
- brokerId,
17395
- reason: gate.reason
17396
- }
17434
+ return {
17435
+ prepare: async ({ sourceUrl }) => {
17436
+ const gate = prestreamEligible({
17437
+ batteryOperated: await this.isBatteryOperated(deviceId),
17438
+ declaresSignals: (await this.declaredSignalCodes(deviceId)).length > 0
17397
17439
  });
17398
- return null;
17399
- }
17400
- const stream = this.cameraStreams.get(deviceId)?.get(camStreamId);
17401
- const codec = this.lastProbedByBroker.get(brokerId)?.codec ?? stream?.codec;
17402
- const planned = planPrestream({
17403
- sourceUrl,
17404
- width: stream?.resolution?.width,
17405
- height: stream?.resolution?.height,
17406
- codec
17407
- });
17408
- if ("refusal" in planned) {
17409
- logger.info("prestream: no fill for this camera", {
17410
- tags: { deviceId },
17411
- meta: {
17412
- refusal: planned.refusal,
17413
- camStreamId,
17414
- width: stream?.resolution?.width ?? null,
17415
- height: stream?.resolution?.height ?? null,
17416
- codec: codec ?? null
17417
- }
17440
+ if (!gate.eligible) {
17441
+ logger.info("prestream: not offered for this camera", {
17442
+ tags: { deviceId },
17443
+ meta: {
17444
+ brokerId,
17445
+ reason: gate.reason
17446
+ }
17447
+ });
17448
+ return null;
17449
+ }
17450
+ const stream = this.cameraStreams.get(deviceId)?.get(camStreamId);
17451
+ const codec = this.lastProbedByBroker.get(brokerId)?.codec ?? stream?.codec;
17452
+ const planned = planPrestream({
17453
+ sourceUrl,
17454
+ width: stream?.resolution?.width,
17455
+ height: stream?.resolution?.height,
17456
+ codec
17457
+ });
17458
+ if ("refusal" in planned) {
17459
+ logger.info("prestream: no fill for this camera", {
17460
+ tags: { deviceId },
17461
+ meta: {
17462
+ refusal: planned.refusal,
17463
+ camStreamId,
17464
+ width: stream?.resolution?.width ?? null,
17465
+ height: stream?.resolution?.height ?? null,
17466
+ codec: codec ?? null
17467
+ }
17468
+ });
17469
+ return null;
17470
+ }
17471
+ return await this.preparePrestreamFrame(deviceId, brokerId, planned.plan, logger);
17472
+ },
17473
+ /**
17474
+ * REFRESH THE PICTURE WHILE THE CAMERA IS UP.
17475
+ *
17476
+ * The window shows the camera's last snapshot, and nothing was
17477
+ * refreshing it: a battery camera refuses a capture while it believes
17478
+ * itself asleep, and that belief lags the wake by seconds — so the only
17479
+ * attempts ever made were the prestream's own, while composing, which is
17480
+ * BEFORE any media exists. Measured on device 640 (2026-09-16): four
17481
+ * wakes in forty minutes, the encoded picture `bytes=23912` every time,
17482
+ * and between first packet and suspend not one capture was even
17483
+ * attempted. The operator read the result as a placeholder rather than
17484
+ * as their own room, which is exactly what it had become.
17485
+ *
17486
+ * Here the camera is provably awake and its media is flowing, so a
17487
+ * capture costs it nothing it is not already paying — and what lands is
17488
+ * a scene from the event that just happened.
17489
+ *
17490
+ * NEVER `force`: it is an operator signal, and the rule holds even where
17491
+ * it would be harmless. If the native path still refuses, the addon's
17492
+ * own stream-broker fallback pulls a frame from the restream that is, by
17493
+ * construction, running right now.
17494
+ *
17495
+ * Fire and forget, and a failure is DEBUG: the window already has a
17496
+ * picture to show, and this only decides how recent the NEXT one is.
17497
+ */
17498
+ onRealMediaStarted: () => {
17499
+ const api = this.api;
17500
+ if (!api) return;
17501
+ api.snapshot.getSnapshot.query({ deviceId }).then(() => {
17502
+ logger.debug("prestream: picture refreshed from the live window", {
17503
+ tags: { deviceId },
17504
+ meta: { brokerId }
17505
+ });
17506
+ }).catch((err) => {
17507
+ logger.debug("prestream: live-window refresh failed — keeping the older picture", {
17508
+ tags: { deviceId },
17509
+ meta: {
17510
+ brokerId,
17511
+ error: require_dist.errMsg(err)
17512
+ }
17513
+ });
17418
17514
  });
17419
- return null;
17420
17515
  }
17421
- return await this.preparePrestreamFrame(deviceId, brokerId, planned.plan, logger);
17422
- } };
17516
+ };
17423
17517
  }
17424
17518
  /**
17425
17519
  * Fetch the picture, compose the treatment, encode ONCE and cache the bytes.
@@ -31444,6 +31538,20 @@ var CameraStreamsProvider = class {
31444
31538
  return null;
31445
31539
  }
31446
31540
  }
31541
+ if (requirements.requireProfileBound === true) {
31542
+ const boundIds = new Set(this.manager.getProfileSlotsForDevice(input.deviceId).map((s) => s.sourceCamStreamId).filter((id) => id !== null));
31543
+ for (let i = candidates.length - 1; i >= 0; i -= 1) if (!boundIds.has(candidates[i].camStreamId)) candidates.splice(i, 1);
31544
+ if (candidates.length === 0) {
31545
+ this.manager.getLogger?.()?.info("camera-streams: pickStream — no candidate is bound to a profile", {
31546
+ tags: { deviceId: input.deviceId },
31547
+ meta: {
31548
+ deviceId: input.deviceId,
31549
+ bound: [...boundIds]
31550
+ }
31551
+ });
31552
+ return null;
31553
+ }
31554
+ }
31447
31555
  const preferredProviders = preferences?.preferredProviders ?? PROVIDER_PREFIX_DEFAULTS;
31448
31556
  const resolutionPreference = preferences?.resolutionPreference ?? "highest";
31449
31557
  const providerRank = (camStreamId) => {
@@ -31474,12 +31582,40 @@ var CameraStreamsProvider = class {
31474
31582
  reasonParts.push(ranked.transcodes ? "transcode" : "passthrough");
31475
31583
  if (preferences?.targetHeight !== void 0) reasonParts.push(`target=${preferences.targetHeight}`);
31476
31584
  if (requireSibling.length > 0) reasonParts.push(`bypassed=${requireSibling.join("|")}`);
31585
+ const bound = this.boundSlotFor(input.deviceId, winner.camStreamId);
31586
+ if (bound !== null) reasonParts.push(`profile=${bound.profile}`);
31477
31587
  return {
31478
31588
  camStreamId: winner.camStreamId,
31479
31589
  codec: winner.codec,
31480
31590
  resolution: winner.resolution,
31481
31591
  transcodes: ranked.transcodes,
31482
- reason: reasonParts.join(" ")
31592
+ reason: reasonParts.join(" "),
31593
+ ...bound === null ? {} : {
31594
+ profile: bound.profile,
31595
+ brokerId: bound.brokerId,
31596
+ ...bound.url === void 0 ? {} : { url: bound.url }
31597
+ },
31598
+ ...winner.fps === void 0 ? {} : {
31599
+ fps: winner.fps,
31600
+ fpsSource: "published"
31601
+ }
31602
+ };
31603
+ }
31604
+ /**
31605
+ * The profile slot serving `camStreamId`, with the url to dial it, or null
31606
+ * when the broker has bound no slot to it.
31607
+ *
31608
+ * Both reads are local to the manager — no round trip is added to answer
31609
+ * what every consumer was fetching for itself.
31610
+ */
31611
+ boundSlotFor(deviceId, camStreamId) {
31612
+ const slot = this.manager.getProfileSlotsForDevice(deviceId).find((s) => s.sourceCamStreamId === camStreamId);
31613
+ if (slot === void 0) return null;
31614
+ const entry = this.manager.getProfileRtspEntriesForDevice(deviceId).find((e) => e.profile === slot.profile);
31615
+ return {
31616
+ profile: slot.profile,
31617
+ brokerId: slot.brokerId,
31618
+ ...entry?.url === void 0 ? {} : { url: entry.url }
31483
31619
  };
31484
31620
  }
31485
31621
  };
@@ -1,10 +1,10 @@
1
1
  import { r as __toESM } from "../chunk-DnnnRqeS.mjs";
2
- import { A as RingBuffer, B as declareLogChannel, Bt as buildFfmpegArgs$1, E as RATE_CONTROL_TIGHT, Et as resolveEgressDecodeHwAccel, Gt as CAM_PROFILE_ORDER, H as deriveBatteryPresence, Ht as isSoftwareDecode, I as batteryCapability, It as webrtcSessionCapability, K as egressTranscodeSharingKey, Kt as DeviceFeature, L as cameraStreamsCapability, Lt as errMsg, M as addonWidgetsSourceCapability, Nt as streamBrokerCapability, Qt as expandAudioChunkToF32le, R as createHwAccelCache, Rt as AUDIO_PRESETS, T as RATE_CONTROL_RELAXED, Ut as BOOT_RECOVERY_BACKOFF_MS, Vt as invocationFromEncodeProfile, Wt as BaseAddon, Yt as createEvent, _n as record, an as parseProfileBrokerId, at as logChannelsCapability, bn as EventCategory, cn as _enum, dn as discriminatedUnion, en as isEvent, h as EncodeProfileSchema, hn as object, i as BatteryStatusSchema, it as loadContributionCapability, ln as array, mn as number, nn as makeSourceBrokerId, pn as literal, q as egressTransportFromRequest, qt as DeviceType, rn as nodePin, st as maskUrlCredentials, tn as makeProfileBrokerId, un as boolean, v as G711_SCALE_CORRECTION_DB, vn as string, xt as rankStreams, yn as union, z as createLogChannelsProvider } from "../dist-CGIdW7g5.mjs";
3
- import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-C7MbYv9I.mjs";
2
+ import { A as RingBuffer, B as declareLogChannel, Bt as buildFfmpegArgs$1, E as RATE_CONTROL_TIGHT, Et as resolveEgressDecodeHwAccel, Gt as CAM_PROFILE_ORDER, H as deriveBatteryPresence, Ht as isSoftwareDecode, I as batteryCapability, It as webrtcSessionCapability, K as egressTranscodeSharingKey, Kt as DeviceFeature, L as cameraStreamsCapability, Lt as errMsg, M as addonWidgetsSourceCapability, Nt as streamBrokerCapability, Qt as expandAudioChunkToF32le, R as createHwAccelCache, Rt as AUDIO_PRESETS, T as RATE_CONTROL_RELAXED, Ut as BOOT_RECOVERY_BACKOFF_MS, Vt as invocationFromEncodeProfile, Wt as BaseAddon, Yt as createEvent, _n as record, an as parseProfileBrokerId, at as logChannelsCapability, bn as EventCategory, cn as _enum, dn as discriminatedUnion, en as isEvent, h as EncodeProfileSchema, hn as object, i as BatteryStatusSchema, it as loadContributionCapability, ln as array, mn as number, nn as makeSourceBrokerId, pn as literal, q as egressTransportFromRequest, qt as DeviceType, rn as nodePin, st as maskUrlCredentials, tn as makeProfileBrokerId, un as boolean, v as G711_SCALE_CORRECTION_DB, vn as string, xt as rankStreams, yn as union, z as createLogChannelsProvider } from "../dist-UDv0mi6-.mjs";
3
+ import { i as NO_COST_CLAIM, n as Fmp4FragmentChild, r as Fmp4FragmentPlane, t as ChildCostRegistry } from "../node-5P6235Qa.mjs";
4
4
  import { c as PREBUFFER_MAX_MS, d as resolvePrebuffer, i as parseRestreamPath, l as capPreRollRequestMs, n as NO_DECLARATION, u as prebufferRetentionMs } from "../restream-intent-MeJhGCK9.mjs";
5
5
  import { n as profileForStreamId } from "../remote-restream-BeHi78PZ.mjs";
6
6
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
7
- import { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-BW5ndPxS.mjs";
7
+ import { i as require_mp4box_all, n as demuxSegmentPreferJs, r as runFfmpegOnce, t as demuxSegmentFullJs } from "../segment-demux-js-CUFnkl3n.mjs";
8
8
  import { createRequire } from "node:module";
9
9
  import * as os from "node:os";
10
10
  import { networkInterfaces, tmpdir } from "node:os";
@@ -12480,6 +12480,8 @@ var StreamBroker = class StreamBroker {
12480
12480
  * behaviour, unchanged.
12481
12481
  */
12482
12482
  prestreamPump;
12483
+ /** The manager's hook for "real media started" — see `PrestreamProvider`. */
12484
+ onRealMediaStarted;
12483
12485
  /**
12484
12486
  * Latched once real media has been seen on this session.
12485
12487
  *
@@ -12756,6 +12758,31 @@ var StreamBroker = class StreamBroker {
12756
12758
  /** True while the stream is suspended (ffmpeg killed, no RTSP connection). */
12757
12759
  _suspended = false;
12758
12760
  /**
12761
+ * The LAZY url this broker was started from, remembered for as long as it
12762
+ * lives.
12763
+ *
12764
+ * It exists because the published url and the broker's state disagree for
12765
+ * about sixteen seconds after a suspend. The catalog still carries the real
12766
+ * `tcp://…` socket — the publisher only republishes the sentinel when its
12767
+ * sleep poll notices the camera has gone — while the broker has already torn
12768
+ * that socket down. In that window `resolveCurrentSource()` truthfully
12769
+ * answers with a socket nobody can dial.
12770
+ *
12771
+ * That cost device 640 its prestream on 2026-09-16: the window re-opened
12772
+ * (which `suspend()` does deliberately, for a consumer that never left) with
12773
+ * the real url, `planPrestream` refused it `not-lazy`, and the refusal is
12774
+ * terminal — the operator's client sat dark for ten seconds and gave up six
12775
+ * before the sentinel came back.
12776
+ *
12777
+ * NOT cleared when `start()` is later called with a materialized url: that
12778
+ * happens on every wake, and clearing it there is the bug again. A broker
12779
+ * genuinely reconfigured onto a permanent mains source keeps a stale value,
12780
+ * which costs nothing — `prestreamEligible` (battery + declared signals) is
12781
+ * the authority on whether a camera gets a prestream at all, and it is asked
12782
+ * first, in the manager.
12783
+ */
12784
+ lazySourceUrl;
12785
+ /**
12759
12786
  * Optional demand-transition observer. Fires `true` the instant demand first
12760
12787
  * appears (a consumer attaches) and `false` when the broker actually suspends
12761
12788
  * after the idle grace — aligned with the broker's own reader spawn/teardown
@@ -13027,6 +13054,7 @@ var StreamBroker = class StreamBroker {
13027
13054
  * sees black, which is the outcome every refusal falls back to.
13028
13055
  */
13029
13056
  setPrestreamProvider(provider) {
13057
+ this.onRealMediaStarted = provider.onRealMediaStarted;
13030
13058
  this.prestreamPump = new PrestreamPump({
13031
13059
  prepare: provider.prepare,
13032
13060
  emit: (frame) => {
@@ -13136,13 +13164,26 @@ var StreamBroker = class StreamBroker {
13136
13164
  const fresh = this.sourceProvider();
13137
13165
  if (fresh) {
13138
13166
  this.source = fresh;
13167
+ this.noteLazyUrl(fresh.url);
13139
13168
  return fresh;
13140
13169
  }
13141
13170
  }
13142
13171
  return this.source ?? null;
13143
13172
  }
13173
+ /**
13174
+ * Remember a lazy url ANY time one is seen — see {@link lazySourceUrl}.
13175
+ *
13176
+ * Here and not only in `start()` because a broker that has never dialled is a
13177
+ * real and common state: a battery camera nobody has asked for yet, with a
13178
+ * prestream consumer attached, has a recipe and no socket. Learning it from
13179
+ * the dial alone would leave exactly that broker unable to describe itself.
13180
+ */
13181
+ noteLazyUrl(url) {
13182
+ if (typeof url === "string" && url.startsWith("lazy:rfc4571:")) this.lazySourceUrl = url;
13183
+ }
13144
13184
  async start(source) {
13145
13185
  this.source = source;
13186
+ this.noteLazyUrl(source.url);
13146
13187
  this.manualStop = false;
13147
13188
  this.stopping = false;
13148
13189
  if (source.videoCodec) this.detectedCodec = source.videoCodec;
@@ -13656,7 +13697,7 @@ var StreamBroker = class StreamBroker {
13656
13697
  reconcilePrestreamWindow() {
13657
13698
  if (!this.prestreamPump) return;
13658
13699
  if (this.prestreamWanted && !this.prestreamWindowClosed) {
13659
- const url = this.resolveCurrentSource()?.url ?? this.source?.url;
13700
+ const url = this.lazySourceUrl ?? this.resolveCurrentSource()?.url ?? this.source?.url;
13660
13701
  if (url !== void 0) this.prestreamPump.start(url);
13661
13702
  return;
13662
13703
  }
@@ -13783,6 +13824,14 @@ var StreamBroker = class StreamBroker {
13783
13824
  if (packet.isPlaceholder !== true && !this.prestreamWindowClosed) {
13784
13825
  this.prestreamWindowClosed = true;
13785
13826
  this.prestreamPump?.stop("first real packet");
13827
+ try {
13828
+ this.onRealMediaStarted?.();
13829
+ } catch (err) {
13830
+ this.logger?.debug("prestream: real-media hook threw", {
13831
+ tags: { deviceId: this.numericDeviceId ?? 0 },
13832
+ meta: { error: String(err) }
13833
+ });
13834
+ }
13786
13835
  }
13787
13836
  if (this.source?.type === "push") {
13788
13837
  this.stopErrorPlaceholder();
@@ -17378,44 +17427,89 @@ var StreamBrokerManager = class StreamBrokerManager {
17378
17427
  * synthetic-frame path is shaped around.
17379
17428
  */
17380
17429
  buildPrestreamProvider(deviceId, camStreamId, brokerId, logger) {
17381
- return { prepare: async ({ sourceUrl }) => {
17382
- const gate = prestreamEligible({
17383
- batteryOperated: await this.isBatteryOperated(deviceId),
17384
- declaresSignals: (await this.declaredSignalCodes(deviceId)).length > 0
17385
- });
17386
- if (!gate.eligible) {
17387
- logger.info("prestream: not offered for this camera", {
17388
- tags: { deviceId },
17389
- meta: {
17390
- brokerId,
17391
- reason: gate.reason
17392
- }
17430
+ return {
17431
+ prepare: async ({ sourceUrl }) => {
17432
+ const gate = prestreamEligible({
17433
+ batteryOperated: await this.isBatteryOperated(deviceId),
17434
+ declaresSignals: (await this.declaredSignalCodes(deviceId)).length > 0
17393
17435
  });
17394
- return null;
17395
- }
17396
- const stream = this.cameraStreams.get(deviceId)?.get(camStreamId);
17397
- const codec = this.lastProbedByBroker.get(brokerId)?.codec ?? stream?.codec;
17398
- const planned = planPrestream({
17399
- sourceUrl,
17400
- width: stream?.resolution?.width,
17401
- height: stream?.resolution?.height,
17402
- codec
17403
- });
17404
- if ("refusal" in planned) {
17405
- logger.info("prestream: no fill for this camera", {
17406
- tags: { deviceId },
17407
- meta: {
17408
- refusal: planned.refusal,
17409
- camStreamId,
17410
- width: stream?.resolution?.width ?? null,
17411
- height: stream?.resolution?.height ?? null,
17412
- codec: codec ?? null
17413
- }
17436
+ if (!gate.eligible) {
17437
+ logger.info("prestream: not offered for this camera", {
17438
+ tags: { deviceId },
17439
+ meta: {
17440
+ brokerId,
17441
+ reason: gate.reason
17442
+ }
17443
+ });
17444
+ return null;
17445
+ }
17446
+ const stream = this.cameraStreams.get(deviceId)?.get(camStreamId);
17447
+ const codec = this.lastProbedByBroker.get(brokerId)?.codec ?? stream?.codec;
17448
+ const planned = planPrestream({
17449
+ sourceUrl,
17450
+ width: stream?.resolution?.width,
17451
+ height: stream?.resolution?.height,
17452
+ codec
17453
+ });
17454
+ if ("refusal" in planned) {
17455
+ logger.info("prestream: no fill for this camera", {
17456
+ tags: { deviceId },
17457
+ meta: {
17458
+ refusal: planned.refusal,
17459
+ camStreamId,
17460
+ width: stream?.resolution?.width ?? null,
17461
+ height: stream?.resolution?.height ?? null,
17462
+ codec: codec ?? null
17463
+ }
17464
+ });
17465
+ return null;
17466
+ }
17467
+ return await this.preparePrestreamFrame(deviceId, brokerId, planned.plan, logger);
17468
+ },
17469
+ /**
17470
+ * REFRESH THE PICTURE WHILE THE CAMERA IS UP.
17471
+ *
17472
+ * The window shows the camera's last snapshot, and nothing was
17473
+ * refreshing it: a battery camera refuses a capture while it believes
17474
+ * itself asleep, and that belief lags the wake by seconds — so the only
17475
+ * attempts ever made were the prestream's own, while composing, which is
17476
+ * BEFORE any media exists. Measured on device 640 (2026-09-16): four
17477
+ * wakes in forty minutes, the encoded picture `bytes=23912` every time,
17478
+ * and between first packet and suspend not one capture was even
17479
+ * attempted. The operator read the result as a placeholder rather than
17480
+ * as their own room, which is exactly what it had become.
17481
+ *
17482
+ * Here the camera is provably awake and its media is flowing, so a
17483
+ * capture costs it nothing it is not already paying — and what lands is
17484
+ * a scene from the event that just happened.
17485
+ *
17486
+ * NEVER `force`: it is an operator signal, and the rule holds even where
17487
+ * it would be harmless. If the native path still refuses, the addon's
17488
+ * own stream-broker fallback pulls a frame from the restream that is, by
17489
+ * construction, running right now.
17490
+ *
17491
+ * Fire and forget, and a failure is DEBUG: the window already has a
17492
+ * picture to show, and this only decides how recent the NEXT one is.
17493
+ */
17494
+ onRealMediaStarted: () => {
17495
+ const api = this.api;
17496
+ if (!api) return;
17497
+ api.snapshot.getSnapshot.query({ deviceId }).then(() => {
17498
+ logger.debug("prestream: picture refreshed from the live window", {
17499
+ tags: { deviceId },
17500
+ meta: { brokerId }
17501
+ });
17502
+ }).catch((err) => {
17503
+ logger.debug("prestream: live-window refresh failed — keeping the older picture", {
17504
+ tags: { deviceId },
17505
+ meta: {
17506
+ brokerId,
17507
+ error: errMsg(err)
17508
+ }
17509
+ });
17414
17510
  });
17415
- return null;
17416
17511
  }
17417
- return await this.preparePrestreamFrame(deviceId, brokerId, planned.plan, logger);
17418
- } };
17512
+ };
17419
17513
  }
17420
17514
  /**
17421
17515
  * Fetch the picture, compose the treatment, encode ONCE and cache the bytes.
@@ -31440,6 +31534,20 @@ var CameraStreamsProvider = class {
31440
31534
  return null;
31441
31535
  }
31442
31536
  }
31537
+ if (requirements.requireProfileBound === true) {
31538
+ const boundIds = new Set(this.manager.getProfileSlotsForDevice(input.deviceId).map((s) => s.sourceCamStreamId).filter((id) => id !== null));
31539
+ for (let i = candidates.length - 1; i >= 0; i -= 1) if (!boundIds.has(candidates[i].camStreamId)) candidates.splice(i, 1);
31540
+ if (candidates.length === 0) {
31541
+ this.manager.getLogger?.()?.info("camera-streams: pickStream — no candidate is bound to a profile", {
31542
+ tags: { deviceId: input.deviceId },
31543
+ meta: {
31544
+ deviceId: input.deviceId,
31545
+ bound: [...boundIds]
31546
+ }
31547
+ });
31548
+ return null;
31549
+ }
31550
+ }
31443
31551
  const preferredProviders = preferences?.preferredProviders ?? PROVIDER_PREFIX_DEFAULTS;
31444
31552
  const resolutionPreference = preferences?.resolutionPreference ?? "highest";
31445
31553
  const providerRank = (camStreamId) => {
@@ -31470,12 +31578,40 @@ var CameraStreamsProvider = class {
31470
31578
  reasonParts.push(ranked.transcodes ? "transcode" : "passthrough");
31471
31579
  if (preferences?.targetHeight !== void 0) reasonParts.push(`target=${preferences.targetHeight}`);
31472
31580
  if (requireSibling.length > 0) reasonParts.push(`bypassed=${requireSibling.join("|")}`);
31581
+ const bound = this.boundSlotFor(input.deviceId, winner.camStreamId);
31582
+ if (bound !== null) reasonParts.push(`profile=${bound.profile}`);
31473
31583
  return {
31474
31584
  camStreamId: winner.camStreamId,
31475
31585
  codec: winner.codec,
31476
31586
  resolution: winner.resolution,
31477
31587
  transcodes: ranked.transcodes,
31478
- reason: reasonParts.join(" ")
31588
+ reason: reasonParts.join(" "),
31589
+ ...bound === null ? {} : {
31590
+ profile: bound.profile,
31591
+ brokerId: bound.brokerId,
31592
+ ...bound.url === void 0 ? {} : { url: bound.url }
31593
+ },
31594
+ ...winner.fps === void 0 ? {} : {
31595
+ fps: winner.fps,
31596
+ fpsSource: "published"
31597
+ }
31598
+ };
31599
+ }
31600
+ /**
31601
+ * The profile slot serving `camStreamId`, with the url to dial it, or null
31602
+ * when the broker has bound no slot to it.
31603
+ *
31604
+ * Both reads are local to the manager — no round trip is added to answer
31605
+ * what every consumer was fetching for itself.
31606
+ */
31607
+ boundSlotFor(deviceId, camStreamId) {
31608
+ const slot = this.manager.getProfileSlotsForDevice(deviceId).find((s) => s.sourceCamStreamId === camStreamId);
31609
+ if (slot === void 0) return null;
31610
+ const entry = this.manager.getProfileRtspEntriesForDevice(deviceId).find((e) => e.profile === slot.profile);
31611
+ return {
31612
+ profile: slot.profile,
31613
+ brokerId: slot.brokerId,
31614
+ ...entry?.url === void 0 ? {} : { url: entry.url }
31479
31615
  };
31480
31616
  }
31481
31617
  };
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BQlBhj7w.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DpxY0D_G.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.249",
3
+ "version": "1.2.251",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",