@camstack/addon-model-studio 1.1.263 → 1.1.265

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 (17) hide show
  1. package/dist/{MotionZonesSettings-DtoUJxd6.mjs → MotionZonesSettings-DXOf3xDp.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-Bd5lL0CO.mjs → PrivacyMaskSettings-DasEXHXg.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-BG0CBUrr.mjs → SceneMonitorEditor-ClVvQVl6.mjs} +3 -3
  4. package/dist/_stub.js +3115 -3093
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-DANjfgEB.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-DSIDYb1S.mjs} +3 -3
  6. package/dist/{_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DlW3IlLM.mjs → _virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B0F3F6BP.mjs} +1 -1
  7. package/dist/{hostInit-DE_6Mq5X.mjs → hostInit-DuCPmdPM.mjs} +2 -2
  8. package/dist/model-studio.addon.js +177 -26
  9. package/dist/model-studio.addon.mjs +177 -26
  10. package/dist/{player-overlays-DXeDukPC.mjs → player-overlays-BWxIhTSh.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-D9Bo78lO.mjs → responsive-Bb8t1Rws.mjs} +1 -1
  13. package/dist/{scene-monitor-copy-CQr7egaT.mjs → scene-monitor-copy-Bps8TGiG.mjs} +1 -1
  14. package/dist/{square-d__poSpa.mjs → square-DSJt_nVw.mjs} +1 -1
  15. package/dist/{trash-2-C0XdoZQu.mjs → trash-2-CzUTU-oX.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CxkFEexB.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-LnfkvJQ6.mjs} +1 -1
  17. package/package.json +1 -1
@@ -5782,7 +5782,7 @@ var ZodIssueCode = {
5782
5782
  var ZodFirstPartyTypeKind;
5783
5783
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5784
5784
  //#endregion
5785
- //#region ../types/dist/sleep-DRqxtc0C.mjs
5785
+ //#region ../types/dist/sleep-bwsTj221.mjs
5786
5786
  /**
5787
5787
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5788
5788
  * window to float samples (D455).
@@ -7033,7 +7033,6 @@ function createHandle(raw, opts) {
7033
7033
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
7034
7034
  const readAddonStoreResult = raw.readAddonStoreResult;
7035
7035
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
7036
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
7037
7036
  const overrides = {
7038
7037
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
7039
7038
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -7057,12 +7056,12 @@ function createHandle(raw, opts) {
7057
7056
  };
7058
7057
  } } : {},
7059
7058
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
7060
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
7061
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
7059
+ readDeviceStoreBatch: async (deviceIds) => {
7060
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
7062
7061
  const out = /* @__PURE__ */ new Map();
7063
7062
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
7064
7063
  return out;
7065
- } } : {},
7064
+ },
7066
7065
  writeAddonStore: async (patch) => {
7067
7066
  await writeAddon(patch);
7068
7067
  },
@@ -7889,7 +7888,7 @@ var BaseAddon = class {
7889
7888
  return this._addonSecretTree;
7890
7889
  }
7891
7890
  deviceSecretTree() {
7892
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7891
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
7893
7892
  return this._deviceSecretTree;
7894
7893
  }
7895
7894
  /**
@@ -7965,42 +7964,135 @@ var BaseAddon = class {
7965
7964
  this[timerOf] = timer;
7966
7965
  }
7967
7966
  /**
7968
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
7969
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
7970
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
7971
- * re-initialize) doesn't propagate up into `initialize()` and leave the
7967
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
7968
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
7972
7969
  * addon permanently broken.
7973
7970
  *
7974
- * Retry only the two known infra fingerprints. Anything else propagates so
7975
- * real bugs surface immediately. After the budget expires we fall back to
7976
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
7971
+ * Two kinds of outage, two budgets:
7972
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
7973
+ * initialized`, `provider not available`): a short budget, then the addon
7974
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
7975
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
7976
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
7977
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
7978
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
7979
+ * loop. A long, bounded budget; only after it the error propagates - the
7980
+ * addon never runs on defaults for a store it could not ask.
7981
+ * Anything else propagates immediately so real bugs surface.
7977
7982
  */
