@camstack/system 1.2.321 → 1.2.323

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 (63) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/camera-grid/addon.d.ts +13 -1
  12. package/dist/builtins/camera-grid/index.js +46 -12
  13. package/dist/builtins/camera-grid/index.mjs +46 -12
  14. package/dist/builtins/composer/composer.addon.js +2 -2
  15. package/dist/builtins/composer/composer.addon.mjs +2 -2
  16. package/dist/builtins/console-logging/index.js +1 -1
  17. package/dist/builtins/console-logging/index.mjs +1 -1
  18. package/dist/builtins/core-blocks/composition-api.d.ts +4 -2
  19. package/dist/builtins/core-blocks/core-blocks.addon.js +6 -4
  20. package/dist/builtins/core-blocks/core-blocks.addon.mjs +6 -4
  21. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  22. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  23. package/dist/builtins/doorbell/doorbell-composition-migration.d.ts +1 -13
  24. package/dist/builtins/doorbell/virtual-doorbell.addon.js +6 -64
  25. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +5 -63
  26. package/dist/builtins/hub-forwarder/index.js +1 -1
  27. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  28. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  29. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  30. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  31. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  32. package/dist/builtins/local-network/local-network.addon.js +1 -1
  33. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  34. package/dist/builtins/loki-logging/index.js +1 -1
  35. package/dist/builtins/loki-logging/index.mjs +1 -1
  36. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  37. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  38. package/dist/builtins/platform-probe/index.js +1 -1
  39. package/dist/builtins/platform-probe/index.mjs +1 -1
  40. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  41. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  42. package/dist/builtins/snapshot/index.js +18 -5
  43. package/dist/builtins/snapshot/index.mjs +18 -5
  44. package/dist/builtins/snapshot/snapshot.addon.d.ts +3 -0
  45. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  46. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  47. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  48. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  49. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  51. package/dist/builtins/system-config/system-config.addon.js +1 -1
  52. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  53. package/dist/builtins/winston-logging/index.js +1 -1
  54. package/dist/builtins/winston-logging/index.mjs +1 -1
  55. package/dist/{composition-sources-iDqQVPng.mjs → composition-sources-CeAPNF5Q.mjs} +1 -1
  56. package/dist/{composition-sources-gGrw35Lt.js → composition-sources-DkVxrXLm.js} +1 -1
  57. package/dist/{dist-BVHQ6TX1.mjs → dist-Bs6UyVGd.mjs} +2020 -1479
  58. package/dist/{dist-BMDYmNV3.js → dist-Bup0sTZb.js} +2043 -1490
  59. package/dist/index.js +1 -1
  60. package/dist/index.mjs +1 -1
  61. package/dist/{retired-settings-keys-FhjpCM5N.js → retired-settings-keys-Db0P4Yvn.js} +1 -1
  62. package/dist/{retired-settings-keys-BlQLUReM.mjs → retired-settings-keys-LROp1DXx.mjs} +1 -1
  63. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { At as isSameAddonId, Dn as SOURCE_CAP_ACTIVE_FIELD, En as SOURCE_CAPS, Gn as BaseAddon, On as SOURCE_CAP_CHANGED_AT_FIELD, Rn as evaluateSensorEdge, Un as DeviceType, W as bareAddonId, Wn as errMsg, ct as defineExtensionSlotProvider, hr as EventCategory, it as createDeviceExtensionProvider, mt as doorbellCapability, tr as createEvent, ut as deviceExtensionCapability, zn as isSourceCap } from "../../dist-BVHQ6TX1.mjs";
1
+ import { An as SOURCE_CAPS, Bn as evaluateSensorEdge, Gn as DeviceType, Kn as errMsg, Nt as isSameAddonId, Vn as isSourceCap, W as bareAddonId, _r as EventCategory, _t as doorbellFeatureSources, at as createDeviceExtensionProvider, ft as deviceExtensionCapability, gt as doorbellCapability, lt as defineExtensionSlotProvider, q as buildDoorbellCompositionFeature, qn as BaseAddon, rr as createEvent } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { n as parseDeviceStateChanged } from "../../device-state-changed-DoBbkyEI.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/doorbell/trigger-engine.ts
@@ -607,50 +607,6 @@ var DOORBELL_COMPOSITION_MIGRATION_CALLER = {
607
607
  userId: "system:doorbell-source-migration",
608
608
  isAdmin: true
609
609
  };
