@camstack/system 1.2.241 → 1.2.243

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 (55) 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/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/adoption-job-engine.d.ts +28 -7
  14. package/dist/builtins/device-manager/device-manager.addon.d.ts +11 -0
  15. package/dist/builtins/device-manager/device-manager.addon.js +147 -15
  16. package/dist/builtins/device-manager/device-manager.addon.mjs +147 -15
  17. package/dist/builtins/device-manager/device-meta-actions.d.ts +32 -0
  18. package/dist/builtins/device-manager/device-meta-types.d.ts +17 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  21. package/dist/builtins/hub-forwarder/index.js +1 -1
  22. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  26. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  27. package/dist/builtins/local-network/local-network.addon.js +1 -1
  28. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  29. package/dist/builtins/loki-logging/index.js +1 -1
  30. package/dist/builtins/loki-logging/index.mjs +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  33. package/dist/builtins/platform-probe/index.js +1 -1
  34. package/dist/builtins/platform-probe/index.mjs +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  37. package/dist/builtins/snapshot/index.js +1 -1
  38. package/dist/builtins/snapshot/index.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  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-BjSYm1hH.mjs → dist-B4htkDVv.mjs} +104 -3
  50. package/dist/{dist-C6JIhqS6.js → dist-D6kqBW1G.js} +115 -2
  51. package/dist/index.js +1 -1
  52. package/dist/index.mjs +1 -1
  53. package/dist/{retired-settings-keys-C53Y2Q1C.js → retired-settings-keys-BPIyHR4_.js} +1 -1
  54. package/dist/{retired-settings-keys-MpSfaPEY.mjs → retired-settings-keys-C9w4b4TV.mjs} +1 -1
  55. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { $t as DeviceRole, G as deviceManagerCapability, Jt as BaseAddon, K as deviceStateCapability, L as buildStreamParamsConfigSchema, Mt as runtimeStatePolicyFor, Qt as DeviceFeature, S as STREAM_PROFILE_META, St as normalizeUnit, X as enumerateItemArrayFields, Z as enumerateSchemaFields, a as CAMERA_SWITCH_ORDER, bn as EventCategory, en as DeviceType, f as DeviceStatusSchema, fn as isDeviceConfigCap, kt as resolveContainerPrimaryChild, o as CAP_NAMES_WITH_STATUS, q as deviceStatusCapability, qt as errMsg, rn as WELL_KNOWN_TAB_MAP, t as ALL_CAPABILITY_DEFINITIONS, wt as parseStreamParamsFormPatch, yn as sleep$1 } from "../../dist-BjSYm1hH.mjs";
