@camstack/system 1.2.166 → 1.2.168

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 +1 -1
  2. package/dist/addon-runner.mjs +1 -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 +97 -31
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +97 -31
  17. package/dist/builtins/device-manager/device-meta-actions.d.ts +36 -15
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  20. package/dist/builtins/hub-forwarder/index.js +1 -1
  21. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  26. package/dist/builtins/local-network/local-network.addon.js +1 -1
  27. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  28. package/dist/builtins/loki-logging/index.js +1 -1
  29. package/dist/builtins/loki-logging/index.mjs +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  32. package/dist/builtins/platform-probe/index.js +1 -1
  33. package/dist/builtins/platform-probe/index.mjs +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  36. package/dist/builtins/snapshot/index.js +1 -1
  37. package/dist/builtins/snapshot/index.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  40. package/dist/builtins/sqlite-storage/retired-collections.d.ts +9 -0
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  45. package/dist/builtins/system-config/system-config.addon.js +1 -1
  46. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  47. package/dist/builtins/winston-logging/index.js +1 -1
  48. package/dist/builtins/winston-logging/index.mjs +1 -1
  49. package/dist/{dist-CFTKQGym.js → dist-DCdtLXgx.js} +28 -75
  50. package/dist/{dist-Dw9cm1NS.mjs → dist-DwaeuknI.mjs} +28 -75
  51. package/dist/index.js +2 -2
  52. package/dist/index.mjs +2 -2
  53. package/dist/{manifest-system-deps-BIxY1Y5e.js → manifest-system-deps-Bi7oyXcN.js} +1 -1
  54. package/dist/{manifest-system-deps-DTz_zNQf.mjs → manifest-system-deps-D52_8SN1.mjs} +1 -1
  55. package/dist/{retired-settings-keys-diW1NmyP.mjs → retired-settings-keys-CB3wWpuC.mjs} +1 -1
  56. package/dist/{retired-settings-keys-dehyu2N3.js → retired-settings-keys-DobfRRgq.js} +1 -1
  57. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_system_deps = require("./manifest-system-deps-BIxY1Y5e.js");