610
- function buildDoorbellCompositionFeature(input) {
611
- const sources = [...input.sources].toSorted((a, b) => `${a.source.addonId}/${a.source.stableId}#${a.cap}`.localeCompare(`${b.source.addonId}/${b.source.stableId}#${b.cap}`));
612
- if (sources.length === 0) throw new Error("doorbell composition needs at least one source");
613
- const edgeBindings = {};
614
- sources.forEach((entry, index) => {
615
- edgeBindings[`active${index}`] = {
616
- kind: "from",
617
- source: entry.source,
618
- cap: entry.cap,
619
- fieldPath: entry.activeField
620
- };
621
- edgeBindings[`changed${index}`] = {
622
- kind: "from",
623
- source: entry.source,
624
- cap: entry.cap,
625
- fieldPath: entry.changedAtField
626
- };
627
- });
628
- return {
629
- kind: "fields",
630
- cap: "doorbell",
631
- enabled: input.enabled,
632
- fields: {
633
- lastPressedAt: {
634
- kind: "expression",
635
- expr: `latest(${sources.map((_, index) => `rose(active${index}, changed${index}, 2000)`).join(", ")})`,
636
- bindings: edgeBindings
637
- },
638
- pressCountSinceStart: {
639
- kind: "expression",
640
- expr: "count(lastPressedAt)",
641
- bindings: { lastPressedAt: {
642
- kind: "from",
643
- source: {
644
- addonId: input.camera.addonId,
645
- stableId: input.camera.stableId
646
- },
647
- cap: "doorbell",
648
- fieldPath: "lastPressedAt"
649
- } }
650
- }
651
- }
652
- };
653
- }
654
610
  function isRecord(value) {
655
611
  return typeof value === "object" && value !== null && !Array.isArray(value);
656
612
  }
@@ -666,32 +622,18 @@ function withoutEnabled(feature) {
666
622
  const { enabled: _enabled, ...rest } = feature;
667
623
  return rest;
668
624
  }
669
- function bindingHasCap(entries, cap) {
670
- return entries.some((entry) => entry.capName === cap);
671
- }
672
625
  function migrationSources(camera, configured, devicesById, bindingsById) {
673
626
  const sources = [];
674
627
  for (const configuredSource of configured) {
675
628
  const device = devicesById.get(configuredSource.deviceId);
676
629
  const entries = bindingsById.get(configuredSource.deviceId);
677
630
  if (device === void 0 || entries === void 0) throw new Error(`source device ${configuredSource.deviceId} could not be resolved and read`);
678
- const ref = {
631
+ const found = doorbellFeatureSources({
679
632
  addonId: device.addonId,
680
633
  stableId: device.stableId
681
- };
682
- const supportedCaps = Object.keys(SOURCE_CAP_ACTIVE_FIELD).filter((cap) => Object.hasOwn(SOURCE_CAP_CHANGED_AT_FIELD, cap) && bindingHasCap(entries, cap)).toSorted();
683
- if (supportedCaps.length === 0) throw new Error(`source device ${configuredSource.deviceId} has no supported source capability`);
684
- for (const cap of supportedCaps) {
685
- const activeField = SOURCE_CAP_ACTIVE_FIELD[cap];
686
- const changedAtField = SOURCE_CAP_CHANGED_AT_FIELD[cap];
687
- if (activeField === void 0 || changedAtField === void 0) throw new Error(`source capability ${cap} has no transition field mapping`);
688
- sources.push({
689
- source: ref,
690
- cap,
691
- activeField,
692
- changedAtField
693
- });
694
- }
634
+ }, entries.map((entry) => entry.capName));
635
+ if (found.length === 0) throw new Error(`source device ${configuredSource.deviceId} has no supported source capability`);
636
+ sources.push(...found);
695
637
  }
696
638
  if (sources.length === 0) throw new Error(`camera ${camera.id} has no resolvable doorbell sources`);
697
639
  return sources;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, Rt as logDestinationCapability } from "../../dist-BVHQ6TX1.mjs";
