@camstack/system 1.2.305 → 1.2.307

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 (60) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/camera-grid/index.js +1 -1
  12. package/dist/builtins/camera-grid/index.mjs +1 -1
  13. package/dist/builtins/console-logging/index.js +1 -1
  14. package/dist/builtins/console-logging/index.mjs +1 -1
  15. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  16. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  17. package/dist/builtins/device-manager/device-extension-linked-slot.d.ts +19 -0
  18. package/dist/builtins/device-manager/device-linked-devices.d.ts +26 -18
  19. package/dist/builtins/device-manager/device-manager.addon.js +157 -135
  20. package/dist/builtins/device-manager/device-manager.addon.mjs +157 -135
  21. package/dist/builtins/doorbell/binding-mirror.d.ts +13 -7
  22. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +12 -45
  23. package/dist/builtins/doorbell/virtual-doorbell.addon.js +29 -102
  24. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +29 -102
  25. package/dist/builtins/hub-forwarder/index.js +1 -1
  26. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  27. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  28. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  29. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  30. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  31. package/dist/builtins/local-network/local-network.addon.js +1 -1
  32. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  33. package/dist/builtins/loki-logging/index.js +1 -1
  34. package/dist/builtins/loki-logging/index.mjs +1 -1
  35. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  36. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  37. package/dist/builtins/platform-probe/index.js +1 -1
  38. package/dist/builtins/platform-probe/index.mjs +1 -1
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  40. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  41. package/dist/builtins/snapshot/index.js +1 -1
  42. package/dist/builtins/snapshot/index.mjs +1 -1
  43. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  44. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  46. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  47. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  48. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  49. package/dist/builtins/system-config/system-config.addon.js +1 -1
  50. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  51. package/dist/builtins/winston-logging/index.js +1 -1
  52. package/dist/builtins/winston-logging/index.mjs +1 -1
  53. package/dist/{dist-BK4iP6ab.js → dist-D3ch99WD.js} +66 -73
  54. package/dist/{dist-Df1NhKxm.mjs → dist-DdfJa8Zn.mjs} +67 -68
  55. package/dist/index.js +1 -1
  56. package/dist/index.mjs +1 -1
  57. package/dist/{retired-settings-keys-dC4ckbXJ.mjs → retired-settings-keys-BJNhhXap.mjs} +1 -1
  58. package/dist/{retired-settings-keys-DwWMph6F.js → retired-settings-keys-DG_LEPqC.js} +1 -1
  59. package/package.json +1 -1
  60. package/dist/builtins/device-manager/device-store-sections.d.ts +0 -26