7978
7983
  async readAddonStoreWithRetry() {
7979
7984
  const settings = this.rawSettings();
7980
7985
  if (!settings) return {};
7981
- const delaysMs = [
7982
- 150,
7983
- 350,
7984
- 600,
7985
- 900
7986
- ];
7987
7986
  let lastErr;
7988
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7987
+ for (let attempt = 0;; attempt++) try {
7989
7988
  const stored = await settings.readAddonStore() ?? {};
7990
7989
  this.settingsStoreReady = true;
7991
7990
  return stored;
7992
7991
  } catch (err) {
7993
7992
  lastErr = err;
7994
- const msg = err instanceof Error ? err.message : String(err);
7995
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
7996
- if (attempt === delaysMs.length) break;
7997
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7993
+ const kind = classifyStoreError(err);
7994
+ if (kind === "permanent") throw err;
7995
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
7996
+ if (delay === void 0) {
7997
+ if (kind === "transport") throw err;
7998
+ break;
7999
+ }
8000
+ await new Promise((r) => setTimeout(r, delay));
7998
8001
  }
7999
8002
  this.settingsStoreReady = false;
8000
8003
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
8001
8004
  return {};
8002
8005
  }
8003
8006
  };
8007
+ /** The settings store's own restart fingerprints. */
8008
+ var RESTART_RETRY_DELAYS_MS = [
8009
+ 150,
8010
+ 350,
8011
+ 600,
8012
+ 900
8013
+ ];
8014
+ /** A hub restarting under a booting runner: ~100 s in total. */
8015
+ var TRANSPORT_RETRY_DELAYS_MS = [
8016
+ 150,
8017
+ 350,
8018
+ 600,
8019
+ 900,
8020
+ 2e3,
8021
+ 5e3,
8022
+ 1e4,
8023
+ 2e4,
8024
+ 3e4,
8025
+ 3e4
8026
+ ];
8027
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
8028
+ /** Node / socket error codes of a connection that did not happen or broke. */
8029
+ var TRANSPORT_CODES = new Set([
8030
+ "ECONNREFUSED",
8031
+ "ECONNRESET",
8032
+ "ETIMEDOUT",
8033
+ "EPIPE",
8034
+ "EHOSTUNREACH",
8035
+ "ENETUNREACH"
8036
+ ]);
8037
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
8038
+ var UNAVAILABLE_TRPC_CODES = new Set([
8039
+ "TIMEOUT",
8040
+ "SERVICE_UNAVAILABLE",
8041
+ "BAD_GATEWAY",
8042
+ "GATEWAY_TIMEOUT"
8043
+ ]);
8044
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
8045
+ var TRANSPORT_PHRASES = [
8046
+ "discovery deadline exceeded",
8047
+ "parent could not route child unowned cap call",
8048
+ "UDS request timed out after",
8049
+ "channel closed"
8050
+ ];
8051
+ /** Moleculer error `type`s of a call that could not be served right now. */
8052
+ var MOLECULER_TRANSIENT_TYPES = new Set([
8053
+ "REQUEST_TIMEOUT",
8054
+ "SERVICE_NOT_FOUND",
8055
+ "SERVICE_NOT_AVAILABLE",
8056
+ "REQUEST_REJECTED",
8057
+ "BROKER_DISCONNECTED"
8058
+ ]);
8059
+ function stringProp(value, key) {
8060
+ if (typeof value !== "object" || value === null) return void 0;
8061
+ const found = Reflect.get(value, key);
8062
+ return typeof found === "string" ? found : void 0;
8063
+ }
8064
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
8065
+ function errorCode(err) {
8066
+ const out = {};
8067
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
8068
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
8069
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
8070
+ if (node !== void 0) out.node = node;
8071
+ const trpc = stringProp(data, "code");
8072
+ if (trpc !== void 0) out.trpc = trpc;
8073
+ return out;
8074
+ }
8075
+ /**
8076
+ * Is a settings read failure worth retrying, and with which budget? Only
8077
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
8078
+ * one of the exact phrases our own transport raises. A message that merely
8079
+ * contains "timed out" or "unreachable" is not evidence of anything, and
8080
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
8081
+ */
8082
+ function classifyStoreError(err) {
8083
+ const message = err instanceof Error ? err.message : String(err);
8084
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
8085
+ const code = errorCode(err);
8086
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
8087
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
8088
+ if (typeof err === "object" && err !== null) {
8089
+ if (Reflect.get(err, "retryable") === true) return "transport";
8090
+ const type = stringProp(err, "type");
8091
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
8092
+ }
8093
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
8094
+ return "permanent";
8095
+ }
8004
8096
  /**
8005
8097
  * Collect the keys of every field marked `perNode: true`, recursing into
8006
8098
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8636,7 +8728,7 @@ object({
8636
8728
  params: record(string(), union([string(), number()])).optional()
8637
8729
  });
8638
8730
  //#endregion
8639
- //#region ../types/dist/err-msg-DLerKiVO.mjs
8731
+ //#region ../types/dist/err-msg--2RrYiLO.mjs
8640
8732
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8641
8733
  DeviceType["Camera"] = "camera";
8642
8734
  DeviceType["Hub"] = "hub";
@@ -8905,6 +8997,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8905
8997
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8906
8998
  * real Switch device for UI rendering / export adapters. */