1
+ import { Vt as logDestinationCapability, qn as BaseAddon } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-BMDYmNV3.js");
3
+ const require_dist = require("../../dist-Bup0sTZb.js");
4
4
  //#region src/builtins/liveness-monitor/liveness-checks.ts
5
5
  var NO_DEVICES = "liveness:no-devices";
6
6
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -1,4 +1,4 @@
1
- import { E as RECORDING_SOURCE_CAMSTACK_ADDON, Gn as BaseAddon, Wn as errMsg, hr as EventCategory, k as RecordingStatusSchema, tr as createEvent } from "../../dist-BVHQ6TX1.mjs";
1
+ import { E as RECORDING_SOURCE_CAMSTACK_ADDON, Kn as errMsg, _r as EventCategory, k as RecordingStatusSchema, qn as BaseAddon, rr as createEvent } from "../../dist-Bs6UyVGd.mjs";
2
2
  //#region src/builtins/liveness-monitor/liveness-checks.ts
3
3
  var NO_DEVICES = "liveness:no-devices";
4
4
  var ALL_OFFLINE = "liveness:all-devices-offline";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Gn as BaseAddon, H as authProviderCapability, L as UserRecordSchema, M as ScopedTokenSchema, r as ApiKeyRecordSchema, vn as userManagementCapability } from "../../dist-BVHQ6TX1.mjs";
2
+ import { H as authProviderCapability, L as UserRecordSchema, M as ScopedTokenSchema, Sn as userManagementCapability, qn as BaseAddon, r as ApiKeyRecordSchema } from "../../dist-Bs6UyVGd.mjs";
3
3
  import * as crypto$2 from "node:crypto";
4
4
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
5
5
  import nodeCrypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-BMDYmNV3.js");
3
+ const require_dist = require("../../dist-Bup0sTZb.js");
4
4
  const require_tls = require("../../tls-D_wUeOTD.js");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, It as localNetworkCapability, hr as EventCategory } from "../../dist-BVHQ6TX1.mjs";
1
+ import { _r as EventCategory, qn as BaseAddon, zt as localNetworkCapability } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, i as writeExtraSans, l as reissueTlsLeaf, n as readTlsAccessStatus, o as validateUploadedTls, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, y as applyLanHttp } from "../../tls-IM-v7JV4.mjs";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  //#region src/builtins/loki-logging/loki-payload.ts
8
8
  /**
9
9
  * Loki rejects a label name that is not a valid Prometheus label
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, Rt as logDestinationCapability } from "../../dist-BVHQ6TX1.mjs";
1
+ import { Vt as logDestinationCapability, qn as BaseAddon } from "../../dist-Bs6UyVGd.mjs";
2
2
  //#region src/builtins/loki-logging/loki-payload.ts
3
3
  /**
4
4
  * Loki rejects a label name that is not a valid Prometheus label
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  const require_proc_stat = require("../../proc-stat-DL-WUOhl.js");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_fs = require("node:fs");
@@ -1,4 +1,4 @@
1
- import { Ct as foldSnapshotByFunction, Gn as BaseAddon, Ut as metricsProviderCapability, Wn as errMsg, en as reducePoints, hr as EventCategory, nn as resolveBucketMs, tr as createEvent } from "../../dist-BVHQ6TX1.mjs";
1
+ import { Et as foldSnapshotByFunction, Kn as errMsg, _r as EventCategory, in as reducePoints, on as resolveBucketMs, qn as BaseAddon, qt as metricsProviderCapability, rr as createEvent } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { a as parseProcStat, i as nodeProcReader, n as elapsedSecSince, o as parseProcStatusRss, r as lifetimeCpuPercent, t as commandFromCmdline } from "../../proc-stat-CbC55Kqi.mjs";
3
3
  import { access, readFile, readdir } from "node:fs/promises";
4
4
  import * as fs from "node:fs";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Gn as BaseAddon, Wn as errMsg, Zt as platformProbeCapability, cn as scoreRuntimes, hr as EventCategory, ht as enumerateInferenceDevices, rr as emitReadiness } from "../../dist-BVHQ6TX1.mjs";
2
+ import { Kn as errMsg, _r as EventCategory, ar as emitReadiness, fn as scoreRuntimes, qn as BaseAddon, tn as platformProbeCapability, vt as enumerateInferenceDevices } from "../../dist-Bs6UyVGd.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { execFile } from "node:child_process";
5
5
  import * as os from "node:os";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, hr as EventCategory } from "../../dist-BVHQ6TX1.mjs";
1
+ import { _r as EventCategory, qn as BaseAddon } from "../../dist-Bs6UyVGd.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-Cqs7GkOV.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1135,6 +1135,9 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
1135
1135
  captureFlight = new SingleFlight(COALESCE_MS, (outcome) => outcome.ok && outcome.image !== null);
1136
1136
  /** Bounds simultaneous ffmpeg keyframe grabs (the wrapper path — common case). */