@@ -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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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
@@ -166,13 +166,19 @@ var DoorbellTriggerEngine = class {
166
166
  * not survive the next read. So a negative read ARMS the revocation and a
167
167
  * second consecutive negative applies it.
168
168
  *
169
- * ERRING. `undefined` (never read) and `unbind-pending` both ring. Reaching
170
- * the ring path at all means the camera has a configured trigger source,
171
- * and a source can only be configured through this wrapper's own
172
- * binding-gated settings surface — so a configured source is itself
173
- * evidence the wrapper was bound. The cost of erring open is a spurious
174
- * ring on a camera the operator unbound but left configured, for at most
175
- * two reload cycles. The cost of erring closed is the press.
169
+ * ERRING. `undefined` (never read) and `unbind-pending` both ring. The cost
170
+ * of erring open is a spurious ring on a camera whose wrapper is not bound
171
+ * but which has trigger sources, for at most two reload cycles. The cost of
172
+ * erring closed is the press.
173
+ *
174
+ * D655: a configured source is NO LONGER evidence of the binding. D49 leaned
175
+ * on "a source can only be configured through this wrapper's own
176
+ * binding-gated settings surface"; that surface is retired, and the
177
+ * `doorbell-source` extension slot that replaced it is offered on every
178
+ * camera, bound or not (D648). The erring-open window above is therefore
179
+ * reachable by configuring sources on a never-bound camera, not only by
180
+ * unbinding a configured one — still bounded by two reload cycles, and the
181
+ * slot's card says `inactive` once the reads agree.
176
182
  *
177
183
  * ─────────────────────────────────────────────────────────────────────
178
184
  * 2026-08-07 (D72). The corroboration above was DEFEATED, and the reason
@@ -380,8 +386,8 @@ function applyDoorbellSourcesPatch(current, patchValue) {
380
386
  //#region src/builtins/doorbell/doorbell-extension-slot.ts
381
387
  /**
382
388
  * The virtual doorbell's side of `device-extension` (D648): the
383
- * `doorbell-source` slot, served over the `doorbellSources` store the
384
- * "Virtual doorbell" settings section already writes.
389
+ * `doorbell-source` slot, served over the wrapper's `doorbellSources` store
390
+ * — since D655 the only editor of a camera's trigger devices.
385
391
  *
386
392
  * - `list` reads the store through `parseDoorbellSources` — the parser the
387
393
  * trigger engine's assignments are built from — so the slot's value is
@@ -401,7 +407,7 @@ function applyDoorbellSourcesPatch(current, patchValue) {
401
407
  * - The picked sources are labelled with ONE batched `listAll({deviceIds})`,
402
408
  * never a `getDevice` per id.
403
409
  * - `set` goes through the addon's `persistDoorbellSources`, the SAME
404
- * normalise-and-store path the settings save uses, which also invalidates
410
+ * normalise-and-store path the wrapper has, which also invalidates
405
411
  * the assignment cache so the binding mirror is re-read off the ring path
406
412
  * (D49). A `setSlot` call is NOT evidence that the wrapper is bound (the
407
413
  * contract reaches this provider through `device-extension`'s own
@@ -622,9 +628,9 @@ function parseDoorbellSlice(slice) {
622
628
  *
623
629
  * 1. The operator binds the wrapper to a camera in the bindings UI
624
630
  * (`defaultActive: false` — never auto-bound).
625
- * 2. In the camera's GENERAL settings (next to "Linked devices") the
626
- * operator picks one or more SOURCE devices from a searchable list —
627
- * that is the ONLY choice. The reaction is derived automatically per
631
+ * 2. In the camera's Extensions section (the `doorbell-source` slot,
632
+ * D648/D655) the operator picks one or more SOURCE devices — that is
633
+ * the ONLY choice. The reaction is derived automatically per
628
634
  * device type: a binary sensor rings when it goes active, a switch
629
635
  * rings when it turns on (see `trigger-engine.ts`). The persisted
630
636
  * shape (`doorbellSources: [{deviceId}]`) stays extensible for a
@@ -671,20 +677,15 @@ var VirtualDoorbellAddon = class extends require_dist.BaseAddon {
671
677
  });
672
678
  return [{
673
679
  capability: require_dist.doorbellCapability,
674
- provider: {
675
- getStatus: (input) => this.getStatus(input.deviceId),
676
- getDeviceSettingsContribution: (input) => this.buildDeviceSettingsContribution(input.deviceId),
677
- getDeviceLiveContribution: async () => null,
678
- applyDeviceSettingsPatch: (input) => this.saveDeviceSettingsPatch(input.deviceId, input.patch)
679
- }
680
+ provider: { getStatus: (input) => this.getStatus(input.deviceId) }
680
681
  }, {
681
682
  capability: require_dist.deviceExtensionCapability,
682
683
  provider: this.buildExtensionProvider()
683
684
  }];
684
685
  }
685
686
  /**
686
- * The `device-extension` provider: the same store and the same save path as
687
- * the settings section below (see `doorbell-extension-slot.ts`).
687
+ * The `device-extension` provider: the only editor of a camera's trigger
688
+ * devices since D655 (see `doorbell-extension-slot.ts`).
688
689
  */
689
690
  buildExtensionProvider() {
690
691
  const toSlotDevice = (d) => ({
@@ -969,19 +970,10 @@ var VirtualDoorbellAddon = class extends require_dist.BaseAddon {
969
970
  }
970
971
  }
971
972
  /**
972
- * Record a binding we observed FIRST-HAND. Both callers are cap methods
973
- * the router only reaches by resolving this wrapper for this device —
974
- * which it can only do through the binding — so a call is direct evidence
975
- * and outranks anything the store says.
976
- */
977
- confirmBindingFromCapCall(deviceId) {
978
- this.bindingMirror.set(deviceId, nextBindingState(this.bindingMirror.get(deviceId), true));
979
- }
980
- /**
981
973
  * Lazily (re)build the camera→source assignment set from every
982
974
  * camera's device store. TTL-bounded so devices/settings appearing
983
- * after boot are picked up without a settings save; invalidated
984
- * eagerly by `applyDeviceSettingsPatch`.
975
+ * after boot are picked up without a save; invalidated eagerly by
976
+ * `persistDoorbellSources`.
985
977
  */
986
978
  async ensureAssignments() {
987
979
  const now = Date.now();
@@ -1052,75 +1044,10 @@ var VirtualDoorbellAddon = class extends require_dist.BaseAddon {
1052
1044
  return parseDoorbellSources(await this.ctx.settings.readDeviceStore(deviceId));
1053
1045
  }
1054
1046
  /**
1055
- * Single-trip, non-throwing device lookup (mirrors snapshot's
1056
- * `lookupDeviceMeta`). Null on any failure — callers fall back to
1057
- * rendering the section (better a spurious section than a thrown
1058
- * aggregator entry).
1059
- */
1060
- async lookupDevice(deviceId) {
1061
- try {
1062
- const found = await this.ctx.api.deviceManager.getDevice.query({ deviceId });
1063
- return found ? { type: found.type } : null;
1064
- } catch (err) {
1065
- this.ctx.logger.debug("virtual-doorbell: getDevice failed during contribution", {
1066
- tags: { deviceId },
1067
- meta: { error: require_dist.errMsg(err) }
1068
- });
1069
- return null;
1070
- }
1071
- }
1072
- /**
1073
- * Settings section for the device-detail page — contributed into the
1074
- * GENERAL settings surface (no `tab`, so it lands in the same tab as
1075
- * "Linked devices", ordered just after it). Camera-only: the doorbell
1076
- * cap also applies to Button accessories (native providers) where a
1077
- * virtual-source picker is meaningless.
1078
- *
1079
- * A SINGLE `device-multiselect` field carries only a declarative
1080
- * `deviceFilter` — the admin-ui renders the SHARED searchable device
1081
- * picker (same idiom as "Linked devices") whose options come from
1082
- * `deviceManager.listAll` filtered CLIENT-SIDE. The addon NO LONGER
1083
- * computes the option list server-side: the old path enumerated
1084
- * `deviceManager.getAllBindings`, which returns an empty set on the live
1085
- * cluster and left the picker blank. The reaction is still derived
1086
- * automatically per device type — no trigger/eventType controls.
1087
- */
1088
- async buildDeviceSettingsContribution(deviceId) {
1089
- const device = await this.lookupDevice(deviceId);
1090
- if (device && device.type !== require_dist.DeviceType.Camera) return null;
1091
- this.confirmBindingFromCapCall(deviceId);
1092
- const sources = await this.readDeviceSources(deviceId);
1093
- return { sections: [{
1094
- id: "virtual-doorbell",
1095
- title: "Virtual doorbell",
1096
- description: "Ring this camera’s doorbell when a chosen device becomes active — a contact opening, a switch turning on, or a sensor firing. Pick any binary sensor or switch below; the reaction is automatic per device type.",
1097
- order: 71,
1098
- fields: [{
1099
- type: "device-multiselect",
1100
- key: KEY_SOURCES,
1101
- label: "Trigger devices",
1102
- description: "Any of these becoming active rings this camera. Binary sensors ring when they go active; switches ring when they turn on.",
1103
- deviceFilter: {
1104
- caps: require_dist.SOURCE_CAPS,
1105
- types: require_dist.SOURCE_DEVICE_TYPES,
1106
- excludeDeviceId: deviceId
1107
- },
1108
- value: sources.map((s) => String(s.deviceId))
1109
- }]
1110
- }] };
1111
- }
1112
- async saveDeviceSettingsPatch(deviceId, patch) {
1113
- if (!this.ctx.settings) throw new Error("[virtual-doorbell] settings store unavailable — cannot persist per-device settings");
1114
- this.confirmBindingFromCapCall(deviceId);
1115
- if ("doorbellSources" in patch) await this.persistDoorbellSources(deviceId, patch[KEY_SOURCES]);
1116
- return { success: true };
1117
- }
1118
- /**
1119
- * THE write path for a camera's trigger sources — the settings save above
1120
- * and the `doorbell-source` extension slot both land here (D648), so the
1121
- * two surfaces cannot store different shapes: normalise to
1122
- * `doorbellSources: [{deviceId}]`, strip the legacy keys, and invalidate the
1123
- * assignment cache so the next rise (and the binding mirror's next
1047
+ * THE write path for a camera's trigger sources — the `doorbell-source`
1048
+ * extension slot lands here (D648; its only caller since D655): normalise
1049
+ * to `doorbellSources: [{deviceId}]`, strip the legacy keys, and invalidate
1050
+ * the assignment cache so the next rise (and the binding mirror's next
1124
1051
  * refresh, off the ring path) sees the new wiring.
1125
1052
  */
1126
1053
  async persistDoorbellSources(deviceId, value) {
@@ -1,4 +1,4 @@
1
- import { An as createEvent, Cn as DeviceType, Ct as isSameAddonId, D as SOURCE_CAPS, O as SOURCE_DEVICE_TYPES, U as bareAddonId, Wn as EventCategory, Z as createDeviceExtensionProvider, _n as BaseAddon, dt as evaluateSensorEdge, hn as errMsg, rt as deviceExtensionCapability, st as doorbellCapability, tt as defineExtensionSlotProvider, wt as isSourceCap } from "../../dist-Df1NhKxm.mjs";
1
+ import { Ct as isSourceCap, D as SOURCE_CAPS, H as bareAddonId, Sn as DeviceType, St as isSameAddonId, Un as EventCategory, X as createDeviceExtensionProvider, et as defineExtensionSlotProvider, gn as BaseAddon, kn as createEvent, mn as errMsg, nt as deviceExtensionCapability, ot as doorbellCapability, ut as evaluateSensorEdge } from "../../dist-DdfJa8Zn.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
@@ -161,13 +161,19 @@ var DoorbellTriggerEngine = class {
161
161
  * not survive the next read. So a negative read ARMS the revocation and a
162
162
  * second consecutive negative applies it.
163
163
  *
164
- * ERRING. `undefined` (never read) and `unbind-pending` both ring. Reaching
165
- * the ring path at all means the camera has a configured trigger source,
166
- * and a source can only be configured through this wrapper's own
167
- * binding-gated settings surface — so a configured source is itself
168
- * evidence the wrapper was bound. The cost of erring open is a spurious
169
- * ring on a camera the operator unbound but left configured, for at most
170
- * two reload cycles. The cost of erring closed is the press.
164
+ * ERRING. `undefined` (never read) and `unbind-pending` both ring. The cost
165
+ * of erring open is a spurious ring on a camera whose wrapper is not bound
166
+ * but which has trigger sources, for at most two reload cycles. The cost of
167
+ * erring closed is the press.
168
+ *
169
+ * D655: a configured source is NO LONGER evidence of the binding. D49 leaned
170
+ * on "a source can only be configured through this wrapper's own
171
+ * binding-gated settings surface"; that surface is retired, and the
172
+ * `doorbell-source` extension slot that replaced it is offered on every
173
+ * camera, bound or not (D648). The erring-open window above is therefore
174
+ * reachable by configuring sources on a never-bound camera, not only by
175
+ * unbinding a configured one — still bounded by two reload cycles, and the
176
+ * slot's card says `inactive` once the reads agree.
171
177
  *
172
178
  * ─────────────────────────────────────────────────────────────────────
173
179
  * 2026-08-07 (D72). The corroboration above was DEFEATED, and the reason
@@ -375,8 +381,8 @@ function applyDoorbellSourcesPatch(current, patchValue) {
375
381
  //#region src/builtins/doorbell/doorbell-extension-slot.ts
376
382
  /**
377
383
  * The virtual doorbell's side of `device-extension` (D648): the
378
- * `doorbell-source` slot, served over the `doorbellSources` store the
379
- * "Virtual doorbell" settings section already writes.
384
+ * `doorbell-source` slot, served over the wrapper's `doorbellSources` store
385
+ * — since D655 the only editor of a camera's trigger devices.
380
386
  *
381
387
  * - `list` reads the store through `parseDoorbellSources` — the parser the
382
388
  * trigger engine's assignments are built from — so the slot's value is
@@ -396,7 +402,7 @@ function applyDoorbellSourcesPatch(current, patchValue) {
396
402
  * - The picked sources are labelled with ONE batched `listAll({deviceIds})`,
397
403
  * never a `getDevice` per id.
398
404
  * - `set` goes through the addon's `persistDoorbellSources`, the SAME
399
- * normalise-and-store path the settings save uses, which also invalidates
405
+ * normalise-and-store path the wrapper has, which also invalidates
400
406
  * the assignment cache so the binding mirror is re-read off the ring path
401
407
  * (D49). A `setSlot` call is NOT evidence that the wrapper is bound (the
402
408
  * contract reaches this provider through `device-extension`'s own
@@ -617,9 +623,9 @@ function parseDoorbellSlice(slice) {
617
623
  *
618
624
  * 1. The operator binds the wrapper to a camera in the bindings UI
619
625
  * (`defaultActive: false` — never auto-bound).
620
- * 2. In the camera's GENERAL settings (next to "Linked devices") the
621
- * operator picks one or more SOURCE devices from a searchable list —
622
- * that is the ONLY choice. The reaction is derived automatically per
626
+ * 2. In the camera's Extensions section (the `doorbell-source` slot,
627
+ * D648/D655) the operator picks one or more SOURCE devices — that is
628
+ * the ONLY choice. The reaction is derived automatically per
623
629
  * device type: a binary sensor rings when it goes active, a switch
624
630
  * rings when it turns on (see `trigger-engine.ts`). The persisted
625
631
  * shape (`doorbellSources: [{deviceId}]`) stays extensible for a
@@ -666,20 +672,15 @@ var VirtualDoorbellAddon = class extends BaseAddon {
666
672
  });
667
673
  return [{
668
674
  capability: doorbellCapability,
669
- provider: {
670
- getStatus: (input) => this.getStatus(input.deviceId),
671
- getDeviceSettingsContribution: (input) => this.buildDeviceSettingsContribution(input.deviceId),
672
- getDeviceLiveContribution: async () => null,
673
- applyDeviceSettingsPatch: (input) => this.saveDeviceSettingsPatch(input.deviceId, input.patch)
674
- }
675
+ provider: { getStatus: (input) => this.getStatus(input.deviceId) }
675
676
  }, {
676
677
  capability: deviceExtensionCapability,
677
678
  provider: this.buildExtensionProvider()
678
679
  }];
679
680
  }
680
681
  /**
681
- * The `device-extension` provider: the same store and the same save path as
682
- * the settings section below (see `doorbell-extension-slot.ts`).
682
+ * The `device-extension` provider: the only editor of a camera's trigger
683
+ * devices since D655 (see `doorbell-extension-slot.ts`).
683
684
  */
684
685
  buildExtensionProvider() {
685
686
  const toSlotDevice = (d) => ({
@@ -964,19 +965,10 @@ var VirtualDoorbellAddon = class extends BaseAddon {
964
965
  }
965
966
  }
966
967
  /**
967
- * Record a binding we observed FIRST-HAND. Both callers are cap methods
968
- * the router only reaches by resolving this wrapper for this device —
969
- * which it can only do through the binding — so a call is direct evidence
970
- * and outranks anything the store says.
971
- */
972
- confirmBindingFromCapCall(deviceId) {
973
- this.bindingMirror.set(deviceId, nextBindingState(this.bindingMirror.get(deviceId), true));
974
- }
975
- /**
976
968
  * Lazily (re)build the camera→source assignment set from every
977
969
  * camera's device store. TTL-bounded so devices/settings appearing
978
- * after boot are picked up without a settings save; invalidated
979
- * eagerly by `applyDeviceSettingsPatch`.
970
+ * after boot are picked up without a save; invalidated eagerly by
971
+ * `persistDoorbellSources`.
980
972
  */
981
973
  async ensureAssignments() {
982
974
  const now = Date.now();
@@ -1047,75 +1039,10 @@ var VirtualDoorbellAddon = class extends BaseAddon {
1047
1039
  return parseDoorbellSources(await this.ctx.settings.readDeviceStore(deviceId));
1048
1040
  }
1049
1041
  /**
1050
- * Single-trip, non-throwing device lookup (mirrors snapshot's
1051
- * `lookupDeviceMeta`). Null on any failure — callers fall back to
1052
- * rendering the section (better a spurious section than a thrown
1053
- * aggregator entry).
1054
- */
1055
- async lookupDevice(deviceId) {
1056
- try {
1057
- const found = await this.ctx.api.deviceManager.getDevice.query({ deviceId });
1058
- return found ? { type: found.type } : null;
1059
- } catch (err) {
1060
- this.ctx.logger.debug("virtual-doorbell: getDevice failed during contribution", {
1061
- tags: { deviceId },
1062
- meta: { error: errMsg(err) }
1063
- });
1064
- return null;
1065
- }
1066
- }
1067
- /**
1068
- * Settings section for the device-detail page — contributed into the
1069
- * GENERAL settings surface (no `tab`, so it lands in the same tab as
1070
- * "Linked devices", ordered just after it). Camera-only: the doorbell
1071
- * cap also applies to Button accessories (native providers) where a
1072
- * virtual-source picker is meaningless.
1073
- *
1074
- * A SINGLE `device-multiselect` field carries only a declarative
1075
- * `deviceFilter` — the admin-ui renders the SHARED searchable device
1076
- * picker (same idiom as "Linked devices") whose options come from
1077
- * `deviceManager.listAll` filtered CLIENT-SIDE. The addon NO LONGER
1078
- * computes the option list server-side: the old path enumerated
1079
- * `deviceManager.getAllBindings`, which returns an empty set on the live
1080
- * cluster and left the picker blank. The reaction is still derived
1081
- * automatically per device type — no trigger/eventType controls.
1082
- */
1083
- async buildDeviceSettingsContribution(deviceId) {
1084
- const device = await this.lookupDevice(deviceId);
1085
- if (device && device.type !== DeviceType.Camera) return null;
1086
- this.confirmBindingFromCapCall(deviceId);
1087
- const sources = await this.readDeviceSources(deviceId);
1088
- return { sections: [{
1089
- id: "virtual-doorbell",
1090
- title: "Virtual doorbell",
1091
- description: "Ring this camera’s doorbell when a chosen device becomes active — a contact opening, a switch turning on, or a sensor firing. Pick any binary sensor or switch below; the reaction is automatic per device type.",
1092
- order: 71,
1093
- fields: [{
1094
- type: "device-multiselect",
1095
- key: KEY_SOURCES,
1096
- label: "Trigger devices",
1097
- description: "Any of these becoming active rings this camera. Binary sensors ring when they go active; switches ring when they turn on.",
1098
- deviceFilter: {
1099
- caps: SOURCE_CAPS,
1100
- types: SOURCE_DEVICE_TYPES,
1101
- excludeDeviceId: deviceId
1102
- },
1103
- value: sources.map((s) => String(s.deviceId))
1104
- }]
1105
- }] };
1106
- }
1107
- async saveDeviceSettingsPatch(deviceId, patch) {
1108
- if (!this.ctx.settings) throw new Error("[virtual-doorbell] settings store unavailable — cannot persist per-device settings");
1109
- this.confirmBindingFromCapCall(deviceId);
1110
- if ("doorbellSources" in patch) await this.persistDoorbellSources(deviceId, patch[KEY_SOURCES]);
1111
- return { success: true };
1112
- }
1113
- /**
1114
- * THE write path for a camera's trigger sources — the settings save above
1115
- * and the `doorbell-source` extension slot both land here (D648), so the
1116
- * two surfaces cannot store different shapes: normalise to
1117
- * `doorbellSources: [{deviceId}]`, strip the legacy keys, and invalidate the
1118
- * assignment cache so the next rise (and the binding mirror's next
1042
+ * THE write path for a camera's trigger sources — the `doorbell-source`
1043
+ * extension slot lands here (D648; its only caller since D655): normalise
1044
+ * to `doorbellSources: [{deviceId}]`, strip the legacy keys, and invalidate
1045
+ * the assignment cache so the next rise (and the binding mirror's next
1119
1046
  * refresh, off the ring path) sees the new wiring.
1120
1047
  */
1121
1048
  async persistDoorbellSources(deviceId, value) {
@@ -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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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 { Mt as logDestinationCapability, _n as BaseAddon } from "../../dist-Df1NhKxm.mjs";
1
+ import { gn as BaseAddon, jt as logDestinationCapability } from "../../dist-DdfJa8Zn.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-BK4iP6ab.js");
3
+ const require_dist = require("../../dist-D3ch99WD.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 { An as createEvent, C as RECORDING_SOURCE_CAMSTACK_ADDON, E as RecordingStatusSchema, Wn as EventCategory, _n as BaseAddon, hn as errMsg } from "../../dist-Df1NhKxm.mjs";
1
+ import { C as RECORDING_SOURCE_CAMSTACK_ADDON, E as RecordingStatusSchema, Un as EventCategory, gn as BaseAddon, kn as createEvent, mn as errMsg } from "../../dist-DdfJa8Zn.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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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 { I as UserRecordSchema, V as authProviderCapability, _n as BaseAddon, j as ScopedTokenSchema, r as ApiKeyRecordSchema, un as userManagementCapability } from "../../dist-Df1NhKxm.mjs";
2
+ import { A as ScopedTokenSchema, B as authProviderCapability, F as UserRecordSchema, gn as BaseAddon, ln as userManagementCapability, r as ApiKeyRecordSchema } from "../../dist-DdfJa8Zn.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-BK4iP6ab.js");
3
+ const require_dist = require("../../dist-D3ch99WD.js");
4
4
  const require_tls = require("../../tls-D_wUeOTD.js");
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { At as localNetworkCapability, Wn as EventCategory, _n as BaseAddon } from "../../dist-Df1NhKxm.mjs";
1
+ import { Un as EventCategory, gn as BaseAddon, kt as localNetworkCapability } from "../../dist-DdfJa8Zn.mjs";
2
2
  import { S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, i as writeExtraSans, l as reissueTlsLeaf, n as readTlsAccessStatus, o as validateUploadedTls, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, y as applyLanHttp } from "../../tls-IM-v7JV4.mjs";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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 { Mt as logDestinationCapability, _n as BaseAddon } from "../../dist-Df1NhKxm.mjs";
1
+ import { gn as BaseAddon, jt as logDestinationCapability } from "../../dist-DdfJa8Zn.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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.js");
7
7
  let node_fs_promises = require("node:fs/promises");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { An as createEvent, Jt as resolveBucketMs, Lt as metricsProviderCapability, Wn as EventCategory, _n as BaseAddon, gt as foldSnapshotByFunction, hn as errMsg, qt as reducePoints } from "../../dist-Df1NhKxm.mjs";
1
+ import { It as metricsProviderCapability, Kt as reducePoints, Un as EventCategory, gn as BaseAddon, ht as foldSnapshotByFunction, kn as createEvent, mn as errMsg, qt as resolveBucketMs } from "../../dist-DdfJa8Zn.mjs";
2
2
  import { access, readFile, readdir } from "node:fs/promises";
3
3
  import * as fs from "node:fs";
4
4
  import { constants, readFileSync } from "node:fs";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Mn as emitReadiness, Wn as EventCategory, Wt as platformProbeCapability, _n as BaseAddon, ct as enumerateInferenceDevices, en as scoreRuntimes, hn as errMsg } from "../../dist-Df1NhKxm.mjs";
2
+ import { $t as scoreRuntimes, Un as EventCategory, Ut as platformProbeCapability, gn as BaseAddon, jn as emitReadiness, mn as errMsg, st as enumerateInferenceDevices } from "../../dist-DdfJa8Zn.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { execFile } from "node:child_process";
5
5
  import * as os from "node:os";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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 { Wn as EventCategory, _n as BaseAddon } from "../../dist-Df1NhKxm.mjs";
1
+ import { Un as EventCategory, gn as BaseAddon } from "../../dist-DdfJa8Zn.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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-Cqs7GkOV.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { Cn as DeviceType, Ln as nodePin, U as bareAddonId, W as batteryCapability, Wn as EventCategory, _n as BaseAddon, a as BatteryStatusSchema, hn as errMsg, ln as streamQualityLabel, nt as deriveBatteryPresence, rn as snapshotCapability, xn as DeviceFeature } from "../../dist-Df1NhKxm.mjs";
1
+ import { H as bareAddonId, In as nodePin, Sn as DeviceType, U as batteryCapability, Un as EventCategory, a as BatteryStatusSchema, bn as DeviceFeature, cn as streamQualityLabel, gn as BaseAddon, mn as errMsg, nn as snapshotCapability, tt as deriveBatteryPresence } from "../../dist-DdfJa8Zn.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CZPvteq2.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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 { _n as BaseAddon, ht as filesystemBrowseCapability, sn as storageProviderCapability } from "../../dist-Df1NhKxm.mjs";
1
+ import { gn as BaseAddon, mt as filesystemBrowseCapability, on as storageProviderCapability } from "../../dist-DdfJa8Zn.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.js");
7
7
  const require_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { F as StorageMigrationJobSchema, Ft as mayReadLocation, It as mayWriteToLocation, M as StorageCleanupJobSchema, N as StorageLocationModeSchema, Ot as legacyModeOf, P as StorageLocationTypeSchema, Rn as parseJsonObject, Rt as modeMayWrite, Wn as EventCategory, Yt as resolveLocationMode, _n as BaseAddon, an as storageMigrationCapability, ft as evictionPolicyOfLocation, in as storageCapability, mn as withLocationMode, q as collectSecretConfigKeys, tn as settingsStoreCapability, v as LegacyStorageLocationDefaultSchema, vt as gbToBytes, w as REDACTED_SECRET, xt as isLocationEnabled } from "../../dist-Df1NhKxm.mjs";
1
+ import { Dt as legacyModeOf, Ft as mayWriteToLocation, Jt as resolveLocationMode, K as collectSecretConfigKeys, Ln as parseJsonObject, Lt as modeMayWrite, M as StorageLocationModeSchema, N as StorageLocationTypeSchema, P as StorageMigrationJobSchema, Pt as mayReadLocation, Un as EventCategory, _t as gbToBytes, bt as isLocationEnabled, dt as evictionPolicyOfLocation, en as settingsStoreCapability, gn as BaseAddon, in as storageMigrationCapability, j as StorageCleanupJobSchema, pn as withLocationMode, rn as storageCapability, v as LegacyStorageLocationDefaultSchema, w as REDACTED_SECRET } from "../../dist-DdfJa8Zn.mjs";
2
2
  import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-BdT320dq.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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 { Pn as hydrateSchema, _n as BaseAddon, hn as errMsg } from "../../dist-Df1NhKxm.mjs";
1
+ import { Nn as hydrateSchema, gn as BaseAddon, mn as errMsg } from "../../dist-DdfJa8Zn.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-BK4iP6ab.js");
6
+ const require_dist = require("../../dist-D3ch99WD.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);