@camstack/system 1.2.148 → 1.2.149

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 (57) hide show
  1. package/dist/addon-runner.js +2 -1
  2. package/dist/addon-runner.mjs +2 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.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/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  14. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/retired-settings-keys.d.ts +16 -0
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/system-config/system-config.addon.js +1 -1
  45. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  46. package/dist/builtins/winston-logging/index.js +1 -1
  47. package/dist/builtins/winston-logging/index.mjs +1 -1
  48. package/dist/{dist-BwD83D2f.mjs → dist-CZWAU40-.mjs} +128 -0
  49. package/dist/{dist-B-mBrEz9.js → dist-DdEXjTDo.js} +128 -0
  50. package/dist/index.js +2 -2
  51. package/dist/index.mjs +2 -2
  52. package/dist/kernel/transport/child-cap-protocol.d.ts +2 -2
  53. package/dist/{manifest-python-deps-BV_Cy99l.js → manifest-python-deps-CivtrAha.js} +1 -1
  54. package/dist/{manifest-python-deps-DpRX9LtD.mjs → manifest-python-deps-HFh_iE0E.mjs} +1 -1
  55. package/dist/{retired-settings-keys-Davtjo5p.js → retired-settings-keys-BXaTgLyZ.js} +90 -14
  56. package/dist/{retired-settings-keys-CRKpAmSn.mjs → retired-settings-keys-BynPjBHV.mjs} +90 -14
  57. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_python_deps = require("./manifest-python-deps-BV_Cy99l.js");