1137
1137
  grabPool = new Semaphore(6);
1138
+ /** This node's PINNED ffmpeg (D540), asked once. The keyframe grab used to
1139
+ * spawn a bare `'ffmpeg'` — the HOST's binary — off PATH. */
1140
+ pinnedFfmpeg = require_dist.memoizePinnedFfmpeg(() => this.ctx.deps.ensureFfmpeg());
1138
1141
  /** Bounds simultaneous native (vendor HTTP/ONVIF) snapshot fetches. */
1139
1142
  nativePool = new Semaphore(6);
1140
1143
  /**
@@ -2088,14 +2091,24 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
2088
2091
  * "first keyframe" when a suspended broker resumes.
2089
2092
  */
2090
2093
  async runGrabWithResumeRetry(url, deviceId) {
2094
+ let ffmpegPath;
2095
+ try {
2096
+ ffmpegPath = await this.pinnedFfmpeg();
2097
+ } catch (err) {
2098
+ this.ctx.logger.error("grabFrame refused — no pinned ffmpeg on this node (D540)", {
2099
+ tags: { deviceId },
2100
+ meta: { error: require_dist.errMsg(err) }
2101
+ });
2102
+ throw new Error(`grabFrame: no pinned ffmpeg for device ${deviceId} — ${require_dist.errMsg(err)}`, { cause: err });
2103
+ }
2091
2104
  let buf;
2092
2105
  try {
2093
- buf = await this.grabPool.run(() => runFfmpegFrameGrab(url, 15e3));
2106
+ buf = await this.grabPool.run(() => runFfmpegFrameGrab(ffmpegPath, url, 15e3));
2094
2107
  } catch (err) {
2095
2108
  if (isBrokerColdError(require_dist.errMsg(err))) {
2096
2109
  this.ctx.logger.debug("grabFrame: broker-resume race — retrying in 1500ms", { tags: { deviceId } });
2097
2110
  await new Promise((r) => setTimeout(r, 1500));
2098
- buf = await this.grabPool.run(() => runFfmpegFrameGrab(url, 15e3));
2111
+ buf = await this.grabPool.run(() => runFfmpegFrameGrab(ffmpegPath, url, 15e3));
2099
2112
  } else throw err;
2100
2113
  }
2101
2114
  if (buf.length === 0) return null;
@@ -2642,9 +2655,9 @@ function qualityRank(brokerId, prefix) {
2642
2655
  function isBrokerColdError(msg) {
2643
2656
  return msg.includes("Invalid data found when processing input") || msg.includes("Error opening input") || msg.includes("Connection refused") || msg.includes("No route to host");
2644
2657
  }
2645
- function runFfmpegFrameGrab(url, timeoutMs) {
2658
+ function runFfmpegFrameGrab(ffmpegPath, url, timeoutMs) {
2646
2659
  return new Promise((resolve, reject) => {
2647
- (0, node_child_process.execFile)("ffmpeg", [
2660
+ (0, node_child_process.execFile)(ffmpegPath, [
2648
2661
  "-loglevel",
2649
2662
  "error",
2650
2663
  "-threads",
@@ -1,4 +1,4 @@
1
- import { G as batteryCapability, Gn as BaseAddon, Un as DeviceType, Vn as DeviceFeature, W as bareAddonId, Wn as errMsg, _n as streamQualityLabel, a as BatteryStatusSchema, cr as nodePin, dn as snapshotCapability, hr as EventCategory, lt as deriveBatteryPresence } from "../../dist-BVHQ6TX1.mjs";
1
+ import { G as batteryCapability, Gn as DeviceType, Kn as errMsg, Kt as memoizePinnedFfmpeg, Un as DeviceFeature, W as bareAddonId, _r as EventCategory, a as BatteryStatusSchema, hn as snapshotCapability, qn as BaseAddon, ur as nodePin, ut as deriveBatteryPresence, xn as streamQualityLabel } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CZPvteq2.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -1129,6 +1129,9 @@ var SnapshotAddon = class SnapshotAddon extends BaseAddon {
1129
1129
  captureFlight = new SingleFlight(COALESCE_MS, (outcome) => outcome.ok && outcome.image !== null);
1130
1130
  /** Bounds simultaneous ffmpeg keyframe grabs (the wrapper path — common case). */
1131
1131
  grabPool = new Semaphore(6);
1132
+ /** This node's PINNED ffmpeg (D540), asked once. The keyframe grab used to
1133
+ * spawn a bare `'ffmpeg'` — the HOST's binary — off PATH. */
1134
+ pinnedFfmpeg = memoizePinnedFfmpeg(() => this.ctx.deps.ensureFfmpeg());
1132
1135
  /** Bounds simultaneous native (vendor HTTP/ONVIF) snapshot fetches. */
1133
1136
  nativePool = new Semaphore(6);
1134
1137
  /**
@@ -2082,14 +2085,24 @@ var SnapshotAddon = class SnapshotAddon extends BaseAddon {
2082
2085
  * "first keyframe" when a suspended broker resumes.
2083
2086
  */
2084
2087
  async runGrabWithResumeRetry(url, deviceId) {
2088
+ let ffmpegPath;
2089
+ try {
2090
+ ffmpegPath = await this.pinnedFfmpeg();
2091
+ } catch (err) {
2092
+ this.ctx.logger.error("grabFrame refused — no pinned ffmpeg on this node (D540)", {
2093
+ tags: { deviceId },
2094
+ meta: { error: errMsg(err) }
2095
+ });
2096
+ throw new Error(`grabFrame: no pinned ffmpeg for device ${deviceId} — ${errMsg(err)}`, { cause: err });
2097
+ }
2085
2098
  let buf;
2086
2099
  try {
2087
- buf = await this.grabPool.run(() => runFfmpegFrameGrab(url, 15e3));
2100
+ buf = await this.grabPool.run(() => runFfmpegFrameGrab(ffmpegPath, url, 15e3));
2088
2101
  } catch (err) {
2089
2102
  if (isBrokerColdError(errMsg(err))) {
2090
2103
  this.ctx.logger.debug("grabFrame: broker-resume race — retrying in 1500ms", { tags: { deviceId } });
2091
2104
  await new Promise((r) => setTimeout(r, 1500));
2092
- buf = await this.grabPool.run(() => runFfmpegFrameGrab(url, 15e3));
2105
+ buf = await this.grabPool.run(() => runFfmpegFrameGrab(ffmpegPath, url, 15e3));
2093
2106
  } else throw err;
2094
2107
  }
2095
2108
  if (buf.length === 0) return null;
@@ -2636,9 +2649,9 @@ function qualityRank(brokerId, prefix) {
2636
2649
  function isBrokerColdError(msg) {
2637
2650
  return msg.includes("Invalid data found when processing input") || msg.includes("Error opening input") || msg.includes("Connection refused") || msg.includes("No route to host");
2638
2651
  }
2639
- function runFfmpegFrameGrab(url, timeoutMs) {
2652
+ function runFfmpegFrameGrab(ffmpegPath, url, timeoutMs) {
2640
2653
  return new Promise((resolve, reject) => {
2641
- execFile("ffmpeg", [
2654
+ execFile(ffmpegPath, [
2642
2655
  "-loglevel",
2643
2656
  "error",
2644
2657
  "-threads",
@@ -93,6 +93,9 @@ export declare class SnapshotAddon extends BaseAddon<SnapshotAddonConfig> {
93
93
  private readonly captureFlight;
94
94
  /** Bounds simultaneous ffmpeg keyframe grabs (the wrapper path — common case). */
95
95
  private readonly grabPool;
96
+ /** This node's PINNED ffmpeg (D540), asked once. The keyframe grab used to
97
+ * spawn a bare `'ffmpeg'` — the HOST's binary — off PATH. */
98
+ private readonly pinnedFfmpeg;
96
99
  /** Bounds simultaneous native (vendor HTTP/ONVIF) snapshot fetches. */
97
100
  private readonly nativePool;
98
101
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, St as filesystemBrowseCapability, hn as storageProviderCapability } from "../../dist-BVHQ6TX1.mjs";
1
+ import { Tt as filesystemBrowseCapability, qn as BaseAddon, yn as storageProviderCapability } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  const require_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { Cn as withLocationMode, D as REDACTED_SECRET, F as StorageLocationTypeSchema, Gn as BaseAddon, Ht as mayWriteToLocation, I as StorageMigrationJobSchema, N as StorageCleanupJobSchema, Ot as isLocationEnabled, P as StorageLocationModeSchema, Pt as legacyModeOf, Tt as gbToBytes, Vt as mayReadLocation, Wt as modeMayWrite, Y as collectSecretConfigKeys, fn as storageCapability, hr as EventCategory, ln as settingsStoreCapability, lr as parseJsonObject, pn as storageMigrationCapability, rn as resolveLocationMode, x as LegacyStorageLocationDefaultSchema, yt as evictionPolicyOfLocation } from "../../dist-BVHQ6TX1.mjs";
1
+ import { D as REDACTED_SECRET, Dn as withLocationMode, F as StorageLocationTypeSchema, Gt as mayWriteToLocation, I as StorageMigrationJobSchema, Jt as modeMayWrite, Lt as legacyModeOf, N as StorageCleanupJobSchema, Ot as gbToBytes, P as StorageLocationModeSchema, St as evictionPolicyOfLocation, Wt as mayReadLocation, X as collectSecretConfigKeys, _n as storageMigrationCapability, _r as EventCategory, dr as parseJsonObject, gn as storageCapability, jt as isLocationEnabled, pn as settingsStoreCapability, qn as BaseAddon, sn as resolveLocationMode, x as LegacyStorageLocationDefaultSchema } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-BdT320dq.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, Wn as errMsg, ar as hydrateSchema } from "../../dist-BVHQ6TX1.mjs";
1
+ import { Kn as errMsg, qn as BaseAddon, sr as hydrateSchema } from "../../dist-Bs6UyVGd.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BMDYmNV3.js");
6
+ const require_dist = require("../../dist-Bup0sTZb.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { Gn as BaseAddon, Rt as logDestinationCapability } from "../../dist-BVHQ6TX1.mjs";
1
+ import { Vt as logDestinationCapability, qn as BaseAddon } from "../../dist-Bs6UyVGd.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import { In as compositionSourceKey, Wn as errMsg } from "./dist-BVHQ6TX1.mjs";
1
+ import { Kn as errMsg, Rn as compositionSourceKey } from "./dist-Bs6UyVGd.mjs";
2
2
  //#region src/builtins/core-blocks/composition-sources.ts
3
3
  /**
4
4
  * Describe what a composition reads and writes for SAVE-time validation
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BMDYmNV3.js");
1
+ const require_dist = require("./dist-Bup0sTZb.js");
2
2
  //#region src/builtins/core-blocks/composition-sources.ts
3
3
  /**
4
4
  * Describe what a composition reads and writes for SAVE-time validation