8907
8999
  DeviceRole["BinaryHelper"] = "binary-helper";
9000
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
9001
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
9002
+ DeviceRole["Outlet"] = "outlet";
8908
9003
  /** Generic motion / occupancy / moving event source. Distinct from
8909
9004
  * the camera accessory PirSensor role: that one is a camera child;
8910
9005
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -8972,7 +9067,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8972
9067
  return DeviceRole;
8973
9068
  }({});
8974
9069
  //#endregion
8975
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
9070
+ //#region ../types/dist/composition-DOwQB_Ua.mjs
8976
9071
  /**
8977
9072
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8978
9073
  *
@@ -16478,6 +16573,28 @@ id: union([string(), number()]) }), {
16478
16573
  kind: "mutation",
16479
16574
  auth: "admin"
16480
16575
  });
16576
+ /** One table of the settings database. */
16577
+ var StorageTableSchema = object({
16578
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
16579
+ name: string(),
16580
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
16581
+ owner: string().nullable(),
16582
+ rows: number().int().nonnegative(),
16583
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
16584
+ bytes: number().int().nonnegative().nullable(),
16585
+ /** Declared by an addon since this engine process started (a running addon claims it). */
16586
+ declaredThisBoot: boolean()
16587
+ });
16588
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
16589
+ collection: string().min(1),
16590
+ expectRows: number().int().nonnegative()
16591
+ }), object({
16592
+ dropped: literal(true),
16593
+ rows: number().int().nonnegative()
16594
+ }), {
16595
+ kind: "mutation",
16596
+ auth: "admin"
16597
+ });
16481
16598
  /**
16482
16599
  * `keychain` — the hub's encrypted store of secret values (D787).
16483
16600
  *
@@ -17019,6 +17136,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
17019
17136
  kind: "mutation",
17020
17137
  auth: "admin",
17021
17138
  caller: "required"
17139
+ }), method(object({
17140
+ ref: string().min(1),
17141
+ value: string().min(1)
17142
+ }), object({
17143
+ ref: string(),
17144
+ version: number().int()
17145
+ }), {
17146
+ kind: "mutation",
17147
+ auth: "admin",
17148
+ caller: "required"
17022
17149
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
17023
17150
  deleted: array(string()).readonly(),
17024
17151
  kept: array(object({
@@ -53470,6 +53597,12 @@ object({
53470
53597
  /** Horizontal louver swing on/off. Null when the device has no
53471
53598
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
53472
53599
  swingHorizontal: boolean().nullable(),
53600
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
53601
+ minTemp: number().nullable().optional(),
53602
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
53603
+ maxTemp: number().nullable().optional(),
53604
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
53605
+ tempStep: number().positive().nullable().optional(),
53473
53606
  /** Ms epoch when the slice was last updated (push or command). */