2
- import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-MpSfaPEY.mjs";
1
+ import { C as STREAM_PROFILE_META, Et as parseStreamParamsFormPatch, J as deviceStatusCapability, K as deviceManagerCapability, Pt as runtimeStatePolicyFor, Q as enumerateSchemaFields, R as buildStreamParamsConfigSchema, Sn as EventCategory, Xt as BaseAddon, Yt as errMsg, Z as enumerateItemArrayFields, a as CAMERA_SWITCH_ORDER, an as WELL_KNOWN_TAB_MAP, en as DeviceFeature, f as DeviceStatusSchema, gt as locationIconKey, h as LocationIconIdSchema, jt as resolveContainerPrimaryChild, mn as isDeviceConfigCap, nn as DeviceType, o as CAP_NAMES_WITH_STATUS, q as deviceStateCapability, t as ALL_CAPABILITY_DEFINITIONS, tn as DeviceRole, wt as normalizeUnit, xn as sleep$1 } from "../../dist-B4htkDVv.mjs";
2
+ import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-C9w4b4TV.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
@@ -223,13 +223,15 @@ var AdoptionJobEngine = class {
223
223
  const startedAt = this.now();
224
224
  try {
225
225
  const per = input.perCandidate?.[childNativeId];
226
- const entry = (await provider.adopt({
226
+ const adoptCandidate = async () => await provider.adopt({
227
227
  integrationId: input.integrationId,
228
228
  childNativeIds: [childNativeId],
229
229
  ...input.filter !== void 0 ? { filter: input.filter } : {},
230
230
  ...input.importLocations !== void 0 ? { importLocations: input.importLocations } : {},
231
231
  ...per !== void 0 ? { perCandidate: { [childNativeId]: per } } : {}
232
- })).adopted[0];
232
+ });
233
+ const fence = this.deps.removalFence;
234
+ const entry = (fence === void 0 ? await adoptCandidate() : await fence.runUnfenced(adoptCandidate)).adopted[0];
233
235
  if (!entry) {
234
236
  job.results.push({
235
237
  childNativeId,
@@ -3921,7 +3923,8 @@ async function runLocationBackfillAtBoot(pctx, bootRows) {
3921
3923
  * setLocation, setType, setIntegrationId, setLinkDeviceId,
3922
3924
  * setPrimaryChildEntityId, setChildLayout, setRole, setDisplay,
3923
3925
  * applyInitialMeta, setMetadata, setDisabled), and the location registry
3924
- * (listLocations, addLocation, removeLocation).
3926
+ * (listLocations, addLocation, removeLocation, renameLocation) with its icon
3927
+ * map (listLocationIcons, setLocationIcon).
3925
3928
  *
3926
3929
  * EXACT ordering and side effects preserved — notably registerDevice's
3927
3930
  * name-precedence reconcile + field-preservation spreads, the
@@ -4973,6 +4976,83 @@ async function addLocation(pctx, input) {
4973
4976
  });
4974
4977
  }
4975
4978
  /**
4979
+ * Every location that carries an icon, as `(locationKey, icon)` pairs.
4980
+ *
4981
+ * ADDITIVE beside `listLocations`, which still answers `string[]` — the
4982
+ * autocomplete, the HA adoption import and the recorder all read a list of
4983
+ * LABELS and a shape change there would break every one of them. A surface
4984
+ * that wants icons asks for them and joins on `locationIconKey`.
4985
+ *
4986
+ * Returns only what was actually stored. A location with no assignment is
4987
+ * ABSENT, never `map-pin`: the fallback belongs to the renderer
4988
+ * (`resolveLocationIcon`), and folding it in here would make "the operator
4989
+ * picked the pin" indistinguishable from "the operator picked nothing" —
4990
+ * which is the difference an icon picker has to draw.
4991
+ */
4992
+ async function listLocationIcons(pctx) {
4993
+ const icons = (await pctx.metaStore.readStore()).locationIcons ?? {};
4994
+ return Object.entries(icons).map(([locationKey, icon]) => ({
4995
+ locationKey,
4996
+ icon
4997
+ })).toSorted((a, b) => a.locationKey.localeCompare(b.locationKey));
4998
+ }
4999
+ /**
5000
+ * Set (or clear) the icon of a location label.
5001
+ *
5002
+ * Keyed on `locationIconKey` — trimmed + lower-cased, the equality the
5003
+ * registry matches on everywhere — so a case-only rename keeps the icon and
5004
+ * two spellings of one room can never carry two different icons.
5005
+ *
5006
+ * The label need NOT be registered: `setLocation` accepts a free-form string
5007
+ * and `listLocations` folds in whatever devices carry, so requiring
5008
+ * registration here would leave exactly the ad-hoc rooms unable to have an
5009
+ * icon. An empty label throws — there is no room to name.
5010
+ *
5011
+ * Same read-modify-write and same lock as `addLocation`: the map is one key of
5012
+ * the addon store, and `writeAddonStore` re-inserts the whole key range, so an
5013
+ * unordered write carries a stale copy of every other key.
5014
+ */
5015
+ async function setLocationIcon(pctx, input) {
5016
+ const trimmed = input.location.trim();
5017
+ if (trimmed.length === 0) throw new Error("[device-manager] setLocationIcon: location must be non-empty");
5018
+ const key = locationIconKey(trimmed);
5019
+ await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
5020
+ const icons = (await pctx.metaStore.readStore()).locationIcons ?? {};
5021
+ if (input.icon === null) {
5022
+ if (!(key in icons)) return;
5023
+ const { [key]: _cleared, ...kept } = icons;
5024
+ await pctx.settings.writeAddonStore({ locationIcons: kept });
5025
+ return;
5026
+ }
5027
+ if (icons[key] === input.icon) return;
5028
+ await pctx.settings.writeAddonStore({ locationIcons: {
5029
+ ...icons,
5030
+ [key]: input.icon
5031
+ } });
5032
+ });
5033
+ }
5034
+ /**
5035
+ * Move a location's icon from one key to another, for `renameLocation`.
5036
+ *
5037
+ * Returns `null` when NOTHING changes, so the caller can leave the key out of
5038
+ * its `writeAddonStore` patch instead of re-writing an identical map.
5039
+ *
5040
+ * A rename onto a room that already has an icon is a MERGE, and the surviving
5041
+ * room keeps its own: the operator renaming "Sala" into "Salone" is moving
5042
+ * devices into Salone, not restyling it. The source key is dropped either way.
5043
+ * Pure — builds a new record, mutates nothing.
5044
+ */
5045
+ function renameLocationIcon(icons, fromKey, toKey) {
5046
+ const moved = icons[fromKey];
5047
+ if (moved === void 0) return null;
5048
+ if (fromKey === toKey) return null;
5049
+ const { [fromKey]: _dropped, ...rest } = icons;
5050
+ return toKey in rest ? rest : {
5051
+ ...rest,
5052
+ [toKey]: moved
5053
+ };
5054
+ }
5055
+ /**
4976
5056
  * Remove a label from the curated registry. Match is
4977
5057
  * case-insensitive. Devices that still reference this label keep
4978
5058
  * their `meta.location` value (the registry is a suggestion
@@ -5003,13 +5083,19 @@ async function renameLocation(pctx, input) {
5003
5083
  const fromKey = from.toLowerCase();
5004
5084
  const toKey = to.toLowerCase();
5005
5085
  await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
5006
- const current = (await pctx.metaStore.readStore()).locations ?? [];
5007
- if (!current.some((l) => l.trim().toLowerCase() === fromKey)) return;
5086
+ const store = await pctx.metaStore.readStore();
5087
+ const current = store.locations ?? [];
5088
+ const registered = current.some((l) => l.trim().toLowerCase() === fromKey);
5089
+ const movedIcons = renameLocationIcon(store.locationIcons ?? {}, fromKey, toKey);
5090
+ if (!registered && movedIcons === null) return;
5008
5091
  const kept = current.filter((l) => {
5009
5092
  const key = l.trim().toLowerCase();
5010
5093
  return key !== fromKey && key !== toKey;
5011
5094
  });
5012
- await pctx.settings.writeAddonStore({ locations: [...kept, to] });
5095
+ await pctx.settings.writeAddonStore({
5096
+ ...registered ? { locations: [...kept, to] } : {},
5097
+ ...movedIcons !== null ? { locationIcons: movedIcons } : {}
5098
+ });
5013
5099
  });
5014
5100
  const moved = await pctx.metaStore.withMetaWriteLock(async () => {
5015
5101
  const out = [];
@@ -5047,10 +5133,19 @@ async function removeLocation(pctx, input) {
5047
5133
  const trimmed = input.name.trim();
5048
5134
  if (trimmed.length === 0) return;
5049
5135
  await pctx.bindingsDeps.withAddonStoreWriteLock(async () => {
5050
- const current = (await pctx.metaStore.readStore()).locations ?? [];
5051
- const remaining = current.filter((l) => l.toLowerCase() !== trimmed.toLowerCase());
5052
- if (remaining.length === current.length) return;
5053
- await pctx.settings.writeAddonStore({ locations: remaining });
5136
+ const store = await pctx.metaStore.readStore();
5137
+ const current = store.locations ?? [];
5138
+ const key = locationIconKey(trimmed);
5139
+ const remaining = current.filter((l) => l.toLowerCase() !== key);
5140
+ const droppedLabel = remaining.length !== current.length;
5141
+ const icons = store.locationIcons ?? {};
5142
+ const droppedIcon = key in icons;
5143
+ if (!droppedLabel && !droppedIcon) return;
5144
+ const { [key]: _removedIcon, ...keptIcons } = icons;
5145
+ await pctx.settings.writeAddonStore({
5146
+ ...droppedLabel ? { locations: remaining } : {},
5147
+ ...droppedIcon ? { locationIcons: keptIcons } : {}
5148
+ });
5054
5149
  });
5055
5150
  if (input.cascade !== true) return;
5056
5151
  const cleared = await pctx.metaStore.withMetaWriteLock(async () => {
@@ -5152,12 +5247,35 @@ function decodeAddonStore(raw) {
5152
5247
  const nextDeviceId = raw["nextDeviceId"];
5153
5248
  const locations = raw["locations"];
5154
5249
  const roleDisplayDefaults = raw["roleDisplayDefaults"];
5250
+ const locationIcons = decodeLocationIcons(raw["locationIcons"]);
5155
5251
  return {
5156
5252
  ...typeof nextDeviceId === "number" && Number.isFinite(nextDeviceId) ? { nextDeviceId } : {},
5157
5253
  ...Array.isArray(locations) ? { locations: locations.filter((l) => typeof l === "string") } : {},
5158
- ...isRoleDisplayDefaults(roleDisplayDefaults) ? { roleDisplayDefaults } : {}
5254
+ ...isRoleDisplayDefaults(roleDisplayDefaults) ? { roleDisplayDefaults } : {},
5255
+ ...locationIcons !== null ? { locationIcons } : {}
5159
5256
  };
5160
5257
  }
5258
+ /**
5259
+ * Decode the `locationIcons` map, dropping every entry whose value is not a
5260
+ * member of the closed vocabulary.
5261
+ *
5262
+ * Dropping, not passing through: the icon's whole point is that both surfaces
5263
+ * can draw it, and an id from a hand-edit or a newer hub is exactly the value
5264
+ * that draws nothing. Absent reads as "no icon chosen", which every renderer
5265
+ * already degrades to the fallback pin. Returns `null` when the key itself is
5266
+ * absent or of the wrong shape, so the field stays OMITTED rather than being
5267
+ * written back as an empty object.
5268
+ */
5269
+ function decodeLocationIcons(raw) {
5270
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return null;
5271
+ const out = {};
5272
+ for (const [key, value] of Object.entries(raw)) {
5273
+ const parsed = LocationIconIdSchema.safeParse(value);
5274
+ if (!parsed.success) continue;
5275
+ out[key] = parsed.data;
5276
+ }
5277
+ return out;
5278
+ }
5161
5279
  function isRoleDisplayDefaults(value) {
5162
5280
  return value !== null && typeof value === "object" && !Array.isArray(value);
5163
5281
  }
@@ -6744,6 +6862,17 @@ var DeviceManagerAddon = class extends BaseAddon {
6744
6862
  remoteNativeCaps = /* @__PURE__ */ new Map();
6745
6863
  /** Background adoption jobs — lazily constructed (needs `ctx.logger`). */
6746
6864
  adoptionJobEngine = null;
6865
+ /**
6866
+ * ONE removal fence for this process, shared by the provider context and the
6867
+ * adoption-job engine.
6868
+ *
6869
+ * It used to be constructed inline into the context, so the job engine had
6870
+ * no way to reach it and an operator re-adopting hardware they had just
6871
+ * deleted was refused for the whole 5-minute TTL — their own action, denied.
6872
+ * Two fences would be worse than one: the engine would suspend a fence that
6873
+ * `allocateDeviceId` never consults.
6874
+ */
6875
+ removalFence = createRemovalFence();
6747
6876
  /** The runtime-state mirror, asserted present (constructed in onInitialize). */
6748
6877
  get stateMirror() {
6749
6878
  if (!this.stateMirrorImpl) throw new Error("DeviceManagerAddon: state mirror accessed before onInitialize");
@@ -6781,7 +6910,8 @@ var DeviceManagerAddon = class extends BaseAddon {
6781
6910
  get adoptionJobsImpl() {
6782
6911
  if (!this.adoptionJobEngine) this.adoptionJobEngine = new AdoptionJobEngine({
6783
6912
  resolveProvider: async (addonId) => this.requireDeviceAdoptionProvider(addonId),
6784
- logger: this.ctx.logger
6913
+ logger: this.ctx.logger,
6914
+ removalFence: this.removalFence
6785
6915
  });
6786
6916
  return this.adoptionJobEngine;
6787
6917
  }
@@ -6932,7 +7062,7 @@ var DeviceManagerAddon = class extends BaseAddon {
6932
7062
  registry,
6933
7063
  bindingsDeps: this.bindingsDeps,
6934
7064
  migrationGuard: createMigrationGuard(),
6935
- removalFence: createRemovalFence(),
7065
+ removalFence: this.removalFence,
6936
7066
  requireDeviceOps,
6937
7067
  stampIntegrationId: stampIntegrationId$1,
6938
7068
  get provider() {
@@ -6978,6 +7108,8 @@ var DeviceManagerAddon = class extends BaseAddon {
6978
7108
  addLocation: (input) => addLocation(pctx, input),
6979
7109
  removeLocation: (input) => removeLocation(pctx, input),
6980
7110
  renameLocation: (input) => renameLocation(pctx, input),
7111
+ listLocationIcons: () => listLocationIcons(pctx),
7112
+ setLocationIcon: (input) => setLocationIcon(pctx, input),
6981
7113
  listPersistedByAddon: (input) => listPersistedByAddon(pctx, input),
6982
7114
  listAll: (input) => listAll(pctx, input),
6983
7115
  getDevice: (input) => getDevice(pctx, input),
@@ -212,6 +212,38 @@ export declare function listLocations(pctx: ProviderContext): ReturnType<IDevice
212
212
  * meaningful label.
213
213
  */
214
214
  export declare function addLocation(pctx: ProviderContext, input: Parameters<IDeviceManagerProvider['addLocation']>[0]): ReturnType<IDeviceManagerProvider['addLocation']>;
215
+ /**
216
+ * Every location that carries an icon, as `(locationKey, icon)` pairs.
217
+ *
218
+ * ADDITIVE beside `listLocations`, which still answers `string[]` — the
219
+ * autocomplete, the HA adoption import and the recorder all read a list of
220
+ * LABELS and a shape change there would break every one of them. A surface
221
+ * that wants icons asks for them and joins on `locationIconKey`.
222
+ *
223
+ * Returns only what was actually stored. A location with no assignment is
224
+ * ABSENT, never `map-pin`: the fallback belongs to the renderer
225
+ * (`resolveLocationIcon`), and folding it in here would make "the operator
226
+ * picked the pin" indistinguishable from "the operator picked nothing" —
227
+ * which is the difference an icon picker has to draw.
228
+ */
229
+ export declare function listLocationIcons(pctx: ProviderContext): ReturnType<IDeviceManagerProvider['listLocationIcons']>;
230
+ /**
231
+ * Set (or clear) the icon of a location label.
232
+ *
233
+ * Keyed on `locationIconKey` — trimmed + lower-cased, the equality the
234
+ * registry matches on everywhere — so a case-only rename keeps the icon and
235
+ * two spellings of one room can never carry two different icons.
236
+ *
237
+ * The label need NOT be registered: `setLocation` accepts a free-form string
238
+ * and `listLocations` folds in whatever devices carry, so requiring
239
+ * registration here would leave exactly the ad-hoc rooms unable to have an
240
+ * icon. An empty label throws — there is no room to name.
241
+ *
242
+ * Same read-modify-write and same lock as `addLocation`: the map is one key of
243
+ * the addon store, and `writeAddonStore` re-inserts the whole key range, so an
244
+ * unordered write carries a stale copy of every other key.
245
+ */
246
+ export declare function setLocationIcon(pctx: ProviderContext, input: Parameters<IDeviceManagerProvider['setLocationIcon']>[0]): ReturnType<IDeviceManagerProvider['setLocationIcon']>;
215
247
  /**
216
248
  * Remove a label from the curated registry. Match is
217
249
  * case-insensitive. Devices that still reference this label keep
@@ -1,4 +1,4 @@
1
- import { ChildLayout, DeviceDisplayOverride, RoleDisplayDefault } from '@camstack/types';
1
+ import { ChildLayout, DeviceDisplayOverride, LocationIconId, RoleDisplayDefault } from '@camstack/types';
2
2
  /**
3
3
  * Wire shape matching `z.infer<typeof SettingsSchemaWithValuesSchema>` —
4
4
  * duplicated as a plain interface because importing the Zod schema across
@@ -161,6 +161,22 @@ export interface AddonStore {
161
161
  * the operator forgot to register a label before assigning it.
162
162
  */
163
163
  locations?: readonly string[];
164
+ /**
165
+ * Icon per location, keyed by `locationIconKey(label)` — trimmed and
166
+ * lower-cased, the same equality the registry itself matches on. Lives HERE,
167
+ * beside `locations`, because that is where a location exists at all: a
168
+ * location is a label, not a row, so there is no record to hang an icon on.
169
+ *
170
+ * Keyed on the folded label rather than the operator's casing on purpose. A
171
+ * case-only rename (`cucina` → `Cucina`) is a real edit this repo supports,
172
+ * and a map keyed on the displayed spelling would silently orphan its entry
173
+ * on exactly that edit — the icon would vanish and nothing would say why.
174
+ *
175
+ * A value outside `LOCATION_ICON_IDS` is DROPPED on decode, not trusted: the
176
+ * store is JSON on disk and a renderer handed an unknown id can only draw a
177
+ * hole. Dropping it degrades the location to the fallback pin instead.
178
+ */
179
+ locationIcons?: Readonly<Record<string, LocationIconId>>;
164
180
  /** Operator-authored per-role display defaults (unit/precision/icon), keyed
165
181
  * by `DeviceRole` string. Resolution merges these UNDER any per-device
166
182
  * `display` override. Written whole-record by `setRoleDisplayDefaults`
@@ -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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { F as bareAddonId, J as doorbellCapability, Jt as BaseAddon, Q as evaluateSensorEdge, b as SOURCE_DEVICE_TYPES, bn as EventCategory, ct as isSameAddonId, en as DeviceType, lt as isSourceCap, qt as errMsg, sn as createEvent, y as SOURCE_CAPS } from "../../dist-BjSYm1hH.mjs";
1
+ import { $ as evaluateSensorEdge, I as bareAddonId, Sn as EventCategory, Xt as BaseAddon, Y as doorbellCapability, Yt as errMsg, b as SOURCE_CAPS, ln as createEvent, lt as isSameAddonId, nn as DeviceType, ut as isSourceCap, x as SOURCE_DEVICE_TYPES } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, ht as logDestinationCapability } from "../../dist-BjSYm1hH.mjs";
1
+ import { Xt as BaseAddon, _t as logDestinationCapability } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
3
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, bn as EventCategory, qt as errMsg, sn as createEvent, v as RecordingStatusSchema } from "../../dist-BjSYm1hH.mjs";
1
+ import { Sn as EventCategory, Xt as BaseAddon, Yt as errMsg, ln as createEvent, y as RecordingStatusSchema } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { C as ScopedTokenSchema, Jt as BaseAddon, N as authProviderCapability, O as UserRecordSchema, Ut as userManagementCapability, r as ApiKeyRecordSchema } from "../../dist-BjSYm1hH.mjs";
2
+ import { Gt as userManagementCapability, P as authProviderCapability, Xt as BaseAddon, k as UserRecordSchema, r as ApiKeyRecordSchema, w as ScopedTokenSchema } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
3
+ const require_dist = require("../../dist-D6kqBW1G.js");
4
4
  const require_tls = require("../../tls-BFs4PzoW.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 { Jt as BaseAddon, bn as EventCategory, mt as localNetworkCapability } from "../../dist-BjSYm1hH.mjs";
1
+ import { Sn as EventCategory, Xt as BaseAddon, ht as localNetworkCapability } from "../../dist-B4htkDVv.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-2xbgg78V.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, ht as logDestinationCapability } from "../../dist-BjSYm1hH.mjs";
1
+ import { Xt as BaseAddon, _t as logDestinationCapability } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Dt as reducePoints, Jt as BaseAddon, Ot as resolveBucketMs, bn as EventCategory, bt as metricsProviderCapability, nt as foldSnapshotByFunction, qt as errMsg, sn as createEvent } from "../../dist-BjSYm1hH.mjs";
1
+ import { At as resolveBucketMs, Sn as EventCategory, St as metricsProviderCapability, Xt as BaseAddon, Yt as errMsg, kt as reducePoints, ln as createEvent, rt as foldSnapshotByFunction } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Ft as scoreRuntimes, Jt as BaseAddon, Tt as platformProbeCapability, Y as enumerateInferenceDevices, bn as EventCategory, ln as emitReadiness, qt as errMsg } from "../../dist-BjSYm1hH.mjs";
2
+ import { Dt as platformProbeCapability, Lt as scoreRuntimes, Sn as EventCategory, X as enumerateInferenceDevices, Xt as BaseAddon, Yt as errMsg, dn as emitReadiness } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, bn as EventCategory } from "../../dist-BjSYm1hH.mjs";
1
+ import { Sn as EventCategory, Xt as BaseAddon } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-oeTWCXPv.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { F as bareAddonId, Ht as streamQualityLabel, I as batteryCapability, Jt as BaseAddon, Lt as snapshotCapability, Qt as DeviceFeature, W as deriveBatteryPresence, bn as EventCategory, en as DeviceType, i as BatteryStatusSchema, pn as nodePin, qt as errMsg } from "../../dist-BjSYm1hH.mjs";
1
+ import { G as deriveBatteryPresence, I as bareAddonId, L as batteryCapability, Sn as EventCategory, Wt as streamQualityLabel, Xt as BaseAddon, Yt as errMsg, en as DeviceFeature, hn as nodePin, i as BatteryStatusSchema, nn as DeviceType, zt as snapshotCapability } from "../../dist-B4htkDVv.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-LoJNgHHZ.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, Vt as storageProviderCapability, tt as filesystemBrowseCapability } from "../../dist-BjSYm1hH.mjs";
1
+ import { Ut as storageProviderCapability, Xt as BaseAddon, nt as filesystemBrowseCapability } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { $ as evictionPolicyOfLocation, At as resolveLocationMode, D as StorageMigrationJobSchema, E as StorageLocationTypeSchema, It as settingsStoreCapability, Jt as BaseAddon, Kt as withLocationMode, R as collectSecretConfigKeys, Rt as storageCapability, T as StorageLocationModeSchema, bn as EventCategory, ft as legacyModeOf, g as REDACTED_SECRET, m as LegacyStorageLocationDefaultSchema, mn as parseJsonObject, ot as isLocationEnabled, rt as gbToBytes, vt as mayReadLocation, w as StorageCleanupJobSchema, xt as modeMayWrite, yt as mayWriteToLocation, zt as storageMigrationCapability } from "../../dist-BjSYm1hH.mjs";
1
+ import { Bt as storageCapability, Ct as modeMayWrite, D as StorageLocationTypeSchema, E as StorageLocationModeSchema, Jt as withLocationMode, Mt as resolveLocationMode, O as StorageMigrationJobSchema, Rt as settingsStoreCapability, Sn as EventCategory, T as StorageCleanupJobSchema, Vt as storageMigrationCapability, Xt as BaseAddon, _ as REDACTED_SECRET, bt as mayReadLocation, et as evictionPolicyOfLocation, gn as parseJsonObject, it as gbToBytes, m as LegacyStorageLocationDefaultSchema, pt as legacyModeOf, st as isLocationEnabled, xt as mayWriteToLocation, z as collectSecretConfigKeys } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, dn as hydrateSchema, qt as errMsg } from "../../dist-BjSYm1hH.mjs";
1
+ import { Xt as BaseAddon, Yt as errMsg, pn as hydrateSchema } from "../../dist-B4htkDVv.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-C6JIhqS6.js");
6
+ const require_dist = require("../../dist-D6kqBW1G.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 { Jt as BaseAddon, ht as logDestinationCapability } from "../../dist-BjSYm1hH.mjs";
1
+ import { Xt as BaseAddon, _t as logDestinationCapability } from "../../dist-B4htkDVv.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";