2
+ const require_manifest_system_deps = require("./manifest-system-deps-Bi7oyXcN.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);
@@ -1,4 +1,4 @@
1
- import { Ct as resolveAddonClass, N as createUdsLoggerWithControl, P as LocalChildClient, Rt as RUNNER_RSS_BUDGET_ENV, St as installManifestNativeDeps, Zt as startRunnerHeapWatch, a as createUdsAddonContext, at as getWorkerNativeCapSnapshot, it as getWorkerNativeCapProvider, lt as validateProviderRegistrations, n as installManifestPythonDeps, nn as emitHeapDiagnosticReport, st as setWorkerNativeCapsChangeListener, t as installManifestSystemDeps } from "./manifest-system-deps-DTz_zNQf.mjs";
1
+ import { Ct as resolveAddonClass, N as createUdsLoggerWithControl, P as LocalChildClient, Rt as RUNNER_RSS_BUDGET_ENV, St as installManifestNativeDeps, Zt as startRunnerHeapWatch, a as createUdsAddonContext, at as getWorkerNativeCapSnapshot, it as getWorkerNativeCapProvider, lt as validateProviderRegistrations, n as installManifestPythonDeps, nn as emitHeapDiagnosticReport, st as setWorkerNativeCapsChangeListener, t as installManifestSystemDeps } from "./manifest-system-deps-D52_8SN1.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";
@@ -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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { Mt as errMsg, Nt as BaseAddon, T as addonPagesCapability, an as EventCategory } from "../../dist-Dw9cm1NS.mjs";
1
+ import { Mt as errMsg, Nt as BaseAddon, T as addonPagesCapability, an as EventCategory } from "../../dist-DwaeuknI.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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { D as addonWidgetsCapability, Mt as errMsg, Nt as BaseAddon, an as EventCategory } from "../../dist-Dw9cm1NS.mjs";
1
+ import { D as addonWidgetsCapability, Mt as errMsg, Nt as BaseAddon, an as EventCategory } from "../../dist-DwaeuknI.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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { Kt as createEvent, Mt as errMsg, Nt as BaseAddon, O as alertsCapability, an as EventCategory, n as AlertSchema } from "../../dist-Dw9cm1NS.mjs";
1
+ import { Kt as createEvent, Mt as errMsg, Nt as BaseAddon, O as alertsCapability, an as EventCategory, n as AlertSchema } from "../../dist-DwaeuknI.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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { A as backupCapability, Nt as BaseAddon, an as EventCategory } from "../../dist-Dw9cm1NS.mjs";
1
+ import { A as backupCapability, Nt as BaseAddon, an as EventCategory } from "../../dist-DwaeuknI.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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { Nt as BaseAddon, st as logDestinationCapability } from "../../dist-Dw9cm1NS.mjs";
1
+ import { Nt as BaseAddon, st as logDestinationCapability } from "../../dist-DwaeuknI.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-CFTKQGym.js");
3
+ const require_dist = require("../../dist-DCdtLXgx.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 { F as coreBlockAddonId, I as coreBlockIdFromAddonId, L as coreBlocksCapability, Mt as errMsg, Nt as BaseAddon, c as CoreBlockSchema, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, u as DeclaredDevices } from "../../dist-Dw9cm1NS.mjs";
1
+ import { F as coreBlockAddonId, I as coreBlockIdFromAddonId, L as coreBlocksCapability, Mt as errMsg, Nt as BaseAddon, c as CoreBlockSchema, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, u as DeclaredDevices } from "../../dist-DwaeuknI.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-CFTKQGym.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-dehyu2N3.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-DobfRRgq.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
@@ -3611,21 +3611,27 @@ async function collectDescendants(metaStore, rootId) {
3611
3611
  * `setName`; consumers subscribe to `DeviceMetaChanged` and
3612
3612
  * filter on `field: 'location'`.
3613
3613
  *
3614
- * CASCADE (operator decision, 2026-08-26 supersedes the read-time
3615
- * inheritance `device-linked-devices.ts` used to resolve): when `deviceId`
3616
- * is a `DeviceType.Container`, the SAME location is written to every
3617
- * descendant's own `location` column, recursively, in this exact call —
3618
- * never resolved live by a reader again. This DELIBERATELY overwrites a
3619
- * descendant's own, possibly different, location: a cascade is a write, and
3620
- * a write that stops at an existing value is not a cascade — the operator
3621
- * setting the container's location is understood as organising the whole
3622
- * subtree by it. Only a CONTAINER cascades; a non-container device's
3623
- * location write is a single row, same as before.
3614
+ * CASCADE (operator decision, 2026-08-26; widened to EVERY parent 2026-09-01):
3615
+ * supersedes the read-time inheritance `device-linked-devices.ts` used to
3616
+ * resolve. The SAME location is written to every descendant's own `location`
3617
+ * column, recursively, in this exact call — never resolved live by a reader
3618
+ * again. This DELIBERATELY overwrites a descendant's own, possibly different,
3619
+ * location: a cascade is a write, and a write that stops at an existing value
3620
+ * is not a cascade — the operator setting the parent's location is understood
3621
+ * as organising the whole subtree by it.
3624
3622
  *
3625
- * RETROACTIVITY: this only runs when `setLocation` is called. A container
3626
- * that already had a location before this shipped does NOT retroactively
3627
- * align its descendants they stay unaligned until the operator re-saves
3628
- * the container's location.
3623
+ * NO TYPE PREDICATE. The cascade used to fire only for `DeviceType.Container`,
3624
+ * which split the fleet by an accident of provider modelling: on the measured
3625
+ * hub, 50 containers cascaded to 440 children while 13 non-container parents
3626
+ * (11 `camera`, 2 `hub`) left 20 accessories behind. The Locations page tells
3627
+ * the operator "assign the parent, the accessories follow"; a predicate here
3628
+ * would make that a lie for exactly the cameras. `collectDescendants` is
3629
+ * type-agnostic, and so is this.
3630
+ *
3631
+ * RETROACTIVITY: this only runs when `setLocation` is called. A parent that
3632
+ * already had a location before this shipped does NOT retroactively align its
3633
+ * descendants — they stay unaligned until the operator re-saves the parent's
3634
+ * location.
3629
3635
  *
3630
3636
  * ATOMICITY: the whole cascade runs inside ONE `withMetaWriteLock` critical
3631
3637
  * section, so no concurrent writer can interleave a read or a write into the
@@ -3634,7 +3640,7 @@ async function collectDescendants(metaStore, rootId) {
3634
3640
  * not a silent one): it is collected as a failure, every other descendant is
3635
3641
  * still attempted, and if any failed the outcome is logged at `error` with
3636
3642
  * every failing id — never indistinguishable from a clean cascade. A
3637
- * `DeviceMetaChanged` event fires for the container itself and for every
3643
+ * `DeviceMetaChanged` event fires for the parent itself and for every
3638
3644
  * descendant actually written (never for one that failed), so linked-device
3639
3645
  * consumers (auto-link) see exactly what landed.
3640
3646
  */
@@ -3643,20 +3649,17 @@ async function setLocation(pctx, input) {
3643
3649
  const cascaded = [];
3644
3650
  const failed = [];
3645
3651
  await pctx.metaStore.withMetaWriteLock(async () => {
3646
- const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
3647
- if (!persisted) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
3652
+ if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
3648
3653
  await pctx.metaStore.rows.patch(deviceId, { location });
3649
- if (persisted.meta.type === require_dist.DeviceType.Container) {
3650
- const descendants = await collectDescendants(pctx.metaStore, deviceId);
3651
- for (const descendant of descendants) try {
3652
- await pctx.metaStore.rows.patch(descendant.id, { location });
3653
- cascaded.push(descendant.id);
3654
- } catch (err) {
3655
- failed.push({
3656
- id: descendant.id,
3657
- error: err
3658
- });
3659
- }
3654
+ const descendants = await collectDescendants(pctx.metaStore, deviceId);
3655
+ for (const descendant of descendants) try {
3656
+ await pctx.metaStore.rows.patch(descendant.id, { location });
3657
+ cascaded.push(descendant.id);
3658
+ } catch (err) {
3659
+ failed.push({
3660
+ id: descendant.id,
3661
+ error: err
3662
+ });
3660
3663
  }
3661
3664
  });
3662
3665
  pctx.host.ctx.eventBus.emit({
@@ -3687,7 +3690,7 @@ async function setLocation(pctx, input) {
3687
3690
  value: location
3688
3691
  }
3689
3692
  });
3690
- if (cascaded.length > 0 || failed.length > 0) pctx.host.ctx.logger.info("setLocation: cascaded location to container descendants", {
3693
+ if (cascaded.length > 0 || failed.length > 0) pctx.host.ctx.logger.info("setLocation: cascaded location to descendants", {
3691
3694
  tags: { deviceId },
3692
3695
  meta: {
3693
3696
  location,
@@ -4136,6 +4139,68 @@ async function addLocation(pctx, input) {
4136
4139
  * label. Cascade only matches case-insensitively + trimmed, same
4137
4140
  * as the registry equality check.
4138
4141
  */
4142
+ /**
4143
+ * Rename a room in ONE server operation: the registry label in place, then
4144
+ * every device that carried it.
4145
+ *
4146
+ * The lock order is the one `removeLocation` documents and must not be
4147
+ * "simplified": the registry edit takes the addon-store lock and RELEASES it
4148
+ * before the cascade takes the meta lock. Holding both, in that order, is the
4149
+ * reverse of `allocateNextDeviceId`'s meta ⊃ addon-store nesting — the pair
4150
+ * that deadlocks.
4151
+ *
4152
+ * Validation happens BEFORE either lock. A rename that throws halfway is the
4153
+ * exact failure this function exists to remove, so an empty target must be
4154
+ * refused while nothing has been written yet.
4155
+ */
4156
+ async function renameLocation(pctx, input) {
4157
+ const from = input.from.trim();
4158
+ const to = input.to.trim();
4159
+ if (from.length === 0) throw new Error("[device-manager] renameLocation: `from` must be non-empty");
4160
+ if (to.length === 0) throw new Error("[device-manager] renameLocation: `to` must be non-empty");
4161
+ const fromKey = from.toLowerCase();
4162
+ const toKey = to.toLowerCase();
4163
+ await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
4164
+ const current = (await pctx.metaStore.readStore()).locations ?? [];
4165
+ if (!current.some((l) => l.trim().toLowerCase() === fromKey)) return;
4166
+ const kept = current.filter((l) => {
4167
+ const key = l.trim().toLowerCase();
4168
+ return key !== fromKey && key !== toKey;
4169
+ });
4170
+ await pctx.settings.writeAddonStore({ locations: [...kept, to] });
4171
+ });
4172
+ const moved = await pctx.metaStore.withMetaWriteLock(async () => {
4173
+ const out = [];
4174
+ for (const row of await pctx.metaStore.rows.listAll()) {
4175
+ const location = row.meta.location;
4176
+ if (typeof location !== "string") continue;
4177
+ if (location.trim().toLowerCase() !== fromKey) continue;
4178
+ await pctx.metaStore.rows.patch(row.meta.id, { location: to });
4179
+ out.push(row.meta.id);
4180
+ }
4181
+ return out;
4182
+ });
4183
+ for (const deviceId of moved) pctx.host.ctx.eventBus.emit({
4184
+ id: (0, node_crypto.randomUUID)(),
4185
+ timestamp: /* @__PURE__ */ new Date(),
4186
+ source: {
4187
+ type: "device",
4188
+ id: deviceId
4189
+ },
4190
+ category: require_dist.EventCategory.DeviceMetaChanged,
4191
+ data: {
4192
+ deviceId,
4193
+ field: "location",
4194
+ value: to
4195
+ }
4196
+ });
4197
+ pctx.host.ctx.logger.info("renameLocation: room renamed", { meta: {
4198
+ from,
4199
+ to,
4200
+ moved: moved.length
4201
+ } });
4202
+ return { moved: moved.length };
4203
+ }
4139
4204
  async function removeLocation(pctx, input) {
4140
4205
  const trimmed = input.name.trim();
4141
4206
  if (trimmed.length === 0) return;
@@ -5829,6 +5894,7 @@ var DeviceManagerAddon = class extends require_dist.BaseAddon {
5829
5894
  listLocations: () => listLocations(pctx),
5830
5895
  addLocation: (input) => addLocation(pctx, input),
5831
5896
  removeLocation: (input) => removeLocation(pctx, input),
5897
+ renameLocation: (input) => renameLocation(pctx, input),
5832
5898
  listPersistedByAddon: (input) => listPersistedByAddon(pctx, input),
5833
5899
  listAll: (input) => listAll(pctx, input),
5834
5900
  getDevice: (input) => getDevice(pctx, input),
@@ -1,5 +1,5 @@
1
- import { H as deviceStateCapability, Ht as WELL_KNOWN_TAB_MAP, K as enumerateItemArrayFields, Lt as DeviceFeature, Mt as errMsg, N as buildStreamParamsConfigSchema, Nt as BaseAddon, Rt as DeviceRole, U as deviceStatusCapability, V as deviceManagerCapability, Zt as isDeviceConfigCap, a as CAP_NAMES_WITH_STATUS, an as EventCategory, d as DeviceStatusSchema, dt as normalizeUnit, in as sleep, pt as parseStreamParamsFormPatch, q as enumerateSchemaFields, t as ALL_CAPABILITY_DEFINITIONS, y as STREAM_PROFILE_META, yt as runtimeStatePolicyFor, zt as DeviceType } from "../../dist-Dw9cm1NS.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-diW1NmyP.mjs";
1
+ import { H as deviceStateCapability, Ht as WELL_KNOWN_TAB_MAP, K as enumerateItemArrayFields, Lt as DeviceFeature, Mt as errMsg, N as buildStreamParamsConfigSchema, Nt as BaseAddon, Rt as DeviceRole, U as deviceStatusCapability, V as deviceManagerCapability, Zt as isDeviceConfigCap, a as CAP_NAMES_WITH_STATUS, an as EventCategory, d as DeviceStatusSchema, dt as normalizeUnit, in as sleep, pt as parseStreamParamsFormPatch, q as enumerateSchemaFields, t as ALL_CAPABILITY_DEFINITIONS, y as STREAM_PROFILE_META, yt as runtimeStatePolicyFor, zt as DeviceType } from "../../dist-DwaeuknI.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-CB3wWpuC.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
@@ -3606,21 +3606,27 @@ async function collectDescendants(metaStore, rootId) {
3606
3606
  * `setName`; consumers subscribe to `DeviceMetaChanged` and
3607
3607
  * filter on `field: 'location'`.
3608
3608
  *
3609
- * CASCADE (operator decision, 2026-08-26 supersedes the read-time
3610
- * inheritance `device-linked-devices.ts` used to resolve): when `deviceId`
3611
- * is a `DeviceType.Container`, the SAME location is written to every
3612
- * descendant's own `location` column, recursively, in this exact call —
3613
- * never resolved live by a reader again. This DELIBERATELY overwrites a
3614
- * descendant's own, possibly different, location: a cascade is a write, and
3615
- * a write that stops at an existing value is not a cascade — the operator
3616
- * setting the container's location is understood as organising the whole
3617
- * subtree by it. Only a CONTAINER cascades; a non-container device's
3618
- * location write is a single row, same as before.
3609
+ * CASCADE (operator decision, 2026-08-26; widened to EVERY parent 2026-09-01):
3610
+ * supersedes the read-time inheritance `device-linked-devices.ts` used to
3611
+ * resolve. The SAME location is written to every descendant's own `location`
3612
+ * column, recursively, in this exact call — never resolved live by a reader
3613
+ * again. This DELIBERATELY overwrites a descendant's own, possibly different,
3614
+ * location: a cascade is a write, and a write that stops at an existing value
3615
+ * is not a cascade — the operator setting the parent's location is understood
3616
+ * as organising the whole subtree by it.
3619
3617
  *
3620
- * RETROACTIVITY: this only runs when `setLocation` is called. A container
3621
- * that already had a location before this shipped does NOT retroactively
3622
- * align its descendants they stay unaligned until the operator re-saves
3623
- * the container's location.
3618
+ * NO TYPE PREDICATE. The cascade used to fire only for `DeviceType.Container`,
3619
+ * which split the fleet by an accident of provider modelling: on the measured
3620
+ * hub, 50 containers cascaded to 440 children while 13 non-container parents
3621
+ * (11 `camera`, 2 `hub`) left 20 accessories behind. The Locations page tells
3622
+ * the operator "assign the parent, the accessories follow"; a predicate here
3623
+ * would make that a lie for exactly the cameras. `collectDescendants` is
3624
+ * type-agnostic, and so is this.
3625
+ *
3626
+ * RETROACTIVITY: this only runs when `setLocation` is called. A parent that
3627
+ * already had a location before this shipped does NOT retroactively align its
3628
+ * descendants — they stay unaligned until the operator re-saves the parent's
3629
+ * location.
3624
3630
  *
3625
3631
  * ATOMICITY: the whole cascade runs inside ONE `withMetaWriteLock` critical
3626
3632
  * section, so no concurrent writer can interleave a read or a write into the
@@ -3629,7 +3635,7 @@ async function collectDescendants(metaStore, rootId) {
3629
3635
  * not a silent one): it is collected as a failure, every other descendant is
3630
3636
  * still attempted, and if any failed the outcome is logged at `error` with
3631
3637
  * every failing id — never indistinguishable from a clean cascade. A
3632
- * `DeviceMetaChanged` event fires for the container itself and for every
3638
+ * `DeviceMetaChanged` event fires for the parent itself and for every
3633
3639
  * descendant actually written (never for one that failed), so linked-device
3634
3640
  * consumers (auto-link) see exactly what landed.
3635
3641
  */
@@ -3638,20 +3644,17 @@ async function setLocation(pctx, input) {
3638
3644
  const cascaded = [];
3639
3645
  const failed = [];
3640
3646
  await pctx.metaStore.withMetaWriteLock(async () => {
3641
- const persisted = await pctx.metaStore.resolvePersistedById(deviceId);
3642
- if (!persisted) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
3647
+ if (!await pctx.metaStore.resolvePersistedById(deviceId)) throw new Error(`[device-manager] setLocation: unknown device id=${deviceId}`);
3643
3648
  await pctx.metaStore.rows.patch(deviceId, { location });
3644
- if (persisted.meta.type === DeviceType.Container) {
3645
- const descendants = await collectDescendants(pctx.metaStore, deviceId);
3646
- for (const descendant of descendants) try {
3647
- await pctx.metaStore.rows.patch(descendant.id, { location });
3648
- cascaded.push(descendant.id);
3649
- } catch (err) {
3650
- failed.push({
3651
- id: descendant.id,
3652
- error: err
3653
- });
3654
- }
3649
+ const descendants = await collectDescendants(pctx.metaStore, deviceId);
3650
+ for (const descendant of descendants) try {
3651
+ await pctx.metaStore.rows.patch(descendant.id, { location });
3652
+ cascaded.push(descendant.id);
3653
+ } catch (err) {
3654
+ failed.push({
3655
+ id: descendant.id,
3656
+ error: err
3657
+ });
3655
3658
  }
3656
3659
  });
3657
3660
  pctx.host.ctx.eventBus.emit({
@@ -3682,7 +3685,7 @@ async function setLocation(pctx, input) {
3682
3685
  value: location
3683
3686
  }
3684
3687
  });
3685
- if (cascaded.length > 0 || failed.length > 0) pctx.host.ctx.logger.info("setLocation: cascaded location to container descendants", {
3688
+ if (cascaded.length > 0 || failed.length > 0) pctx.host.ctx.logger.info("setLocation: cascaded location to descendants", {
3686
3689
  tags: { deviceId },
3687
3690
  meta: {
3688
3691
  location,
@@ -4131,6 +4134,68 @@ async function addLocation(pctx, input) {
4131
4134
  * label. Cascade only matches case-insensitively + trimmed, same
4132
4135
  * as the registry equality check.
4133
4136
  */
4137
+ /**
4138
+ * Rename a room in ONE server operation: the registry label in place, then
4139
+ * every device that carried it.
4140
+ *
4141
+ * The lock order is the one `removeLocation` documents and must not be
4142
+ * "simplified": the registry edit takes the addon-store lock and RELEASES it
4143
+ * before the cascade takes the meta lock. Holding both, in that order, is the
4144
+ * reverse of `allocateNextDeviceId`'s meta ⊃ addon-store nesting — the pair
4145
+ * that deadlocks.
4146
+ *
4147
+ * Validation happens BEFORE either lock. A rename that throws halfway is the
4148
+ * exact failure this function exists to remove, so an empty target must be
4149
+ * refused while nothing has been written yet.
4150
+ */
4151
+ async function renameLocation(pctx, input) {
4152
+ const from = input.from.trim();
4153
+ const to = input.to.trim();
4154
+ if (from.length === 0) throw new Error("[device-manager] renameLocation: `from` must be non-empty");
4155
+ if (to.length === 0) throw new Error("[device-manager] renameLocation: `to` must be non-empty");
4156
+ const fromKey = from.toLowerCase();
4157
+ const toKey = to.toLowerCase();
4158
+ await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
4159
+ const current = (await pctx.metaStore.readStore()).locations ?? [];
4160
+ if (!current.some((l) => l.trim().toLowerCase() === fromKey)) return;
4161
+ const kept = current.filter((l) => {
4162
+ const key = l.trim().toLowerCase();
4163
+ return key !== fromKey && key !== toKey;
4164
+ });
4165
+ await pctx.settings.writeAddonStore({ locations: [...kept, to] });
4166
+ });
4167
+ const moved = await pctx.metaStore.withMetaWriteLock(async () => {
4168
+ const out = [];
4169
+ for (const row of await pctx.metaStore.rows.listAll()) {
4170
+ const location = row.meta.location;
4171
+ if (typeof location !== "string") continue;
4172
+ if (location.trim().toLowerCase() !== fromKey) continue;
4173
+ await pctx.metaStore.rows.patch(row.meta.id, { location: to });
4174
+ out.push(row.meta.id);
4175
+ }
4176
+ return out;
4177
+ });
4178
+ for (const deviceId of moved) pctx.host.ctx.eventBus.emit({
4179
+ id: randomUUID(),
4180
+ timestamp: /* @__PURE__ */ new Date(),
4181
+ source: {
4182
+ type: "device",
4183
+ id: deviceId
4184
+ },
4185
+ category: EventCategory.DeviceMetaChanged,
4186
+ data: {
4187
+ deviceId,
4188
+ field: "location",
4189
+ value: to
4190
+ }
4191
+ });
4192
+ pctx.host.ctx.logger.info("renameLocation: room renamed", { meta: {
4193
+ from,
4194
+ to,
4195
+ moved: moved.length
4196
+ } });
4197
+ return { moved: moved.length };
4198
+ }
4134
4199
  async function removeLocation(pctx, input) {
4135
4200
  const trimmed = input.name.trim();
4136
4201
  if (trimmed.length === 0) return;
@@ -5824,6 +5889,7 @@ var DeviceManagerAddon = class extends BaseAddon {
5824
5889
  listLocations: () => listLocations(pctx),
5825
5890
  addLocation: (input) => addLocation(pctx, input),
5826
5891
  removeLocation: (input) => removeLocation(pctx, input),
5892
+ renameLocation: (input) => renameLocation(pctx, input),
5827
5893
  listPersistedByAddon: (input) => listPersistedByAddon(pctx, input),
5828
5894
  listAll: (input) => listAll(pctx, input),
5829
5895
  getDevice: (input) => getDevice(pctx, input),
@@ -44,21 +44,27 @@ export declare function setName(pctx: ProviderContext, input: Parameters<IDevice
44
44
  * `setName`; consumers subscribe to `DeviceMetaChanged` and
45
45
  * filter on `field: 'location'`.
46
46
  *
47
- * CASCADE (operator decision, 2026-08-26 supersedes the read-time
48
- * inheritance `device-linked-devices.ts` used to resolve): when `deviceId`
49
- * is a `DeviceType.Container`, the SAME location is written to every
50
- * descendant's own `location` column, recursively, in this exact call —
51
- * never resolved live by a reader again. This DELIBERATELY overwrites a
52
- * descendant's own, possibly different, location: a cascade is a write, and
53
- * a write that stops at an existing value is not a cascade — the operator
54
- * setting the container's location is understood as organising the whole
55
- * subtree by it. Only a CONTAINER cascades; a non-container device's
56
- * location write is a single row, same as before.
47
+ * CASCADE (operator decision, 2026-08-26; widened to EVERY parent 2026-09-01):
48
+ * supersedes the read-time inheritance `device-linked-devices.ts` used to
49
+ * resolve. The SAME location is written to every descendant's own `location`
50
+ * column, recursively, in this exact call — never resolved live by a reader
51
+ * again. This DELIBERATELY overwrites a descendant's own, possibly different,
52
+ * location: a cascade is a write, and a write that stops at an existing value
53
+ * is not a cascade — the operator setting the parent's location is understood
54
+ * as organising the whole subtree by it.
57
55
  *
58
- * RETROACTIVITY: this only runs when `setLocation` is called. A container
59
- * that already had a location before this shipped does NOT retroactively
60
- * align its descendants they stay unaligned until the operator re-saves
61
- * the container's location.
56
+ * NO TYPE PREDICATE. The cascade used to fire only for `DeviceType.Container`,
57
+ * which split the fleet by an accident of provider modelling: on the measured
58
+ * hub, 50 containers cascaded to 440 children while 13 non-container parents
59
+ * (11 `camera`, 2 `hub`) left 20 accessories behind. The Locations page tells
60
+ * the operator "assign the parent, the accessories follow"; a predicate here
61
+ * would make that a lie for exactly the cameras. `collectDescendants` is
62
+ * type-agnostic, and so is this.
63
+ *
64
+ * RETROACTIVITY: this only runs when `setLocation` is called. A parent that
65
+ * already had a location before this shipped does NOT retroactively align its
66
+ * descendants — they stay unaligned until the operator re-saves the parent's
67
+ * location.
62
68
  *
63
69
  * ATOMICITY: the whole cascade runs inside ONE `withMetaWriteLock` critical
64
70
  * section, so no concurrent writer can interleave a read or a write into the
@@ -67,7 +73,7 @@ export declare function setName(pctx: ProviderContext, input: Parameters<IDevice
67
73
  * not a silent one): it is collected as a failure, every other descendant is
68
74
  * still attempted, and if any failed the outcome is logged at `error` with
69
75
  * every failing id — never indistinguishable from a clean cascade. A
70
- * `DeviceMetaChanged` event fires for the container itself and for every
76
+ * `DeviceMetaChanged` event fires for the parent itself and for every
71
77
  * descendant actually written (never for one that failed), so linked-device
72
78
  * consumers (auto-link) see exactly what landed.
73
79
  */
@@ -213,5 +219,20 @@ export declare function addLocation(pctx: ProviderContext, input: Parameters<IDe
213
219
  * label. Cascade only matches case-insensitively + trimmed, same
214
220
  * as the registry equality check.
215
221
  */
222
+ /**
223
+ * Rename a room in ONE server operation: the registry label in place, then
224
+ * every device that carried it.
225
+ *
226
+ * The lock order is the one `removeLocation` documents and must not be
227
+ * "simplified": the registry edit takes the addon-store lock and RELEASES it
228
+ * before the cascade takes the meta lock. Holding both, in that order, is the
229
+ * reverse of `allocateNextDeviceId`'s meta ⊃ addon-store nesting — the pair
230
+ * that deadlocks.
231
+ *
232
+ * Validation happens BEFORE either lock. A rename that throws halfway is the
233
+ * exact failure this function exists to remove, so an empty target must be
234
+ * refused while nothing has been written yet.
235
+ */
236
+ export declare function renameLocation(pctx: ProviderContext, input: Parameters<IDeviceManagerProvider['renameLocation']>[0]): ReturnType<IDeviceManagerProvider['renameLocation']>;
216
237
  export declare function removeLocation(pctx: ProviderContext, input: Parameters<IDeviceManagerProvider['removeLocation']>[0]): ReturnType<IDeviceManagerProvider['removeLocation']>;
217
238
  export {};
@@ -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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { J as evaluateSensorEdge, Kt as createEvent, Mt as errMsg, Nt as BaseAddon, W as doorbellCapability, _ as SOURCE_DEVICE_TYPES, an as EventCategory, g as SOURCE_CAPS, j as bareAddonId, nt as isSourceCap, tt as isSameAddonId, zt as DeviceType } from "../../dist-Dw9cm1NS.mjs";
1
+ import { J as evaluateSensorEdge, Kt as createEvent, Mt as errMsg, Nt as BaseAddon, W as doorbellCapability, _ as SOURCE_DEVICE_TYPES, an as EventCategory, g as SOURCE_CAPS, j as bareAddonId, nt as isSourceCap, tt as isSameAddonId, zt as DeviceType } from "../../dist-DwaeuknI.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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { Nt as BaseAddon, st as logDestinationCapability } from "../../dist-Dw9cm1NS.mjs";
1
+ import { Nt as BaseAddon, st as logDestinationCapability } from "../../dist-DwaeuknI.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-CFTKQGym.js");
3
+ const require_dist = require("../../dist-DCdtLXgx.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 { Kt as createEvent, Mt as errMsg, Nt as BaseAddon, an as EventCategory } from "../../dist-Dw9cm1NS.mjs";
1
+ import { Kt as createEvent, Mt as errMsg, Nt as BaseAddon, an as EventCategory } from "../../dist-DwaeuknI.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-CFTKQGym.js");
6
+ const require_dist = require("../../dist-DCdtLXgx.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 { Nt as BaseAddon, b as ScopedTokenSchema, k as authProviderCapability, kt as userManagementCapability, r as ApiKeyRecordSchema, w as UserRecordSchema } from "../../dist-Dw9cm1NS.mjs";
2
+ import { Nt as BaseAddon, b as ScopedTokenSchema, k as authProviderCapability, kt as userManagementCapability, r as ApiKeyRecordSchema, w as UserRecordSchema } from "../../dist-DwaeuknI.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-CFTKQGym.js");
3
+ const require_dist = require("../../dist-DCdtLXgx.js");
4
4
  const require_tls = require("../../tls-BxQlomxd.js");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let node_path = require("node:path");