53474
53607
  lastFetchedAt: number()
53475
53608
  });
@@ -64475,6 +64608,12 @@ Object.freeze({
64475
64608
  addonId: null,
64476
64609
  access: "create"
64477
64610
  },
64611
+ "keychainAdmin.importSecret": {
64612
+ capName: "keychain-admin",
64613
+ capScope: "system",
64614
+ addonId: null,
64615
+ access: "create"
64616
+ },
64478
64617
  "keychainAdmin.listAudit": {
64479
64618
  capName: "keychain-admin",
64480
64619
  capScope: "system",
@@ -68261,6 +68400,18 @@ Object.freeze({
68261
68400
  addonId: null,
68262
68401
  access: "create"
68263
68402
  },
68403
+ "storageAdmin.dropCollection": {
68404
+ capName: "storage-admin",
68405
+ capScope: "system",
68406
+ addonId: null,
68407
+ access: "delete"
68408
+ },
68409
+ "storageAdmin.listTables": {
68410
+ capName: "storage-admin",
68411
+ capScope: "system",
68412
+ addonId: null,
68413
+ access: "view"
68414
+ },
68264
68415
  "storageEvictable.evict": {
68265
68416
  capName: "storage-evictable",
68266
68417
  capScope: "system",
@@ -5756,7 +5756,7 @@ var ZodIssueCode = {
5756
5756
  var ZodFirstPartyTypeKind;
5757
5757
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5758
5758
  //#endregion
5759
- //#region ../types/dist/sleep-DRqxtc0C.mjs
5759
+ //#region ../types/dist/sleep-bwsTj221.mjs
5760
5760
  /**
5761
5761
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5762
5762
  * window to float samples (D455).
@@ -7007,7 +7007,6 @@ function createHandle(raw, opts) {
7007
7007
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
7008
7008
  const readAddonStoreResult = raw.readAddonStoreResult;
7009
7009
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
7010
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
7011
7010
  const overrides = {
7012
7011
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
7013
7012
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -7031,12 +7030,12 @@ function createHandle(raw, opts) {
7031
7030
  };
7032
7031
  } } : {},
7033
7032
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
7034
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
7035
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
7033
+ readDeviceStoreBatch: async (deviceIds) => {
7034
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
7036
7035
  const out = /* @__PURE__ */ new Map();
7037
7036
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
7038
7037
  return out;
7039
- } } : {},
7038
+ },
7040
7039
  writeAddonStore: async (patch) => {
7041
7040
  await writeAddon(patch);
7042
7041
  },
@@ -7863,7 +7862,7 @@ var BaseAddon = class {
7863
7862
  return this._addonSecretTree;
7864
7863
  }
7865
7864
  deviceSecretTree() {
7866
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7865
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
7867
7866
  return this._deviceSecretTree;
7868
7867
  }
7869
7868
  /**
@@ -7939,42 +7938,135 @@ var BaseAddon = class {
7939
7938
  this[timerOf] = timer;
7940
7939
  }
7941
7940
  /**
7942
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
7943
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
7944
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
7945
- * re-initialize) doesn't propagate up into `initialize()` and leave the
7941
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
7942
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
7946
7943
  * addon permanently broken.
7947
7944
  *
7948
- * Retry only the two known infra fingerprints. Anything else propagates so
7949
- * real bugs surface immediately. After the budget expires we fall back to
7950
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
7945
+ * Two kinds of outage, two budgets:
7946
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
7947
+ * initialized`, `provider not available`): a short budget, then the addon
7948
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
7949
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
7950
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
7951
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
7952
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
7953
+ * loop. A long, bounded budget; only after it the error propagates - the
7954
+ * addon never runs on defaults for a store it could not ask.
7955
+ * Anything else propagates immediately so real bugs surface.
7951
7956
  */
7952
7957
  async readAddonStoreWithRetry() {
7953
7958
  const settings = this.rawSettings();
7954
7959
  if (!settings) return {};
7955
- const delaysMs = [
7956
- 150,
7957
- 350,
7958
- 600,
7959
- 900
7960
- ];
7961
7960
  let lastErr;
7962
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7961
+ for (let attempt = 0;; attempt++) try {
7963
7962
  const stored = await settings.readAddonStore() ?? {};
7964
7963
  this.settingsStoreReady = true;
7965
7964
  return stored;
7966
7965
  } catch (err) {
7967
7966
  lastErr = err;
7968
- const msg = err instanceof Error ? err.message : String(err);
7969
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
7970
- if (attempt === delaysMs.length) break;
7971
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7967
+ const kind = classifyStoreError(err);
7968
+ if (kind === "permanent") throw err;
7969
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
7970
+ if (delay === void 0) {
7971
+ if (kind === "transport") throw err;
7972
+ break;
7973
+ }
7974
+ await new Promise((r) => setTimeout(r, delay));
7972
7975
  }
7973
7976
  this.settingsStoreReady = false;
7974
7977
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7975
7978
  return {};
7976
7979
  }
7977
7980
  };
7981
+ /** The settings store's own restart fingerprints. */
7982
+ var RESTART_RETRY_DELAYS_MS = [
7983
+ 150,
7984
+ 350,
7985
+ 600,
7986
+ 900
7987
+ ];
7988
+ /** A hub restarting under a booting runner: ~100 s in total. */
7989
+ var TRANSPORT_RETRY_DELAYS_MS = [
7990
+ 150,
7991
+ 350,
7992
+ 600,
7993
+ 900,
7994
+ 2e3,
7995
+ 5e3,
7996
+ 1e4,
7997
+ 2e4,
7998
+ 3e4,
7999
+ 3e4
8000
+ ];
8001
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
8002
+ /** Node / socket error codes of a connection that did not happen or broke. */
8003
+ var TRANSPORT_CODES = new Set([
8004
+ "ECONNREFUSED",
8005
+ "ECONNRESET",
8006
+ "ETIMEDOUT",
8007
+ "EPIPE",
8008
+ "EHOSTUNREACH",
8009
+ "ENETUNREACH"
8010
+ ]);
8011
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
8012
+ var UNAVAILABLE_TRPC_CODES = new Set([
8013
+ "TIMEOUT",
8014
+ "SERVICE_UNAVAILABLE",
8015
+ "BAD_GATEWAY",
8016
+ "GATEWAY_TIMEOUT"
8017
+ ]);
8018
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
8019
+ var TRANSPORT_PHRASES = [
8020
+ "discovery deadline exceeded",
8021
+ "parent could not route child unowned cap call",
8022
+ "UDS request timed out after",
8023
+ "channel closed"
8024
+ ];
8025
+ /** Moleculer error `type`s of a call that could not be served right now. */
8026
+ var MOLECULER_TRANSIENT_TYPES = new Set([
8027
+ "REQUEST_TIMEOUT",
8028
+ "SERVICE_NOT_FOUND",
8029
+ "SERVICE_NOT_AVAILABLE",
8030
+ "REQUEST_REJECTED",
8031
+ "BROKER_DISCONNECTED"
8032
+ ]);
8033
+ function stringProp(value, key) {
8034
+ if (typeof value !== "object" || value === null) return void 0;
8035
+ const found = Reflect.get(value, key);
8036
+ return typeof found === "string" ? found : void 0;
8037
+ }
8038
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
8039
+ function errorCode(err) {
8040
+ const out = {};
8041
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
8042
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
8043
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
8044
+ if (node !== void 0) out.node = node;
8045
+ const trpc = stringProp(data, "code");
8046
+ if (trpc !== void 0) out.trpc = trpc;
8047
+ return out;
8048
+ }
8049
+ /**
8050
+ * Is a settings read failure worth retrying, and with which budget? Only
8051
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
8052
+ * one of the exact phrases our own transport raises. A message that merely
8053
+ * contains "timed out" or "unreachable" is not evidence of anything, and
8054
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
8055
+ */
8056
+ function classifyStoreError(err) {
8057
+ const message = err instanceof Error ? err.message : String(err);
8058
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
8059
+ const code = errorCode(err);
8060
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
8061
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
8062
+ if (typeof err === "object" && err !== null) {
8063
+ if (Reflect.get(err, "retryable") === true) return "transport";
8064
+ const type = stringProp(err, "type");
8065
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
8066
+ }
8067
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
8068
+ return "permanent";
8069
+ }
7978
8070
  /**
7979
8071
  * Collect the keys of every field marked `perNode: true`, recursing into
7980
8072
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8610,7 +8702,7 @@ object({
8610
8702
  params: record(string(), union([string(), number()])).optional()
8611
8703
  });
8612
8704
  //#endregion
8613
- //#region ../types/dist/err-msg-DLerKiVO.mjs
8705
+ //#region ../types/dist/err-msg--2RrYiLO.mjs
8614
8706
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8615
8707
  DeviceType["Camera"] = "camera";
8616
8708
  DeviceType["Hub"] = "hub";
@@ -8879,6 +8971,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8879
8971
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8880
8972
  * real Switch device for UI rendering / export adapters. */
8881
8973
  DeviceRole["BinaryHelper"] = "binary-helper";
8974
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
8975
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
8976
+ DeviceRole["Outlet"] = "outlet";
8882
8977
  /** Generic motion / occupancy / moving event source. Distinct from
8883
8978
  * the camera accessory PirSensor role: that one is a camera child;
8884
8979
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -8946,7 +9041,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8946
9041
  return DeviceRole;
8947
9042
  }({});
8948
9043
  //#endregion
8949
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
9044
+ //#region ../types/dist/composition-DOwQB_Ua.mjs
8950
9045
  /**
8951
9046
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8952
9047
  *
@@ -16452,6 +16547,28 @@ id: union([string(), number()]) }), {
16452
16547
  kind: "mutation",
16453
16548
  auth: "admin"
16454
16549
  });
16550
+ /** One table of the settings database. */
16551
+ var StorageTableSchema = object({
16552
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
16553
+ name: string(),
16554
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
16555
+ owner: string().nullable(),
16556
+ rows: number().int().nonnegative(),
16557
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
16558
+ bytes: number().int().nonnegative().nullable(),
16559
+ /** Declared by an addon since this engine process started (a running addon claims it). */
16560
+ declaredThisBoot: boolean()
16561
+ });
16562
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
16563
+ collection: string().min(1),
16564
+ expectRows: number().int().nonnegative()
16565
+ }), object({
16566
+ dropped: literal(true),
16567
+ rows: number().int().nonnegative()
16568
+ }), {
16569
+ kind: "mutation",
16570
+ auth: "admin"
16571
+ });
16455
16572
  /**
16456
16573
  * `keychain` — the hub's encrypted store of secret values (D787).
16457
16574
  *
@@ -16993,6 +17110,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
16993
17110
  kind: "mutation",
16994
17111
  auth: "admin",
16995
17112
  caller: "required"
17113
+ }), method(object({
17114
+ ref: string().min(1),
17115
+ value: string().min(1)
17116
+ }), object({
17117
+ ref: string(),
17118
+ version: number().int()
17119
+ }), {
17120
+ kind: "mutation",
17121
+ auth: "admin",
17122
+ caller: "required"
16996
17123
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
16997
17124
  deleted: array(string()).readonly(),
16998
17125
  kept: array(object({
@@ -53444,6 +53571,12 @@ object({
53444
53571
  /** Horizontal louver swing on/off. Null when the device has no
53445
53572
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
53446
53573
  swingHorizontal: boolean().nullable(),
53574
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
53575
+ minTemp: number().nullable().optional(),
53576
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
53577
+ maxTemp: number().nullable().optional(),
53578
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
53579
+ tempStep: number().positive().nullable().optional(),
53447
53580
  /** Ms epoch when the slice was last updated (push or command). */