2
+ const require_manifest_python_deps = require("./manifest-python-deps-CivtrAha.js");
3
3
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path);
@@ -48,6 +48,7 @@ async function dispatchSettings(addon, addonId, method, args) {
48
48
  if (typeof addon.updateGlobalSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateGlobalSettings`);
49
49
  await addon.updateGlobalSettings(args.patch ?? {}, args.nodeId);
50
50
  return { success: true };
51
+ case "getIntegrationSettings": return typeof addon.getIntegrationSettings === "function" ? await addon.getIntegrationSettings(args.nodeId) ?? null : null;
51
52
  case "getDeviceSettings": return typeof addon.getDeviceSettings === "function" && typeof args.deviceId === "number" ? await addon.getDeviceSettings(args.deviceId) ?? null : null;
52
53
  case "updateDeviceSettings":
53
54
  if (typeof addon.updateDeviceSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateDeviceSettings`);
@@ -1,4 +1,4 @@
1
- import { F as createUdsLoggerWithControl, Ht as startRunnerHeapWatch, I as LocalChildClient, Nt as RUNNER_RSS_BUDGET_ENV, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, yt as installManifestNativeDeps } from "./manifest-python-deps-DpRX9LtD.mjs";
1
+ import { F as createUdsLoggerWithControl, Ht as startRunnerHeapWatch, I as LocalChildClient, Nt as RUNNER_RSS_BUDGET_ENV, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, yt as installManifestNativeDeps } from "./manifest-python-deps-HFh_iE0E.mjs";
2
2
  import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
3
3
  import { register } from "node:module";
4
4
  import * as path$1 from "node:path";
@@ -44,6 +44,7 @@ async function dispatchSettings(addon, addonId, method, args) {
44
44
  if (typeof addon.updateGlobalSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateGlobalSettings`);
45
45
  await addon.updateGlobalSettings(args.patch ?? {}, args.nodeId);
46
46
  return { success: true };
47
+ case "getIntegrationSettings": return typeof addon.getIntegrationSettings === "function" ? await addon.getIntegrationSettings(args.nodeId) ?? null : null;
47
48
  case "getDeviceSettings": return typeof addon.getDeviceSettings === "function" && typeof args.deviceId === "number" ? await addon.getDeviceSettings(args.deviceId) ?? null : null;
48
49
  case "updateDeviceSettings":
49
50
  if (typeof addon.updateDeviceSettings !== "function") throw new Error(`child-addon-call: addon "${addonId}" does not implement updateDeviceSettings`);
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ot as errMsg, S as addonPagesCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { Ot as errMsg, S as addonPagesCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-CZWAU40-.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ot as errMsg, kt as BaseAddon, tn as EventCategory, w as addonWidgetsCapability } from "../../dist-BwD83D2f.mjs";
1
+ import { Ot as errMsg, kt as BaseAddon, tn as EventCategory, w as addonWidgetsCapability } from "../../dist-CZWAU40-.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
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
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  //#region src/builtins/alerts/prune-policy.ts
8
8
  /**
9
9
  * Ids older than `cutoffMs`, oldest first.
@@ -1,4 +1,4 @@
1
- import { Ht as createEvent, Ot as errMsg, T as alertsCapability, kt as BaseAddon, n as AlertSchema, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { Ht as createEvent, Ot as errMsg, T as alertsCapability, kt as BaseAddon, n as AlertSchema, tn as EventCategory } from "../../dist-CZWAU40-.mjs";
2
2
  //#region src/builtins/alerts/prune-policy.ts
3
3
  /**
4
4
  * Ids older than `cutoffMs`, oldest first.
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let buffer = require("buffer");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { D as backupCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { D as backupCapability, kt as BaseAddon, tn as EventCategory } from "../../dist-CZWAU40-.mjs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { Buffer as Buffer$1 } from "buffer";
4
4
  import * as fsp from "node:fs/promises";
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-BwD83D2f.mjs";
1
+ import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-CZWAU40-.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -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-B-mBrEz9.js");
3
+ const require_dist = require("../../dist-DdEXjTDo.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { M as coreBlockIdFromAddonId, N as coreBlocksCapability, Ot as errMsg, c as CoreBlockSchema, j as coreBlockAddonId, kt as BaseAddon, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, u as DeclaredDevices } from "../../dist-BwD83D2f.mjs";
1
+ import { M as coreBlockIdFromAddonId, N as coreBlocksCapability, Ot as errMsg, c as CoreBlockSchema, j as coreBlockAddonId, kt as BaseAddon, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, u as DeclaredDevices } from "../../dist-CZWAU40-.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B-mBrEz9.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-Davtjo5p.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-BXaTgLyZ.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
10
10
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -1,5 +1,5 @@
1
- import { A as buildStreamParamsConfigSchema, Ft as DeviceType, H as enumerateItemArrayFields, L as deviceManagerCapability, Nt as DeviceFeature, Ot as errMsg, Pt as DeviceRole, R as deviceStateCapability, Rt as WELL_KNOWN_TAB_MAP, U as enumerateSchemaFields, _ as STREAM_PROFILE_META, a as CAP_NAMES_WITH_STATUS, d as DeviceStatusSchema, en as sleep, ht as runtimeStatePolicyFor, kt as BaseAddon, lt as parseStreamParamsFormPatch, qt as isDeviceConfigCap, st as normalizeUnit, t as ALL_CAPABILITY_DEFINITIONS, tn as EventCategory, z as deviceStatusCapability } from "../../dist-BwD83D2f.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-CRKpAmSn.mjs";
1
+ import { A as buildStreamParamsConfigSchema, Ft as DeviceType, H as enumerateItemArrayFields, L as deviceManagerCapability, Nt as DeviceFeature, Ot as errMsg, Pt as DeviceRole, R as deviceStateCapability, Rt as WELL_KNOWN_TAB_MAP, U as enumerateSchemaFields, _ as STREAM_PROFILE_META, a as CAP_NAMES_WITH_STATUS, d as DeviceStatusSchema, en as sleep, ht as runtimeStatePolicyFor, kt as BaseAddon, lt as parseStreamParamsFormPatch, qt as isDeviceConfigCap, st as normalizeUnit, t as ALL_CAPABILITY_DEFINITIONS, tn as EventCategory, z as deviceStatusCapability } from "../../dist-CZWAU40-.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-BynPjBHV.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
5
5
  //#region src/builtins/device-manager/adoption-job-engine.ts
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -1,4 +1,4 @@
1
- import { B as doorbellCapability, Ft as DeviceType, Ht as createEvent, O as bareAddonId, Ot as errMsg, Q as isSourceCap, W as evaluateSensorEdge, Z as isSameAddonId, g as SOURCE_DEVICE_TYPES, h as SOURCE_CAPS, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { B as doorbellCapability, Ft as DeviceType, Ht as createEvent, O as bareAddonId, Ot as errMsg, Q as isSourceCap, W as evaluateSensorEdge, Z as isSameAddonId, g as SOURCE_DEVICE_TYPES, h as SOURCE_CAPS, kt as BaseAddon, tn as EventCategory } from "../../dist-CZWAU40-.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-BwD83D2f.mjs";
1
+ import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-CZWAU40-.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-B-mBrEz9.js");
3
+ const require_dist = require("../../dist-DdEXjTDo.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 { Ht as createEvent, Ot as errMsg, kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { Ht as createEvent, Ot as errMsg, kt as BaseAddon, tn as EventCategory } from "../../dist-CZWAU40-.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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { E as authProviderCapability, Tt as userManagementCapability, kt as BaseAddon, r as ApiKeyRecordSchema, v as ScopedTokenSchema, x as UserRecordSchema } from "../../dist-BwD83D2f.mjs";
2
+ import { E as authProviderCapability, Tt as userManagementCapability, kt as BaseAddon, r as ApiKeyRecordSchema, v as ScopedTokenSchema, x as UserRecordSchema } from "../../dist-CZWAU40-.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-B-mBrEz9.js");
3
+ const require_dist = require("../../dist-DdEXjTDo.js");
4
4
  const require_tls = require("../../tls-u8QCJCFE.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 { kt as BaseAddon, nt as localNetworkCapability, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { kt as BaseAddon, nt as localNetworkCapability, tn as EventCategory } from "../../dist-CZWAU40-.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-CQhPGSJm.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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-BwD83D2f.mjs";
1
+ import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-CZWAU40-.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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  let node_fs_promises = require("node:fs/promises");
8
8
  let node_child_process = require("node:child_process");
9
9
  let node_util = require("node:util");
@@ -1,4 +1,4 @@
1
- import { Ht as createEvent, Ot as errMsg, ft as reducePoints, kt as BaseAddon, ot as metricsProviderCapability, pt as resolveBucketMs, q as foldSnapshotByFunction, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { Ht as createEvent, Ot as errMsg, ft as reducePoints, kt as BaseAddon, ot as metricsProviderCapability, pt as resolveBucketMs, q as foldSnapshotByFunction, tn as EventCategory } from "../../dist-CZWAU40-.mjs";
2
2
  import { readFile, readdir } from "node:fs/promises";
3
3
  import { execFile, execFileSync } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  let node_util = require("node:util");
9
9
  let node_os = require("node:os");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Ot as errMsg, V as enumerateInferenceDevices, Wt as emitReadiness, kt as BaseAddon, tn as EventCategory, ut as platformProbeCapability, vt as scoreRuntimes } from "../../dist-BwD83D2f.mjs";
2
+ import { Ot as errMsg, V as enumerateInferenceDevices, Wt as emitReadiness, kt as BaseAddon, tn as EventCategory, ut as platformProbeCapability, vt as scoreRuntimes } from "../../dist-CZWAU40-.mjs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { promisify } from "node:util";
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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { kt as BaseAddon, tn as EventCategory } from "../../dist-BwD83D2f.mjs";
1
+ import { kt as BaseAddon, tn as EventCategory } from "../../dist-CZWAU40-.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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  let zod = require("zod");
8
8
  let node_crypto = require("node:crypto");
9
9
  let _camstack_types_node = require("@camstack/types/node");
@@ -1,4 +1,4 @@
1
- import { Ft as DeviceType, I as deriveBatteryPresence, Jt as nodePin, Nt as DeviceFeature, O as bareAddonId, Ot as errMsg, bt as snapshotCapability, i as BatteryStatusSchema, k as batteryCapability, kt as BaseAddon, tn as EventCategory, wt as streamQualityLabel } from "../../dist-BwD83D2f.mjs";
1
+ import { Ft as DeviceType, I as deriveBatteryPresence, Jt as nodePin, Nt as DeviceFeature, O as bareAddonId, Ot as errMsg, bt as snapshotCapability, i as BatteryStatusSchema, k as batteryCapability, kt as BaseAddon, tn as EventCategory, wt as streamQualityLabel } from "../../dist-CZWAU40-.mjs";
2
2
  import { z } from "zod";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { signExpiringUrl, verifyExpiringUrl } from "@camstack/types/node";
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { Ct as storageProviderCapability, K as filesystemBrowseCapability, kt as BaseAddon } from "../../dist-BwD83D2f.mjs";
1
+ import { Ct as storageProviderCapability, K as filesystemBrowseCapability, kt as BaseAddon } from "../../dist-CZWAU40-.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";
@@ -88,6 +88,22 @@ export interface RetiredKeyLogger {
88
88
  /**
89
89
  * The dead keys, per row.
90
90
  *
91
+ * ## Addressing these rows
92
+ *
93
+ * Every namespace below is a FORKED addon, whose `ctx.settings` comes from
94
+ * `addon-context-factory` and writes `<addonId>:addon-settings` row `root`. The
95
+ * scoped table is therefore the right target — unlike the hub builtin whose
96
+ * mis-addressed spec is dissected at {@link RetiredRowSpec}. Confirmed per
97
+ * namespace against `packages/<pkg>/package.json`'s manifest `id`.
98
+ *
99
+ * ## `keys` vs `perNodeKeys` is a data-loss decision, not a style choice
100
+ *
101
+ * `perNodeKeys` matches `<key>` AND `<key>@<nodeId>`. That is right for a key
102
+ * whose per-node copies are all dead (the engine cascade, the decoder
103
+ * `backend`). It is WRONG for `probedBestHwaccel` / `probedBestAudioBackend`,
104
+ * where the bare key is unreachable residue but every `@<nodeId>` copy is the
105
+ * node's live probe result — those go under `keys`, which is exact-match.
106
+ *
91
107
  * `detection-pipeline` / `addon-settings` / `root` — every key in this row is
92
108
  * dead; the row itself is kept because the addon writes live keys into it
93
109
  * (`pipelineTemplates`, `videoPipelineSteps`).
@@ -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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { St as storageMigrationCapability, Yt as parseJsonObject, b as StorageMigrationJobSchema, kt as BaseAddon, xt as storageCapability, y as StorageLocationTypeSchema, yt as settingsStoreCapability } from "../../dist-BwD83D2f.mjs";
1
+ import { St as storageMigrationCapability, Yt as parseJsonObject, b as StorageMigrationJobSchema, kt as BaseAddon, xt as storageCapability, y as StorageLocationTypeSchema, yt as settingsStoreCapability } from "../../dist-CZWAU40-.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs 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
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { Kt as hydrateSchema, Ot as errMsg, kt as BaseAddon } from "../../dist-BwD83D2f.mjs";
1
+ import { Kt as hydrateSchema, Ot as errMsg, kt as BaseAddon } from "../../dist-CZWAU40-.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-B-mBrEz9.js");
6
+ const require_dist = require("../../dist-DdEXjTDo.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 { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-BwD83D2f.mjs";
1
+ import { kt as BaseAddon, rt as logDestinationCapability } from "../../dist-CZWAU40-.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";
@@ -1247,6 +1247,40 @@ var BaseAddon = class {
1247
1247
  deviceSettingsSchema() {
1248
1248
  return null;
1249
1249
  }
1250
+ /**
1251
+ * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
1252
+ * ARE the configuration of its integration.
1253
+ *
1254
+ * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
1255
+ * operator should find on the addon's integration page (System →
1256
+ * Integrations → <name>) rather than only in the cluster-wide list of every
1257
+ * addon. Empty (the default) means the addon has no integration-level
1258
+ * settings and no such surface is offered — this is opt-in, because whether
1259
+ * an addon's configuration IS its integration's configuration depends on the
1260
+ * nature of the integration.
1261
+ *
1262
+ * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
1263
+ * the ONE global schema, in the ONE addon store, written by the ONE
1264
+ * `updateGlobalSettings` path. There is deliberately no
1265
+ * `updateIntegrationSettings`: a second write path is how a surface acquires
1266
+ * a second store key, and this repo has shipped that twice (`btmPath@hub`,
1267
+ * D266). Selecting sections cannot introduce a key that selecting cannot.
1268
+ *
1269
+ * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
1270
+ * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
1271
+ * removed with the reason recorded at
1272
+ * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
1273
+ * determined by WHICH schema it lives in, not by a field-level marker."* A
1274
+ * marker sprinkled across sections also has to borrow a field that already
1275
+ * means something else; borrowing `section.tab` put the literal word
1276
+ * "integration" into an operator-facing tab bar, because `tab` means "how to
1277
+ * GROUP this visually" and cannot also mean "where this lives" (D269
1278
+ * supersedes D268). One declaration, in one place, next to the schema whose
1279
+ * ids it names.
1280
+ */
1281
+ integrationSettingSections() {
1282
+ return [];
1283
+ }
1250
1284
  async getGlobalSettings(overlay, cap, nodeId) {
1251
1285
  const schema = this.globalSettingsSchema(cap);
1252
1286
  if (!schema) return { sections: [] };
@@ -1257,6 +1291,55 @@ var BaseAddon = class {
1257
1291
  } : projected);
1258
1292
  }
1259
1293
  /**
1294
+ * The integration-level view of this addon's settings: exactly the sections
1295
+ * named by {@link integrationSettingSections}, hydrated from the SAME store
1296
+ * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
1297
+ *
1298
+ * Returns `null` when the addon declared nothing — an addon that opts out has
1299
+ * no integration settings surface at all, rather than an empty one that reads
1300
+ * as a failed load.
1301
+ *
1302
+ * Three properties hold BY CONSTRUCTION, which is why they are here in core
1303
+ * and not in whichever UI happens to render this:
1304
+ *
1305
+ * 1. **One key.** The payload is a SUBSET of the global schema, so a field
1306
+ * shown here is the same field, with the same bare key, that the addon's
1307
+ * own page shows. There is no integration-specific writer — callers save
1308
+ * through `updateGlobalSettings` — so a second store key is unreachable,
1309
+ * not merely discouraged.
1310
+ * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
1311
+ * is `<key>@<nodeId>` and an integration is not a node; whichever node
1312
+ * such a field silently picked would be a wrong answer for the operator
1313
+ * who opened the page (D266).
1314
+ * 3. **No silent typo.** A declared id that names no section throws. The
1315
+ * alternative — skip it — turns a rename into a surface that quietly
1316
+ * empties, which looks exactly like an addon with nothing to configure.
1317
+ */
1318
+ async getIntegrationSettings(nodeId) {
1319
+ const declared = this.integrationSettingSections();
1320
+ if (declared.length === 0) return null;
1321
+ const schema = this.globalSettingsSchema();
1322
+ if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
1323
+ const byId = new Map(schema.sections.map((section) => [section.id, section]));
1324
+ const sections = [];
1325
+ for (const id of declared) {
1326
+ const section = byId.get(id);
1327
+ if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
1328
+ const fields = dropPerNodeFields(section.fields);
1329
+ if (fields.length === 0) continue;
1330
+ sections.push({
1331
+ ...section,
1332
+ fields
1333
+ });
1334
+ }
1335
+ if (sections.length === 0) return null;
1336
+ const projected = await this.resolveGlobalStore(nodeId);
1337
+ return hydrateSchema({
1338
+ ...schema,
1339
+ sections
1340
+ }, projected);
1341
+ }
1342
+ /**
1260
1343
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
1261
1344
  * every `perNode: true` field carries THAT node's scoped value on its bare
1262
1345
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -1560,6 +1643,41 @@ var BaseAddon = class {
1560
1643
  * `hydrateSchema` does. Valueless structural fields (separator/info/…)
1561
1644
  * don't declare `perNode` and are excluded by the `in` narrowing.
1562
1645
  */
1646
+ /**
1647
+ * The same fields with every `perNode: true` one removed, recursing into layout
1648
+ * containers exactly as {@link collectPerNodeFieldKeys} does. A container left
1649
+ * with no child is dropped rather than rendered empty.
1650
+ *
1651
+ * Used by `getIntegrationSettings`: an integration is not a node, so a field
1652
+ * whose store key is `<key>@<nodeId>` has no node to belong to there.
1653
+ */
1654
+ function dropPerNodeFields(fields) {
1655
+ const kept = [];
1656
+ for (const field of fields) {
1657
+ if (field.type === "group") {
1658
+ const inner = dropPerNodeFields(field.fields);
1659
+ if (inner.length > 0) kept.push({
1660
+ ...field,
1661
+ fields: inner
1662
+ });
1663
+ continue;
1664
+ }
1665
+ if (field.type === "sub-tabs") {
1666
+ const tabs = field.tabs.map((tab) => ({
1667
+ ...tab,
1668
+ fields: dropPerNodeFields(tab.fields)
1669
+ })).filter((tab) => tab.fields.length > 0);
1670
+ if (tabs.length > 0) kept.push({
1671
+ ...field,
1672
+ tabs
1673
+ });
1674
+ continue;
1675
+ }
1676
+ if ("perNode" in field && field.perNode === true) continue;
1677
+ kept.push(field);
1678
+ }
1679
+ return kept;
1680
+ }
1563
1681
  function collectPerNodeFieldKeys(fields) {
1564
1682
  const collected = [];
1565
1683
  for (const field of fields) {
@@ -5781,6 +5899,10 @@ var addonSettingsCapability = {
5781
5899
  kind: "mutation",
5782
5900
  auth: "admin"
5783
5901
  }),
5902
+ getIntegrationSettings: method(z.object({
5903
+ addonId: z.string(),
5904
+ nodeId: z.string().optional()
5905
+ }), SettingsSchemaWithValuesSchema.nullable()),
5784
5906
  getDeviceSettings: method(z.object({
5785
5907
  addonId: z.string(),
5786
5908
  deviceId: z.number(),
@@ -32788,6 +32910,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
32788
32910
  addonId: null,
32789
32911
  access: "view"
32790
32912
  },
32913
+ "addonSettings.getIntegrationSettings": {
32914
+ capName: "addon-settings",
32915
+ capScope: "system",
32916
+ addonId: null,
32917
+ access: "view"
32918
+ },
32791
32919
  "addonSettings.updateDeviceSettings": {
32792
32920
  capName: "addon-settings",
32793
32921
  capScope: "system",
@@ -1247,6 +1247,40 @@ var BaseAddon = class {
1247
1247
  deviceSettingsSchema() {
1248
1248
  return null;
1249
1249
  }
1250
+ /**
1251
+ * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
1252
+ * ARE the configuration of its integration.
1253
+ *
1254
+ * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
1255
+ * operator should find on the addon's integration page (System →
1256
+ * Integrations → <name>) rather than only in the cluster-wide list of every
1257
+ * addon. Empty (the default) means the addon has no integration-level
1258
+ * settings and no such surface is offered — this is opt-in, because whether
1259
+ * an addon's configuration IS its integration's configuration depends on the
1260
+ * nature of the integration.
1261
+ *
1262
+ * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
1263
+ * the ONE global schema, in the ONE addon store, written by the ONE
1264
+ * `updateGlobalSettings` path. There is deliberately no
1265
+ * `updateIntegrationSettings`: a second write path is how a surface acquires
1266
+ * a second store key, and this repo has shipped that twice (`btmPath@hub`,
1267
+ * D266). Selecting sections cannot introduce a key that selecting cannot.
1268
+ *
1269
+ * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
1270
+ * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
1271
+ * removed with the reason recorded at
1272
+ * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
1273
+ * determined by WHICH schema it lives in, not by a field-level marker."* A
1274
+ * marker sprinkled across sections also has to borrow a field that already
1275
+ * means something else; borrowing `section.tab` put the literal word
1276
+ * "integration" into an operator-facing tab bar, because `tab` means "how to
1277
+ * GROUP this visually" and cannot also mean "where this lives" (D269
1278
+ * supersedes D268). One declaration, in one place, next to the schema whose
1279
+ * ids it names.
1280
+ */
1281
+ integrationSettingSections() {
1282
+ return [];
1283
+ }
1250
1284
  async getGlobalSettings(overlay, cap, nodeId) {
1251
1285
  const schema = this.globalSettingsSchema(cap);
1252
1286
  if (!schema) return { sections: [] };
@@ -1257,6 +1291,55 @@ var BaseAddon = class {
1257
1291
  } : projected);
1258
1292
  }
1259
1293
  /**
1294
+ * The integration-level view of this addon's settings: exactly the sections
1295
+ * named by {@link integrationSettingSections}, hydrated from the SAME store
1296
+ * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
1297
+ *
1298
+ * Returns `null` when the addon declared nothing — an addon that opts out has
1299
+ * no integration settings surface at all, rather than an empty one that reads
1300
+ * as a failed load.
1301
+ *
1302
+ * Three properties hold BY CONSTRUCTION, which is why they are here in core
1303
+ * and not in whichever UI happens to render this:
1304
+ *
1305
+ * 1. **One key.** The payload is a SUBSET of the global schema, so a field
1306
+ * shown here is the same field, with the same bare key, that the addon's
1307
+ * own page shows. There is no integration-specific writer — callers save
1308
+ * through `updateGlobalSettings` — so a second store key is unreachable,
1309
+ * not merely discouraged.
1310
+ * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
1311
+ * is `<key>@<nodeId>` and an integration is not a node; whichever node
1312
+ * such a field silently picked would be a wrong answer for the operator
1313
+ * who opened the page (D266).
1314
+ * 3. **No silent typo.** A declared id that names no section throws. The
1315
+ * alternative — skip it — turns a rename into a surface that quietly
1316
+ * empties, which looks exactly like an addon with nothing to configure.
1317
+ */
1318
+ async getIntegrationSettings(nodeId) {
1319
+ const declared = this.integrationSettingSections();
1320
+ if (declared.length === 0) return null;
1321
+ const schema = this.globalSettingsSchema();
1322
+ if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
1323
+ const byId = new Map(schema.sections.map((section) => [section.id, section]));
1324
+ const sections = [];
1325
+ for (const id of declared) {
1326
+ const section = byId.get(id);
1327
+ if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
1328
+ const fields = dropPerNodeFields(section.fields);
1329
+ if (fields.length === 0) continue;
1330
+ sections.push({
1331
+ ...section,
1332
+ fields
1333
+ });
1334
+ }
1335
+ if (sections.length === 0) return null;
1336
+ const projected = await this.resolveGlobalStore(nodeId);
1337
+ return hydrateSchema({
1338
+ ...schema,
1339
+ sections
1340
+ }, projected);
1341
+ }
1342
+ /**
1260
1343
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
1261
1344
  * every `perNode: true` field carries THAT node's scoped value on its bare
1262
1345
  * key (absent scoped key ⇒ key absent, so the schema `default` wins — no
@@ -1560,6 +1643,41 @@ var BaseAddon = class {
1560
1643
  * `hydrateSchema` does. Valueless structural fields (separator/info/…)
1561
1644
  * don't declare `perNode` and are excluded by the `in` narrowing.
1562
1645
  */
1646
+ /**
1647
+ * The same fields with every `perNode: true` one removed, recursing into layout
1648
+ * containers exactly as {@link collectPerNodeFieldKeys} does. A container left
1649
+ * with no child is dropped rather than rendered empty.
1650
+ *
1651
+ * Used by `getIntegrationSettings`: an integration is not a node, so a field
1652
+ * whose store key is `<key>@<nodeId>` has no node to belong to there.
1653
+ */
1654
+ function dropPerNodeFields(fields) {
1655
+ const kept = [];
1656
+ for (const field of fields) {
1657
+ if (field.type === "group") {
1658
+ const inner = dropPerNodeFields(field.fields);
1659
+ if (inner.length > 0) kept.push({
1660
+ ...field,
1661
+ fields: inner
1662
+ });
1663
+ continue;
1664
+ }
1665
+ if (field.type === "sub-tabs") {
1666
+ const tabs = field.tabs.map((tab) => ({
1667
+ ...tab,
1668
+ fields: dropPerNodeFields(tab.fields)
1669
+ })).filter((tab) => tab.fields.length > 0);
1670
+ if (tabs.length > 0) kept.push({
1671
+ ...field,
1672
+ tabs
1673
+ });
1674
+ continue;
1675
+ }
1676
+ if ("perNode" in field && field.perNode === true) continue;
1677
+ kept.push(field);
1678
+ }
1679
+ return kept;
1680
+ }
1563
1681
  function collectPerNodeFieldKeys(fields) {
1564
1682
  const collected = [];
1565
1683
  for (const field of fields) {
@@ -5781,6 +5899,10 @@ var addonSettingsCapability = {
5781
5899
  kind: "mutation",
5782
5900
  auth: "admin"
5783
5901
  }),
5902
+ getIntegrationSettings: method(zod.z.object({
5903
+ addonId: zod.z.string(),
5904
+ nodeId: zod.z.string().optional()
5905
+ }), SettingsSchemaWithValuesSchema.nullable()),
5784
5906
  getDeviceSettings: method(zod.z.object({
5785
5907
  addonId: zod.z.string(),
5786
5908
  deviceId: zod.z.number(),
@@ -32788,6 +32910,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
32788
32910
  addonId: null,
32789
32911
  access: "view"
32790
32912
  },
32913
+ "addonSettings.getIntegrationSettings": {
32914
+ capName: "addon-settings",
32915
+ capScope: "system",
32916
+ addonId: null,
32917
+ access: "view"
32918
+ },
32791
32919
  "addonSettings.updateDeviceSettings": {
32792
32920
  capName: "addon-settings",
32793
32921
  capScope: "system",
package/dist/index.js CHANGED
@@ -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-B-mBrEz9.js");
3
+ const require_dist = require("./dist-DdEXjTDo.js");
4
4
  const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
5
5
  require("./builtins/alerts/index.js");
6
6
  const require_formatter = require("./formatter-DqAKDlvN.js");
@@ -26,7 +26,7 @@ require("./builtins/system-config/index.js");
26
26
  const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
27
27
  const require_file_data_plane = require("./file-data-plane-DO8KbxCe.js");
28
28
  const require_tls$1 = require("./tls-u8QCJCFE.js");
29
- const require_manifest_python_deps = require("./manifest-python-deps-BV_Cy99l.js");
29
+ const require_manifest_python_deps = require("./manifest-python-deps-CivtrAha.js");
30
30
  const require_resource_monitor = require("./resource-monitor-CdnzxBLP.js");
31
31
  const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
32
32
  let zod = require("zod");
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { $ as isVoidInput, $t as scopeKey, At as DATAPLANE_SECRET_HEADER$1, Bt as asNumber, C as addonSettingsCapability, G as extractNestedAddonId, Gt as expandCapMethods, Ht as createEvent, It as ReadinessRegistry, J as isArrayOutputSchema, Lt as ReadinessTimeoutError, Mt as DEVICE_STATUS_METHOD, Ot as errMsg$1, Qt as resolveCapMount, Ut as emitDownForOwnedCaps, Vt as asString$1, X as isObjectInput, Xt as parseJsonUnknown$1, Y as isCollectionArrayMethod, Yt as parseJsonObject, Zt as readinessKey, _t as scopesAllowDeviceCap, at as looseSchema, ct as objectInputDeclaresAddonId, dt as procedureAuthKey, et as kebabToCamel, f as LOG_LEVEL_RANK, gt as scopesAllowAddon, it as logLevelAtMost, jt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, m as RUNTIME_DEFAULTS, mt as resolveMethodAuth, p as METHOD_ACCESS_MAP, t as ALL_CAPABILITY_DEFINITIONS, tn as EventCategory$1, tt as lifecycleJobSchema, zt as asJsonObject$1 } from "./dist-BwD83D2f.mjs";
2
+ import { $ as isVoidInput, $t as scopeKey, At as DATAPLANE_SECRET_HEADER$1, Bt as asNumber, C as addonSettingsCapability, G as extractNestedAddonId, Gt as expandCapMethods, Ht as createEvent, It as ReadinessRegistry, J as isArrayOutputSchema, Lt as ReadinessTimeoutError, Mt as DEVICE_STATUS_METHOD, Ot as errMsg$1, Qt as resolveCapMount, Ut as emitDownForOwnedCaps, Vt as asString$1, X as isObjectInput, Xt as parseJsonUnknown$1, Y as isCollectionArrayMethod, Yt as parseJsonObject, Zt as readinessKey, _t as scopesAllowDeviceCap, at as looseSchema, ct as objectInputDeclaresAddonId, dt as procedureAuthKey, et as kebabToCamel, f as LOG_LEVEL_RANK, gt as scopesAllowAddon, it as logLevelAtMost, jt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, m as RUNTIME_DEFAULTS, mt as resolveMethodAuth, p as METHOD_ACCESS_MAP, t as ALL_CAPABILITY_DEFINITIONS, tn as EventCategory$1, tt as lifecycleJobSchema, zt as asJsonObject$1 } from "./dist-CZWAU40-.mjs";
3
3
  import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
4
4
  import "./builtins/alerts/index.mjs";
5
5
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
@@ -25,7 +25,7 @@ import "./builtins/system-config/index.mjs";
25
25
  import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
26
26
  import { a as parseTokenizedUrl, c as collectModelFiles, d as downloadModel, f as ensureModel, h as isModelDownloaded, i as parseRangeHeader, l as deleteModelFromDisk, m as getModelFilePath, n as contentTypeFor, o as resolveFilePath, p as fetchJson, r as createAuthenticatedFileServer, s as ModelDownloadService, t as createFileDataPlaneHandler, u as downloadFile } from "./file-data-plane-BhKdxJgf.mjs";
27
27
  import { C as registerLanHttpHandler, S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, b as bindPendingLanHttp, c as loadTlsCert, d as collectCertIdentity, f as CA_VALIDITY_DAYS, g as evaluateExistingCert, h as SERVER_AUTH_OID, i as writeExtraSans, l as reissueTlsLeaf, m as MAX_LEAF_VALIDITY_DAYS, n as readTlsAccessStatus, o as validateUploadedTls, p as LEAF_RENEWAL_WINDOW_DAYS, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, u as CA_COMMON_NAME, v as allFamiliesListenHost, x as closeLanHttp, y as applyLanHttp } from "./tls-CQhPGSJm.mjs";
28
- import { $ as buildUdsNativeCapProxy, A as createParentUnownedCallHandler, At as RSS_BUDGET_REANNOUNCE_MIN_MS, B as AGENT_CAP_FWD_SERVICE, Bt as shouldReclaim, C as brokerTransportLink, Ct as createAddonDataPlaneFacility, D as HUB_CAP_FWD_ACTION, Dt as HEAP_WATCH_WARN_RATIO, E as localProviderLink, Et as HEAP_WATCH_INTERVAL_MS, F as createUdsLoggerWithControl, Ft as createV8Reclaimer, G as createLocalTransport, H as CapRouteError, Ht as startRunnerHeapWatch, I as LocalChildClient, It as describeRss, J as SocketChannel, K as UdsLocalTransportClient, L as LocalChildRegistry, Lt as hubMainRssBudget, M as createUdsEventBus, Mt as RUNNER_HEAP_WATCH_INTERVAL_MS, N as udsChildLogToWorkerEntry, Nt as RUNNER_RSS_BUDGET_ENV, O as HUB_CAP_FWD_SERVICE, Ot as HUB_MAIN_RSS_BUDGET_MB, P as createUdsLogger, Pt as buildHeapSample, Q as buildNativeCapProxy, R as UDS_NO_ROUTE_PREFIX, Rt as nextRssBudgetState, S as brokerCallForCap, St as runNpm, T as ipcParentLink, Tt as HEAP_RECLAIM_TRIGGER_MB, U as classifyCapRoute, Ut as strandedMb, V as CapRouteResolver, Vt as startHeapWatch, W as callWithServiceDiscovery, X as FrameDecoder, Y as localEndpointPath, Z as encodeFrame, _ as createKernelHwAccel, _t as CapabilityUnavailableError, a as getWorkerDeviceRegistry, at as createAddonService, b as __resetCapUsageRegistryForTests, bt as resolveAddonClass, c as setHubConnected, ct as capActionName, d as getBrokerEventBus, dt as capServiceName, et as createBrokerDeviceManagerApi, f as getMoleculerEventStats, ft as parseCapAction, g as AddonDepsManager, gt as CapabilityHandle, h as subscribePassthrough, ht as DeviceRegistry, i as createUdsAddonContext, j as createUdsEventBridge, jt as RSS_BUDGET_RELEASE_RATIO, k as createHubCapForwardService, kt as HUB_RSS_BUDGET_ENV, l as EVENT_TOPIC_PREFIX, lt as capActionSuffix, m as setNodeEventInterest, mt as serializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as validateProviderRegistrations, p as registerEventBusService, pt as deserializeTypedArrays, q as UdsLocalTransportServer, r as createAddonContext, rt as mountNativeCapService, s as getOrInitReadinessRegistryForClient, st as NATIVE_PROVIDER_SERVICE_INFIX, t as installManifestPythonDeps, u as clusterEventTopic, ut as capBareAction, v as resolveHwAccel, vt as copyBundledNativeModules, w as buildLinkChain, wt as HEAP_RECLAIM_MIN_INTERVAL_MS, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as installManifestNativeDeps, z as AGENT_CAP_FWD_ACTION, zt as parseRssBudgetMb } from "./manifest-python-deps-DpRX9LtD.mjs";
28
+ import { $ as buildUdsNativeCapProxy, A as createParentUnownedCallHandler, At as RSS_BUDGET_REANNOUNCE_MIN_MS, B as AGENT_CAP_FWD_SERVICE, Bt as shouldReclaim, C as brokerTransportLink, Ct as createAddonDataPlaneFacility, D as HUB_CAP_FWD_ACTION, Dt as HEAP_WATCH_WARN_RATIO, E as localProviderLink, Et as HEAP_WATCH_INTERVAL_MS, F as createUdsLoggerWithControl, Ft as createV8Reclaimer, G as createLocalTransport, H as CapRouteError, Ht as startRunnerHeapWatch, I as LocalChildClient, It as describeRss, J as SocketChannel, K as UdsLocalTransportClient, L as LocalChildRegistry, Lt as hubMainRssBudget, M as createUdsEventBus, Mt as RUNNER_HEAP_WATCH_INTERVAL_MS, N as udsChildLogToWorkerEntry, Nt as RUNNER_RSS_BUDGET_ENV, O as HUB_CAP_FWD_SERVICE, Ot as HUB_MAIN_RSS_BUDGET_MB, P as createUdsLogger, Pt as buildHeapSample, Q as buildNativeCapProxy, R as UDS_NO_ROUTE_PREFIX, Rt as nextRssBudgetState, S as brokerCallForCap, St as runNpm, T as ipcParentLink, Tt as HEAP_RECLAIM_TRIGGER_MB, U as classifyCapRoute, Ut as strandedMb, V as CapRouteResolver, Vt as startHeapWatch, W as callWithServiceDiscovery, X as FrameDecoder, Y as localEndpointPath, Z as encodeFrame, _ as createKernelHwAccel, _t as CapabilityUnavailableError, a as getWorkerDeviceRegistry, at as createAddonService, b as __resetCapUsageRegistryForTests, bt as resolveAddonClass, c as setHubConnected, ct as capActionName, d as getBrokerEventBus, dt as capServiceName, et as createBrokerDeviceManagerApi, f as getMoleculerEventStats, ft as parseCapAction, g as AddonDepsManager, gt as CapabilityHandle, h as subscribePassthrough, ht as DeviceRegistry, i as createUdsAddonContext, j as createUdsEventBridge, jt as RSS_BUDGET_RELEASE_RATIO, k as createHubCapForwardService, kt as HUB_RSS_BUDGET_ENV, l as EVENT_TOPIC_PREFIX, lt as capActionSuffix, m as setNodeEventInterest, mt as serializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as validateProviderRegistrations, p as registerEventBusService, pt as deserializeTypedArrays, q as UdsLocalTransportServer, r as createAddonContext, rt as mountNativeCapService, s as getOrInitReadinessRegistryForClient, st as NATIVE_PROVIDER_SERVICE_INFIX, t as installManifestPythonDeps, u as clusterEventTopic, ut as capBareAction, v as resolveHwAccel, vt as copyBundledNativeModules, w as buildLinkChain, wt as HEAP_RECLAIM_MIN_INTERVAL_MS, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as installManifestNativeDeps, z as AGENT_CAP_FWD_ACTION, zt as parseRssBudgetMb } from "./manifest-python-deps-HFh_iE0E.mjs";
29
29
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BWmQ5i-o.mjs";
30
30
  import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
31
31
  import { z } from "zod";
@@ -192,8 +192,8 @@ export interface ReadinessSnapshotResponse {
192
192
  * addon so it can set up its reverse-proxy. Mirrors `routes`.
193
193
  */
194
194
  export type AddonCallTarget = 'routes' | 'custom' | 'settings' | 'data-planes';
195
- /** The four addon-settings methods reachable over the addon-call plane. */
196
- export type AddonSettingsMethod = 'getGlobalSettings' | 'updateGlobalSettings' | 'getDeviceSettings' | 'updateDeviceSettings';
195
+ /** The addon-settings methods reachable over the addon-call plane. */
196
+ export type AddonSettingsMethod = 'getGlobalSettings' | 'updateGlobalSettings' | 'getIntegrationSettings' | 'getDeviceSettings' | 'updateDeviceSettings';
197
197
  /**
198
198
  * Parent → child: invoke an ADDON-LEVEL method on a loaded addon (not a cap).
199
199
  * Carried as a correlated request frame (`channel.request`) — it returns a
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- require("./dist-B-mBrEz9.js");
2
+ require("./dist-DdEXjTDo.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  node_crypto = require_chunk.__toESM(node_crypto);
5
5
  let _camstack_types_node = require("@camstack/types/node");
@@ -1,4 +1,4 @@
1
- import "./dist-BwD83D2f.mjs";
1
+ import "./dist-CZWAU40-.mjs";
2
2
  import * as crypto$1 from "node:crypto";
3
3
  import { randomBytes, randomUUID } from "node:crypto";
4
4
  import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-B-mBrEz9.js");
1
+ const require_dist = require("./dist-DdEXjTDo.js");
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -19,6 +19,22 @@ function settingsStoreIsAuthoritativeHere(env) {
19
19
  /**
20
20
  * The dead keys, per row.
21
21
  *
22
+ * ## Addressing these rows
23
+ *
24
+ * Every namespace below is a FORKED addon, whose `ctx.settings` comes from
25
+ * `addon-context-factory` and writes `<addonId>:addon-settings` row `root`. The
26
+ * scoped table is therefore the right target — unlike the hub builtin whose
27
+ * mis-addressed spec is dissected at {@link RetiredRowSpec}. Confirmed per
28
+ * namespace against `packages/<pkg>/package.json`'s manifest `id`.
29
+ *
30
+ * ## `keys` vs `perNodeKeys` is a data-loss decision, not a style choice
31
+ *
32
+ * `perNodeKeys` matches `<key>` AND `<key>@<nodeId>`. That is right for a key
33
+ * whose per-node copies are all dead (the engine cascade, the decoder
34
+ * `backend`). It is WRONG for `probedBestHwaccel` / `probedBestAudioBackend`,
35
+ * where the bare key is unreachable residue but every `@<nodeId>` copy is the
36
+ * node's live probe result — those go under `keys`, which is exact-match.
37
+ *
22
38
  * `detection-pipeline` / `addon-settings` / `root` — every key in this row is
23
39
  * dead; the row itself is kept because the addon writes live keys into it
24
40
  * (`pipelineTemplates`, `videoPipelineSteps`).
@@ -34,19 +50,79 @@ function settingsStoreIsAuthoritativeHere(env) {
34
50
  * hardware (`resolveAutoEngine`). See
35
51
  * `addon-pipeline/src/detection-pipeline/engine-store-keys.ts`.
36
52
  */
37
- var RETIRED_SETTINGS_KEYS = [{
38
- namespace: "detection-pipeline",
39
- collection: "addon-settings",
40
- row: "root",
41
- keys: ["pipelineSteps", "pipelineEngine"],
42
- perNodeKeys: [
43
- "engineRuntime",
44
- "engineBackend",
45
- "engineDevice",
46
- "probedBestEngine"
47
- ],
48
- reason: "global step/engine seed and the per-node engine cascade — both removed; the live per-camera dispatch path never read either"
49
- }];
53
+ var RETIRED_SETTINGS_KEYS = [
54
+ {
55
+ namespace: "detection-pipeline",
56
+ collection: "addon-settings",
57
+ row: "root",
58
+ keys: ["pipelineSteps", "pipelineEngine"],
59
+ perNodeKeys: [
60
+ "engineRuntime",
61
+ "engineBackend",
62
+ "engineDevice",
63
+ "probedBestEngine"
64
+ ],
65
+ reason: "global step/engine seed and the per-node engine cascade — both removed; the live per-camera dispatch path never read either"
66
+ },
67
+ {
68
+ namespace: "pipeline-orchestrator",
69
+ collection: "addon-settings",
70
+ row: "root",
71
+ keys: [
72
+ "enabledNodes",
73
+ "enabledDecoderNodes",
74
+ "enabledAudioNodes",
75
+ "remoteSourcingNodes",
76
+ "nativeLeaseTtlMs"
77
+ ],
78
+ perNodeKeys: ["backend"],
79
+ reason: "the pre-singleton \"enabled nodes\" role model (node capability now lives per node in agent-settings and is derived by NodeTopology.refresh, never read from this row), `nativeLeaseTtlMs` superseded by `nativeLeaseActivityMs`, and the decoder `backend` selector whose resolver returns DEFAULT_DECODER_BACKEND unconditionally (decoder-backend.ts)"
80
+ },
81
+ {
82
+ namespace: "stream-broker",
83
+ collection: "addon-settings",
84
+ row: "root",
85
+ keys: [
86
+ "maxDecodeFps",
87
+ "initialReconnectDelayMs",
88
+ "maxReconnectDelayMs"
89
+ ],
90
+ perNodeKeys: [],
91
+ reason: "three form knobs deleted 2026-08-28 (D272): nothing capped decode fps, and the reconnect ladder has always run on the INITIAL/MAX_RECONNECT_DELAY_MS constants"
92
+ },
93
+ {
94
+ namespace: "embedding-encoder",
95
+ collection: "addon-settings",
96
+ row: "root",
97
+ keys: ["runtime", "backend"],
98
+ perNodeKeys: [],
99
+ reason: "two selects deleted 2026-08-28 (D272); their only reference was a withTags log field, and the CUDA/CoreML options named acceleration that has never existed — embedding inference is always ONNX in the embedded Python"
100
+ },
101
+ {
102
+ namespace: "audio-analyzer",
103
+ collection: "addon-settings",
104
+ row: "root",
105
+ keys: ["selectedAudioModel", "probedBestAudioBackend"],
106
+ perNodeKeys: [],
107
+ reason: "`selectedAudioModel` deleted 2026-08-28 (D272) — createAudioPipeline takes a backend and no model id, so it never reached inference; the BARE `probedBestAudioBackend` is pre-perNode residue that projectStore already drops (no bare fallback), so no node can ever read it"
108
+ },
109
+ {
110
+ namespace: "decoder-nodeav",
111
+ collection: "addon-settings",
112
+ row: "root",
113
+ keys: ["probedBestHwaccel"],
114
+ perNodeKeys: [],
115
+ reason: "bare pre-perNode residue — the live probe result is `probedBestHwaccel@<nodeId>` and `projectStore` deliberately drops the bare key, so nothing can read it"
116
+ },
117
+ {
118
+ namespace: "pipeline-analytics",
119
+ collection: "addon-settings",
120
+ row: "root",
121
+ keys: ["mediaAttachPolicy"],
122
+ perNodeKeys: [],
123
+ reason: "deleted 2026-08-28 (D272) — absent from MediaSettingsSchema, so the value was never even resolved, let alone read"
124
+ }
125
+ ];
50
126
  /**
51
127
  * The device-manager's three fleet blobs, replaced by one row per device in
52
128
  * `device-manager:devices` (`device-row-store.ts`).
@@ -1,4 +1,4 @@
1
- import { zt as asJsonObject } from "./dist-BwD83D2f.mjs";
1
+ import { zt as asJsonObject } from "./dist-CZWAU40-.mjs";
2
2
  //#region src/builtins/sqlite-storage/retired-settings-keys.ts
3
3
  /**
4
4
  * Is THIS node the one whose settings store is the cluster's authority?
@@ -19,6 +19,22 @@ function settingsStoreIsAuthoritativeHere(env) {
19
19
  /**
20
20
  * The dead keys, per row.
21
21
  *
22
+ * ## Addressing these rows
23
+ *
24
+ * Every namespace below is a FORKED addon, whose `ctx.settings` comes from
25
+ * `addon-context-factory` and writes `<addonId>:addon-settings` row `root`. The
26
+ * scoped table is therefore the right target — unlike the hub builtin whose
27
+ * mis-addressed spec is dissected at {@link RetiredRowSpec}. Confirmed per
28
+ * namespace against `packages/<pkg>/package.json`'s manifest `id`.
29
+ *
30
+ * ## `keys` vs `perNodeKeys` is a data-loss decision, not a style choice
31
+ *
32
+ * `perNodeKeys` matches `<key>` AND `<key>@<nodeId>`. That is right for a key
33
+ * whose per-node copies are all dead (the engine cascade, the decoder
34
+ * `backend`). It is WRONG for `probedBestHwaccel` / `probedBestAudioBackend`,
35
+ * where the bare key is unreachable residue but every `@<nodeId>` copy is the
36
+ * node's live probe result — those go under `keys`, which is exact-match.
37
+ *
22
38
  * `detection-pipeline` / `addon-settings` / `root` — every key in this row is
23
39
  * dead; the row itself is kept because the addon writes live keys into it
24
40
  * (`pipelineTemplates`, `videoPipelineSteps`).
@@ -34,19 +50,79 @@ function settingsStoreIsAuthoritativeHere(env) {
34
50
  * hardware (`resolveAutoEngine`). See
35
51
  * `addon-pipeline/src/detection-pipeline/engine-store-keys.ts`.
36
52
  */
37
- var RETIRED_SETTINGS_KEYS = [{
38
- namespace: "detection-pipeline",
39
- collection: "addon-settings",
40
- row: "root",
41
- keys: ["pipelineSteps", "pipelineEngine"],
42
- perNodeKeys: [
43
- "engineRuntime",
44
- "engineBackend",
45
- "engineDevice",
46
- "probedBestEngine"
47
- ],
48
- reason: "global step/engine seed and the per-node engine cascade — both removed; the live per-camera dispatch path never read either"
49
- }];
53
+ var RETIRED_SETTINGS_KEYS = [
54
+ {
55
+ namespace: "detection-pipeline",
56
+ collection: "addon-settings",
57
+ row: "root",
58
+ keys: ["pipelineSteps", "pipelineEngine"],
59
+ perNodeKeys: [
60
+ "engineRuntime",
61
+ "engineBackend",
62
+ "engineDevice",
63
+ "probedBestEngine"
64
+ ],
65
+ reason: "global step/engine seed and the per-node engine cascade — both removed; the live per-camera dispatch path never read either"
66
+ },
67
+ {
68
+ namespace: "pipeline-orchestrator",
69
+ collection: "addon-settings",
70
+ row: "root",
71
+ keys: [
72
+ "enabledNodes",
73
+ "enabledDecoderNodes",
74
+ "enabledAudioNodes",
75
+ "remoteSourcingNodes",
76
+ "nativeLeaseTtlMs"
77
+ ],
78
+ perNodeKeys: ["backend"],
79
+ reason: "the pre-singleton \"enabled nodes\" role model (node capability now lives per node in agent-settings and is derived by NodeTopology.refresh, never read from this row), `nativeLeaseTtlMs` superseded by `nativeLeaseActivityMs`, and the decoder `backend` selector whose resolver returns DEFAULT_DECODER_BACKEND unconditionally (decoder-backend.ts)"
80
+ },
81
+ {
82
+ namespace: "stream-broker",
83
+ collection: "addon-settings",
84
+ row: "root",
85
+ keys: [
86
+ "maxDecodeFps",
87
+ "initialReconnectDelayMs",
88
+ "maxReconnectDelayMs"
89
+ ],
90
+ perNodeKeys: [],
91
+ reason: "three form knobs deleted 2026-08-28 (D272): nothing capped decode fps, and the reconnect ladder has always run on the INITIAL/MAX_RECONNECT_DELAY_MS constants"
92
+ },
93
+ {
94
+ namespace: "embedding-encoder",
95
+ collection: "addon-settings",
96
+ row: "root",
97
+ keys: ["runtime", "backend"],
98
+ perNodeKeys: [],
99
+ reason: "two selects deleted 2026-08-28 (D272); their only reference was a withTags log field, and the CUDA/CoreML options named acceleration that has never existed — embedding inference is always ONNX in the embedded Python"
100
+ },
101
+ {
102
+ namespace: "audio-analyzer",
103
+ collection: "addon-settings",
104
+ row: "root",
105
+ keys: ["selectedAudioModel", "probedBestAudioBackend"],
106
+ perNodeKeys: [],
107
+ reason: "`selectedAudioModel` deleted 2026-08-28 (D272) — createAudioPipeline takes a backend and no model id, so it never reached inference; the BARE `probedBestAudioBackend` is pre-perNode residue that projectStore already drops (no bare fallback), so no node can ever read it"
108
+ },
109
+ {
110
+ namespace: "decoder-nodeav",
111
+ collection: "addon-settings",
112
+ row: "root",
113
+ keys: ["probedBestHwaccel"],
114
+ perNodeKeys: [],
115
+ reason: "bare pre-perNode residue — the live probe result is `probedBestHwaccel@<nodeId>` and `projectStore` deliberately drops the bare key, so nothing can read it"
116
+ },
117
+ {
118
+ namespace: "pipeline-analytics",
119
+ collection: "addon-settings",
120
+ row: "root",
121
+ keys: ["mediaAttachPolicy"],
122
+ perNodeKeys: [],
123
+ reason: "deleted 2026-08-28 (D272) — absent from MediaSettingsSchema, so the value was never even resolved, let alone read"
124
+ }
125
+ ];
50
126
  /**
51
127
  * The device-manager's three fleet blobs, replaced by one row per device in
52
128
  * `device-manager:devices` (`device-row-store.ts`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.148",
3
+ "version": "1.2.149",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",