53448
53581
  lastFetchedAt: number()
53449
53582
  });
@@ -64449,6 +64582,12 @@ Object.freeze({
64449
64582
  addonId: null,
64450
64583
  access: "create"
64451
64584
  },
64585
+ "keychainAdmin.importSecret": {
64586
+ capName: "keychain-admin",
64587
+ capScope: "system",
64588
+ addonId: null,
64589
+ access: "create"
64590
+ },
64452
64591
  "keychainAdmin.listAudit": {
64453
64592
  capName: "keychain-admin",
64454
64593
  capScope: "system",
@@ -68235,6 +68374,18 @@ Object.freeze({
68235
68374
  addonId: null,
68236
68375
  access: "create"
68237
68376
  },
68377
+ "storageAdmin.dropCollection": {
68378
+ capName: "storage-admin",
68379
+ capScope: "system",
68380
+ addonId: null,
68381
+ access: "delete"
68382
+ },
68383
+ "storageAdmin.listTables": {
68384
+ capName: "storage-admin",
68385
+ capScope: "system",
68386
+ addonId: null,
68387
+ access: "view"
68388
+ },
68238
68389
  "storageEvictable.evict": {
68239
68390
  capName: "storage-evictable",
68240
68391
  capScope: "system",
@@ -1,6 +1,6 @@
1
1
  import { S as e, d as t, u as n, v as r } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react__loadShare__.js-DMBKVIH7.mjs";
2
2
  import { n as i, r as a, t as o } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-ds_Ehzaa.mjs";
3
- import { g as s, h as c } from "./responsive-D9Bo78lO.mjs";
3
+ import { g as s, h as c } from "./responsive-Bb8t1Rws.mjs";
4
4
  var l = s("chevron-down", [["path", {
5
5
  d: "m6 9 6 6 6-6",
6
6
  key: "qrunsl"
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-CxkFEexB.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-LnfkvJQ6.mjs";
2
2
  export { t as get, e as init };
@@ -1,6 +1,6 @@
1
1
  import { S as e, d as t, g as n, i as r, l as i, n as a, r as o, u as s, v as c, y as l } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react__loadShare__.js-DMBKVIH7.mjs";
2
2
  import "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-ds_Ehzaa.mjs";
3
- import { h as u } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DlW3IlLM.mjs";
3
+ import { h as u } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B0F3F6BP.mjs";
4
4
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
5
5
  e();
6
6
  var d = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), f = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), p = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), m = (e) => {
@@ -1,6 +1,6 @@
1
1
  import { S as e, g as t, l as n, v as r } from "./_virtual_mf___mfe_internal__addon_model_studio_page__loadShare__react__loadShare__.js-DMBKVIH7.mjs";
2
2
  import { n as i } from "./ui-text-BqZa5BNK.mjs";
3
- import { g as a } from "./responsive-D9Bo78lO.mjs";
3
+ import { g as a } from "./responsive-Bb8t1Rws.mjs";
4
4
  var o = a("camera", [["path", {
5
5
  d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
6
6
  key: "18u6gg"
@@ -1,4 +1,4 @@
1
- import { g as e } from "./responsive-D9Bo78lO.mjs";
1
+ import { g as e } from "./responsive-Bb8t1Rws.mjs";
2
2
  var t = e("eye-off", [
3
3
  ["path", {
4
4
  d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",
@@ -1,4 +1,4 @@
1
- import { g as e } from "./responsive-D9Bo78lO.mjs";
1
+ import { g as e } from "./responsive-Bb8t1Rws.mjs";
2
2
  var t = e("trash-2", [
3
3
  ["path", {
4
4
  d: "M10 11v6",