@camstack/addon-provider-tuya 0.2.125 → 0.2.127

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 (3) hide show
  1. package/dist/addon.js +1404 -131
  2. package/dist/addon.mjs +1404 -131
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -818,7 +818,7 @@ async function discoverDevices(options = {}, binder = createDgramBinder()) {
818
818
  return [...byId.values()];
819
819
  }
820
820
  //#endregion
821
- //#region ../types/dist/event-category-C4hVnRo8.mjs
821
+ //#region ../types/dist/event-category-BvOFi6PY.mjs
822
822
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
823
823
  EventCategory["SystemBoot"] = "system.boot";
824
824
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -882,6 +882,24 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
882
882
  * consumes this category and maps it to the `addon-crash-loop` kind.
883
883
  */
884
884
  EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
885
+ /**
886
+ * A runner whose addon refused to start with a typed reason (D877) - parked
887
+ * in `refused`, never counted by the crash breaker, retried on operator
888
+ * action. One event per refusal.
889
+ */
890
+ EventCategory["AddonRunnerRefused"] = "addon.runner-refused";
891
+ /**
892
+ * A runner settled on a state (D877): `running` (READY), `crashed`, `failed` or
893
+ * `refused`. In-process telemetry for the boot check (re-evaluate while any
894
+ * runner is still pending) and the alert centre (close a recovered runner).
895
+ */
896
+ EventCategory["ProcessStateChanged"] = "process.state-changed";
897
+ /**
898
+ * The node's one-shot runner boot check (D877): planned runners against the
899
+ * process service, `degraded` when any is failed, refused or missing. The
900
+ * alert centre turns it into one alert and a direct critical push.
901
+ */
902
+ EventCategory["SystemRunnerCheck"] = "system.runner-check";
885
903
  EventCategory["AddonError"] = "addon.error";
886
904
  EventCategory["AddonPageReady"] = "addon.page-ready";
887
905
  EventCategory["AddonWidgetReady"] = "addon.widget-ready";
@@ -6206,7 +6224,7 @@ var ZodIssueCode = {
6206
6224
  var ZodFirstPartyTypeKind;
6207
6225
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6208
6226
  //#endregion
6209
- //#region ../types/dist/sleep-DRqxtc0C.mjs
6227
+ //#region ../types/dist/sleep-BegKjwUu.mjs
6210
6228
  /**
6211
6229
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6212
6230
  * window to float samples (D455).
@@ -6323,7 +6341,7 @@ function isKeychainStoredField(field) {
6323
6341
  var SECRET_CLEAR = { $secretClear: true };
6324
6342
  /** The `'***'` token D634 redaction writes for non-admins. */
6325
6343
  var LEGACY_DEVICE_REDACTION = "***";
6326
- function isRecord$2(value) {
6344
+ function isRecord$5(value) {
6327
6345
  return typeof value === "object" && value !== null && !Array.isArray(value);
6328
6346
  }
6329
6347
  function makeSecretMarker(ref) {
@@ -6334,12 +6352,12 @@ function makeSecretMarker(ref) {
6334
6352
  }
6335
6353
  /** A stored marker: an object whose `$secret` is a non-empty string. */
6336
6354
  function isSecretMarker(value) {
6337
- if (!isRecord$2(value)) return false;
6355
+ if (!isRecord$5(value)) return false;
6338
6356
  const ref = value["$secret"];
6339
6357
  return typeof ref === "string" && ref.length > 0;
6340
6358
  }
6341
6359
  function isSecretClear(value) {
6342
- return isRecord$2(value) && value["$secretClear"] === true;
6360
+ return isRecord$5(value) && value["$secretClear"] === true;
6343
6361
  }
6344
6362
  /**
6345
6363
  * The UI view of a stored secret field value: a marker becomes "set", inline
@@ -6949,7 +6967,7 @@ var KeychainWriteRefusedError = class extends Error {
6949
6967
  }
6950
6968
  };
6951
6969
  var KEEP = { kind: "keep" };
6952
- function isRecord$1(value) {
6970
+ function isRecord$4(value) {
6953
6971
  return typeof value === "object" && value !== null && !Array.isArray(value);
6954
6972
  }
6955
6973
  function defaultMintId() {
@@ -6962,7 +6980,7 @@ function collectSecretMarkerRefs(value, out = []) {
6962
6980
  return out;
6963
6981
  }
6964
6982
  if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
6965
- else if (isRecord$1(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6983
+ else if (isRecord$4(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6966
6984
  return out;
6967
6985
  }
6968
6986
  async function storedValueEquals(secrets, marker, candidate) {
@@ -7017,7 +7035,7 @@ function rowId(row, idKey) {
7017
7035
  return null;
7018
7036
  }
7019
7037
  async function writeArray(ctx, node, incoming, stored, fieldPath) {
7020
- const storedRows = Array.isArray(stored) ? stored.filter(isRecord$1) : [];
7038
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord$4) : [];
7021
7039
  if (!Array.isArray(incoming)) {
7022
7040
  ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
7023
7041
  return {
@@ -7033,7 +7051,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
7033
7051
  const kept = /* @__PURE__ */ new Set();
7034
7052
  const rows = [];
7035
7053
  for (const row of incoming) {
7036
- if (!isRecord$1(row)) {
7054
+ if (!isRecord$4(row)) {
7037
7055
  rows.push(row);
7038
7056
  continue;
7039
7057
  }
@@ -7058,7 +7076,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
7058
7076
  case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
7059
7077
  case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
7060
7078
  case "object": {
7061
- if (!isRecord$1(incoming)) {
7079
+ if (!isRecord$4(incoming)) {
7062
7080
  if (incoming === void 0) return KEEP;
7063
7081
  ctx.deletes.push(...collectSecretMarkerRefs(stored));
7064
7082
  return {
@@ -7066,7 +7084,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
7066
7084
  value: incoming
7067
7085
  };
7068
7086
  }
7069
- const storedObj = isRecord$1(stored) ? stored : {};
7087
+ const storedObj = isRecord$4(stored) ? stored : {};
7070
7088
  return {
7071
7089
  kind: "value",
7072
7090
  value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
@@ -7189,7 +7207,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
7189
7207
  value: items
7190
7208
  };
7191
7209
  }
7192
- if (isRecord$1(v)) {
7210
+ if (isRecord$4(v)) {
7193
7211
  const out = {};
7194
7212
  for (const [k, child] of Object.entries(v)) {
7195
7213
  const s = substitute(child);
@@ -7221,7 +7239,7 @@ var SecretOverwriteRefusedError = class extends Error {
7221
7239
  this.name = "SecretOverwriteRefusedError";
7222
7240
  }
7223
7241
  };
7224
- function isRecord(value) {
7242
+ function isRecord$3(value) {
7225
7243
  return typeof value === "object" && value !== null && !Array.isArray(value);
7226
7244
  }
7227
7245
  function resolvedValue(pass, marker) {
@@ -7250,7 +7268,7 @@ function inArray(pass, incoming, stored) {
7250
7268
  return incoming.map((item, index) => {
7251
7269
  const twin = stored[index];
7252
7270
  if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
7253
- if (isRecord(item) && isRecord(twin)) return inArrayRow(pass, item, twin);
7271
+ if (isRecord$3(item) && isRecord$3(twin)) return inArrayRow(pass, item, twin);
7254
7272
  if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
7255
7273
  return item;
7256
7274
  });
@@ -7261,7 +7279,7 @@ function inArrayRow(pass, incoming, stored) {
7261
7279
  const twin = stored[key];
7262
7280
  if (isSecretMarker(twin)) {
7263
7281
  if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
7264
- } else if (isRecord(value) && isRecord(twin)) out[key] = inArrayRow(pass, value, twin);
7282
+ } else if (isRecord$3(value) && isRecord$3(twin)) out[key] = inArrayRow(pass, value, twin);
7265
7283
  else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
7266
7284
  }
7267
7285
  return out;
@@ -7269,7 +7287,7 @@ function inArrayRow(pass, incoming, stored) {
7269
7287
  async function walk(pass, incoming, stored) {
7270
7288
  if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
7271
7289
  if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
7272
- if (!isRecord(incoming) || !isRecord(stored)) return incoming;
7290
+ if (!isRecord$3(incoming) || !isRecord$3(stored)) return incoming;
7273
7291
  const out = { ...incoming };
7274
7292
  for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
7275
7293
  return out;
@@ -7300,7 +7318,7 @@ async function restoreUnchangedSecrets(input) {
7300
7318
  function isJsonEqual(a, b) {
7301
7319
  if (Object.is(a, b)) return true;
7302
7320
  if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
7303
- if (isRecord(a) && isRecord(b)) {
7321
+ if (isRecord$3(a) && isRecord$3(b)) {
7304
7322
  const keys = Object.keys(a);
7305
7323
  if (keys.length !== Object.keys(b).length) return false;
7306
7324
  return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
@@ -7324,7 +7342,7 @@ function markerKeysOf(blob, owner) {
7324
7342
  }
7325
7343
  function nodeFromStored(value, owner) {
7326
7344
  if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
7327
- if (!isRecord(value)) return null;
7345
+ if (!isRecord$3(value)) return null;
7328
7346
  const children = /* @__PURE__ */ new Map();
7329
7347
  for (const [key, child] of Object.entries(value)) {
7330
7348
  const node = nodeFromStored(child, owner);
@@ -7457,7 +7475,6 @@ function createHandle(raw, opts) {
7457
7475
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
7458
7476
  const readAddonStoreResult = raw.readAddonStoreResult;
7459
7477
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
7460
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
7461
7478
  const overrides = {
7462
7479
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
7463
7480
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -7481,12 +7498,12 @@ function createHandle(raw, opts) {
7481
7498
  };
7482
7499
  } } : {},
7483
7500
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
7484
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
7485
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
7501
+ readDeviceStoreBatch: async (deviceIds) => {
7502
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
7486
7503
  const out = /* @__PURE__ */ new Map();
7487
7504
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
7488
7505
  return out;
7489
- } } : {},
7506
+ },
7490
7507
  writeAddonStore: async (patch) => {
7491
7508
  await writeAddon(patch);
7492
7509
  },
@@ -8113,7 +8130,7 @@ var BaseAddon = class {
8113
8130
  if (!ctx) return;
8114
8131
  const addonId = ctx.id;
8115
8132
  setImmediate(() => {
8116
- ctx.api.addons?.restartAddon?.mutate({ addonId }).then(() => {
8133
+ ctx.api.addons.restartAddon.mutate({ addonId }).then(() => {
8117
8134
  ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: patchKeys.filter((k) => restartKeys.has(k)) } });
8118
8135
  }).catch((err) => {
8119
8136
  ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
@@ -8313,7 +8330,7 @@ var BaseAddon = class {
8313
8330
  return this._addonSecretTree;
8314
8331
  }
8315
8332
  deviceSecretTree() {
8316
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
8333
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
8317
8334
  return this._deviceSecretTree;
8318
8335
  }
8319
8336
  /**
@@ -8389,42 +8406,135 @@ var BaseAddon = class {
8389
8406
  this[timerOf] = timer;
8390
8407
  }
8391
8408
  /**
8392
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
8393
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
8394
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
8395
- * re-initialize) doesn't propagate up into `initialize()` and leave the
8409
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
8410
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
8396
8411
  * addon permanently broken.
8397
8412
  *
8398
- * Retry only the two known infra fingerprints. Anything else propagates so
8399
- * real bugs surface immediately. After the budget expires we fall back to
8400
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
8413
+ * Two kinds of outage, two budgets:
8414
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
8415
+ * initialized`, `provider not available`): a short budget, then the addon
8416
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
8417
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
8418
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
8419
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
8420
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
8421
+ * loop. A long, bounded budget; only after it the error propagates - the
8422
+ * addon never runs on defaults for a store it could not ask.
8423
+ * Anything else propagates immediately so real bugs surface.
8401
8424
  */
8402
8425
  async readAddonStoreWithRetry() {
8403
8426
  const settings = this.rawSettings();
8404
8427
  if (!settings) return {};
8405
- const delaysMs = [
8406
- 150,
8407
- 350,
8408
- 600,
8409
- 900
8410
- ];
8411
8428
  let lastErr;
8412
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
8429
+ for (let attempt = 0;; attempt++) try {
8413
8430
  const stored = await settings.readAddonStore() ?? {};
8414
8431
  this.settingsStoreReady = true;
8415
8432
  return stored;
8416
8433
  } catch (err) {
8417
8434
  lastErr = err;
8418
- const msg = err instanceof Error ? err.message : String(err);
8419
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
8420
- if (attempt === delaysMs.length) break;
8421
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
8435
+ const kind = classifyStoreError(err);
8436
+ if (kind === "permanent") throw err;
8437
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
8438
+ if (delay === void 0) {
8439
+ if (kind === "transport") throw err;
8440
+ break;
8441
+ }
8442
+ await new Promise((r) => setTimeout(r, delay));
8422
8443
  }
8423
8444
  this.settingsStoreReady = false;
8424
8445
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
8425
8446
  return {};
8426
8447
  }
8427
8448
  };
8449
+ /** The settings store's own restart fingerprints. */
8450
+ var RESTART_RETRY_DELAYS_MS = [
8451
+ 150,
8452
+ 350,
8453
+ 600,
8454
+ 900
8455
+ ];
8456
+ /** A hub restarting under a booting runner: ~100 s in total. */
8457
+ var TRANSPORT_RETRY_DELAYS_MS = [
8458
+ 150,
8459
+ 350,
8460
+ 600,
8461
+ 900,
8462
+ 2e3,
8463
+ 5e3,
8464
+ 1e4,
8465
+ 2e4,
8466
+ 3e4,
8467
+ 3e4
8468
+ ];
8469
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
8470
+ /** Node / socket error codes of a connection that did not happen or broke. */
8471
+ var TRANSPORT_CODES = new Set([
8472
+ "ECONNREFUSED",
8473
+ "ECONNRESET",
8474
+ "ETIMEDOUT",
8475
+ "EPIPE",
8476
+ "EHOSTUNREACH",
8477
+ "ENETUNREACH"
8478
+ ]);
8479
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
8480
+ var UNAVAILABLE_TRPC_CODES = new Set([
8481
+ "TIMEOUT",
8482
+ "SERVICE_UNAVAILABLE",
8483
+ "BAD_GATEWAY",
8484
+ "GATEWAY_TIMEOUT"
8485
+ ]);
8486
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
8487
+ var TRANSPORT_PHRASES = [
8488
+ "discovery deadline exceeded",
8489
+ "parent could not route child unowned cap call",
8490
+ "UDS request timed out after",
8491
+ "channel closed"
8492
+ ];
8493
+ /** Moleculer error `type`s of a call that could not be served right now. */
8494
+ var MOLECULER_TRANSIENT_TYPES = new Set([
8495
+ "REQUEST_TIMEOUT",
8496
+ "SERVICE_NOT_FOUND",
8497
+ "SERVICE_NOT_AVAILABLE",
8498
+ "REQUEST_REJECTED",
8499
+ "BROKER_DISCONNECTED"
8500
+ ]);
8501
+ function stringProp(value, key) {
8502
+ if (typeof value !== "object" || value === null) return void 0;
8503
+ const found = Reflect.get(value, key);
8504
+ return typeof found === "string" ? found : void 0;
8505
+ }
8506
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
8507
+ function errorCode(err) {
8508
+ const out = {};
8509
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
8510
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
8511
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
8512
+ if (node !== void 0) out.node = node;
8513
+ const trpc = stringProp(data, "code");
8514
+ if (trpc !== void 0) out.trpc = trpc;
8515
+ return out;
8516
+ }
8517
+ /**
8518
+ * Is a settings read failure worth retrying, and with which budget? Only
8519
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
8520
+ * one of the exact phrases our own transport raises. A message that merely
8521
+ * contains "timed out" or "unreachable" is not evidence of anything, and
8522
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
8523
+ */
8524
+ function classifyStoreError(err) {
8525
+ const message = err instanceof Error ? err.message : String(err);
8526
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
8527
+ const code = errorCode(err);
8528
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
8529
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
8530
+ if (typeof err === "object" && err !== null) {
8531
+ if (Reflect.get(err, "retryable") === true) return "transport";
8532
+ const type = stringProp(err, "type");
8533
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
8534
+ }
8535
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
8536
+ return "permanent";
8537
+ }
8428
8538
  /**
8429
8539
  * Collect the keys of every field marked `perNode: true`, recursing into
8430
8540
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8514,6 +8624,156 @@ var PeerBytesTicketSchema = object({
8514
8624
  */
8515
8625
  declaredBytes: number().int().nonnegative().nullable()
8516
8626
  });
8627
+ /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8628
+ /** The files a kind may live in. `history` is device-history's (D850), never records. */
8629
+ var RECORD_DATABASES = ["settings", "events"];
8630
+ /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8631
+ var RECORD_CLASSES = [
8632
+ "config",
8633
+ "ledger",
8634
+ "audit",
8635
+ "mirror",
8636
+ "ephemeral"
8637
+ ];
8638
+ var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8639
+ var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8640
+ var RECORD_BATCH_MAX = 1e3;
8641
+ var RECORD_QUERY_MAX = 1e3;
8642
+ function defOf(schema) {
8643
+ const zodBag = Reflect.get(schema, "_zod");
8644
+ if (typeof zodBag !== "object" || zodBag === null) return {};
8645
+ const def = Reflect.get(zodBag, "def");
8646
+ if (typeof def !== "object" || def === null) return {};
8647
+ return def;
8648
+ }
8649
+ function declaredHere(schema) {
8650
+ return globalRegistry.get(schema)?.secret === true;
8651
+ }
8652
+ /** Wrapper kinds whose `innerType` is the field itself. */
8653
+ var WRAPPERS = new Set([
8654
+ "optional",
8655
+ "nullable",
8656
+ "default",
8657
+ "prefault",
8658
+ "catch",
8659
+ "readonly",
8660
+ "nonoptional"
8661
+ ]);
8662
+ /**
8663
+ * The schema layers that make up ONE field: the schema itself and every
8664
+ * wrapper below it (optional / default / nullable / readonly / catch), plus
8665
+ * both sides of a pipe (`z.preprocess` puts the real schema on `out`,
8666
+ * `.transform` on `in`).
8667
+ */
8668
+ function layersOf(schema) {
8669
+ const out = [];
8670
+ const queue = [schema];
8671
+ while (queue.length > 0) {
8672
+ const s = queue.shift();
8673
+ if (!s || out.includes(s)) continue;
8674
+ out.push(s);
8675
+ const def = defOf(s);
8676
+ if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
8677
+ if (def.type === "pipe") {
8678
+ if (def.in) queue.push(def.in);
8679
+ if (def.out) queue.push(def.out);
8680
+ }
8681
+ }
8682
+ return out;
8683
+ }
8684
+ /** True when any layer of this field declares `.meta({ secret: true })`. */
8685
+ function isZodSecret(schema) {
8686
+ return layersOf(schema).some(declaredHere);
8687
+ }
8688
+ /**
8689
+ * Every declared secret path in a config schema: dotted keys, `[]` for "each
8690
+ * element of this array" (`connection.password`, `rows[].token`). A declared
8691
+ * object or array is reported as one path and not descended into — everything
8692
+ * under it is secret.
8693
+ */
8694
+ function collectZodSecretPaths(schema, prefix = "") {
8695
+ if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
8696
+ const paths = [];
8697
+ for (const layer of layersOf(schema)) {
8698
+ const def = defOf(layer);
8699
+ if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
8700
+ else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
8701
+ else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
8702
+ else if (def.type === "intersection") {
8703
+ if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
8704
+ if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
8705
+ }
8706
+ }
8707
+ return [...new Set(paths)];
8708
+ }
8709
+ /**
8710
+ * Record kinds (D881): the declaration an addon makes of one kind of fact, the
8711
+ * Zod row schema that describes it, and the helper that turns the two into the
8712
+ * JSON the engine stores.
8713
+ *
8714
+ * A kind is declared ONCE, next to the code that owns it, and the engine's
8715
+ * reconciler creates its indexes - nothing creates DDL at runtime from a write.
8716
+ * Everything the generic machinery needs (secret fields, the entry descriptor)
8717
+ * is DERIVED from the Zod schema: `.meta({ secret: true })` marks a credential,
8718
+ * never a field name (D879).
8719
+ */
8720
+ /** A JSON field path: `trackId`, `auth.accessToken`. Charset-restricted: it is rendered into DDL. */
8721
+ var RecordFieldPathSchema = string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}(\.[A-Za-z_][A-Za-z0-9_]{0,63}){0,3}$/);
8722
+ var RecordIndexSchema = object({
8723
+ name: string().regex(/^[a-z][a-z0-9_]{0,31}$/),
8724
+ fields: array(RecordFieldPathSchema).min(1).max(4),
8725
+ unique: boolean().optional()
8726
+ }).strict();
8727
+ var RecordRetentionSchema = union([
8728
+ object({
8729
+ ttlMs: number().int().positive(),
8730
+ from: _enum(["at", "write"])
8731
+ }).strict(),
8732
+ object({
8733
+ maxRows: number().int().positive().optional(),
8734
+ maxRowsPerDevice: number().int().positive().optional()
8735
+ }).strict().refine((r) => r.maxRows !== void 0 || r.maxRowsPerDevice !== void 0, { message: "name maxRows, maxRowsPerDevice or both" }),
8736
+ object({
8737
+ owner: string().min(1),
8738
+ reason: string().min(1)
8739
+ }).strict(),
8740
+ object({ none: string().min(1) }).strict()
8741
+ ]);
8742
+ /** The descriptor a generic entries UI reads (D881 "generic entries"). */
8743
+ var EntryDescriptorSchema = object({
8744
+ title: string().min(1),
8745
+ /** The record body field shown as the entry's name. */
8746
+ nameField: string().min(1),
8747
+ /** JSON Schema of the entry body; `secret: true` marks a password field. */
8748
+ fields: record(string(), unknown())
8749
+ }).strict();
8750
+ /** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
8751
+ var RecordKindDeclarationSchema = object({
8752
+ database: _enum(RECORD_DATABASES).default("settings"),
8753
+ class: _enum(RECORD_CLASSES),
8754
+ writeTrigger: string().min(1),
8755
+ device: _enum([
8756
+ "required",
8757
+ "optional",
8758
+ "none"
8759
+ ]).default("none"),
8760
+ at: _enum(["required", "none"]).default("none"),
8761
+ indexes: array(RecordIndexSchema).max(8).default([]),
8762
+ retention: RecordRetentionSchema,
8763
+ deviceRemoval: _enum(["delete", "none"]).default("none"),
8764
+ maxDataBytes: number().int().positive().max(RECORD_MAX_DATA_BYTES).default(RECORD_DEFAULT_MAX_DATA_BYTES),
8765
+ /** Derived from the Zod schema; never written by hand. */
8766
+ secretPaths: array(string().min(1)).default([]),
8767
+ /** The id key of each array that carries a secret (`{ headers: 'name' }`). */
8768
+ secretArrayKeys: record(string(), string()).default({}),
8769
+ /** The legacy table this kind adopts from; the boot guard reads it (D853: no migration code). */
8770
+ adoptsFrom: string().min(1).optional(),
8771
+ /** Kinds that must agree through `transact` sit in one file under one family name. */
8772
+ transactFamily: string().min(1).optional(),
8773
+ entry: EntryDescriptorSchema.optional()
8774
+ }).strict();
8775
+ /** The kind names: lower-case, dash separated, short enough for an index name. */
8776
+ var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
8517
8777
  /** Shared Zod schemas used across streaming capabilities. */
8518
8778
  var CamProfileSchema = _enum([
8519
8779
  "high",
@@ -8978,75 +9238,12 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
8978
9238
  kind: "mutation",
8979
9239
  auth: "admin"
8980
9240
  }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
8981
- function defOf(schema) {
8982
- return schema._zod?.def ?? {};
8983
- }
8984
- function declaredHere(schema) {
8985
- return globalRegistry.get(schema)?.secret === true;
8986
- }
8987
- /** Wrapper kinds whose `innerType` is the field itself. */
8988
- var WRAPPERS = new Set([
8989
- "optional",
8990
- "nullable",
8991
- "default",
8992
- "prefault",
8993
- "catch",
8994
- "readonly",
8995
- "nonoptional"
8996
- ]);
8997
- /**
8998
- * The schema layers that make up ONE field: the schema itself and every
8999
- * wrapper below it (optional / default / nullable / readonly / catch), plus
9000
- * both sides of a pipe (`z.preprocess` puts the real schema on `out`,
9001
- * `.transform` on `in`).
9002
- */
9003
- function layersOf(schema) {
9004
- const out = [];
9005
- const queue = [schema];
9006
- while (queue.length > 0) {
9007
- const s = queue.shift();
9008
- if (!s || out.includes(s)) continue;
9009
- out.push(s);
9010
- const def = defOf(s);
9011
- if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
9012
- if (def.type === "pipe") {
9013
- if (def.in) queue.push(def.in);
9014
- if (def.out) queue.push(def.out);
9015
- }
9016
- }
9017
- return out;
9018
- }
9019
- /** True when any layer of this field declares `.meta({ secret: true })`. */
9020
- function isZodSecret(schema) {
9021
- return layersOf(schema).some(declaredHere);
9022
- }
9023
- /**
9024
- * Every declared secret path in a config schema: dotted keys, `[]` for "each
9025
- * element of this array" (`connection.password`, `rows[].token`). A declared
9026
- * object or array is reported as one path and not descended into — everything
9027
- * under it is secret.
9028
- */
9029
- function collectZodSecretPaths(schema, prefix = "") {
9030
- if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
9031
- const paths = [];
9032
- for (const layer of layersOf(schema)) {
9033
- const def = defOf(layer);
9034
- if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
9035
- else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
9036
- else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
9037
- else if (def.type === "intersection") {
9038
- if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
9039
- if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
9040
- }
9041
- }
9042
- return [...new Set(paths)];
9043
- }
9044
9241
  object({
9045
9242
  code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
9046
9243
  params: record(string(), union([string(), number()])).optional()
9047
9244
  });
9048
9245
  //#endregion
9049
- //#region ../types/dist/err-msg-DLerKiVO.mjs
9246
+ //#region ../types/dist/err-msg-BXKzRBwg.mjs
9050
9247
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
9051
9248
  DeviceType["Camera"] = "camera";
9052
9249
  DeviceType["Hub"] = "hub";
@@ -9138,6 +9335,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
9138
9335
  * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
9139
9336
  * integrations sharing the same food/desiccant/hopper surface. */
9140
9337
  DeviceType["PetFeeder"] = "pet-feeder";
9338
+ /** Built-in dishwasher — program lifecycle, door / remote-start gates,
9339
+ * salt and rinse-aid levels. Installed with the `dishwasher` cap.
9340
+ * Sources: native Bosch Home Connect, reusable by similar integrations. */
9341
+ DeviceType["Dishwasher"] = "dishwasher";
9141
9342
  return DeviceType;
9142
9343
  }({});
9143
9344
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -9315,6 +9516,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
9315
9516
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
9316
9517
  * real Switch device for UI rendering / export adapters. */
9317
9518
  DeviceRole["BinaryHelper"] = "binary-helper";
9519
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
9520
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
9521
+ DeviceRole["Outlet"] = "outlet";
9318
9522
  /** Generic motion / occupancy / moving event source. Distinct from
9319
9523
  * the camera accessory PirSensor role: that one is a camera child;
9320
9524
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -9392,7 +9596,7 @@ function errMsg(err) {
9392
9596
  return String(err);
9393
9597
  }
9394
9598
  //#endregion
9395
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
9599
+ //#region ../types/dist/composition-CB6PMT9p.mjs
9396
9600
  /**
9397
9601
  * The ffmpeg build every CamStack node downloads, per platform and arch.
9398
9602
  *
@@ -12596,11 +12800,10 @@ var ClipModelMetaSchema = object({
12596
12800
  /**
12597
12801
  * The `vector-store` index this model's vectors live in. ONE feature space
12598
12802
  * per index: two models share an index only when their vectors are ranked
12599
- * against each other, which for CLIP means never across families. MobileCLIP
12600
- * S1 and S2 share `object-clip` for history (separated by the row's
12601
- * `modelId`); any other model gets its own.
12803
+ * against each other, which for CLIP means never across families. Every
12804
+ * model gets its own.
12602
12805
  */
12603
- vectorIndex: string().regex(/^object-clip(-[a-z0-9][a-z0-9-]*)?$/),
12806
+ vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
12604
12807
  /**
12605
12808
  * The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
12606
12809
  * must be unique per tokenizer: every model's siblings land in ONE flat
@@ -13779,8 +13982,13 @@ var AlertSeveritySchema = _enum([
13779
13982
  "info",
13780
13983
  "success",
13781
13984
  "warning",
13782
- "error"
13985
+ "error",
13986
+ "critical"
13783
13987
  ]);
13988
+ var AlertActionSchema = object({
13989
+ label: string(),
13990
+ href: string().optional()
13991
+ });
13784
13992
  var AlertStatusSchema = _enum([
13785
13993
  "active",
13786
13994
  "in-progress",
@@ -13798,6 +14006,7 @@ var AlertSchema = object({
13798
14006
  severity: AlertSeveritySchema,
13799
14007
  title: string(),
13800
14008
  message: string(),
14009
+ action: AlertActionSchema.optional(),
13801
14010
  status: AlertStatusSchema,
13802
14011
  progress: number().optional(),
13803
14012
  read: boolean(),
@@ -16333,6 +16542,41 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
16333
16542
  keyField: string()
16334
16543
  }).optional()
16335
16544
  })) });
16545
+ /**
16546
+ * The report of a one-shot "move this owner's plaintext secrets into the
16547
+ * keychain" run (D878). COUNTS only - never a value, never a ref.
16548
+ *
16549
+ * The owner runs it on its own rows, through its own write path; an operator
16550
+ * script (`scripts/hub/migrate-raw-secrets.ts`) calls the owner's cap method.
16551
+ * Dry run (`apply: false`) reports what WOULD move and changes nothing.
16552
+ */
16553
+ var RawSecretMigrationInputSchema = object({
16554
+ /** `false` (the default of the script) = report only. */
16555
+ apply: boolean() });
16556
+ var RawSecretMigrationReportSchema = object({
16557
+ apply: boolean(),
16558
+ /** Rows that can carry a secret (a profile, an enrolment). */
16559
+ rows: number().int(),
16560
+ /** Rows whose secret was plaintext when the run started. */
16561
+ raw: number().int(),
16562
+ /** Rows that already held a keychain marker when the run started. */
16563
+ markers: number().int(),
16564
+ /** Plaintext rows moved by THIS run (always 0 on a dry run). */
16565
+ migrated: number().int(),
16566
+ /** Plaintext rows left in place because the move or its read-back failed. */
16567
+ failed: number().int(),
16568
+ /** After the run: rows still holding plaintext. */
16569
+ remainingRaw: number().int(),
16570
+ /** After the run: markers the keychain could not resolve right now. */
16571
+ unresolvedMarkers: number().int(),
16572
+ /**
16573
+ * A second, redundant copy of the secrets' rows that the run found (and, on
16574
+ * `apply`, deleted after verifying the first). Owners with a single copy omit it.
16575
+ */
16576
+ redundantCopies: number().int().optional(),
16577
+ /** True when nothing is raw and every marker resolves. */
16578
+ verified: boolean()
16579
+ });
16336
16580
  /** Where a block runs. The operator chooses — a block driving a device on an
16337
16581
  * agent is the reason placement is not fixed to the hub. */
16338
16582
  var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
@@ -16559,6 +16803,9 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
16559
16803
  }), SettingsUpdateResultSchema, {
16560
16804
  kind: "mutation",
16561
16805
  auth: "admin"
16806
+ }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
16807
+ kind: "mutation",
16808
+ auth: "admin"
16562
16809
  }), method(object({}), object({ libs: array(object({
16563
16810
  filePath: string(),
16564
16811
  content: string()
@@ -16822,6 +17069,347 @@ id: union([string(), number()]) }), {
16822
17069
  auth: "admin"
16823
17070
  });
16824
17071
  /**
17072
+ * records - an addon's own facts and documents (D881): ledgers, audit rows,
17073
+ * config documents, mirrors. The third storage API next to `ctx.settings`
17074
+ * (operator intent, D870) and device-history (numbers over time, D850).
17075
+ *
17076
+ * Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
17077
+ * crossed a process and stated by a hub in-process caller (`ctx.records` does
17078
+ * that with the addon id closed over). It is deliberately NOT in any input
17079
+ * schema, so a session can never supply it, and a call with no stamp is refused
17080
+ * `records/no-caller`. A kind is readable and writable only by its owner; other
17081
+ * addons go through the owner's capability.
17082
+ *
17083
+ * Every read is three-way: a row, `null`, or the call THROWS. A failed read is
17084
+ * never `[]`. Secret fields are never on the wire: a read shows the
17085
+ * redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
17086
+ * resolves itself, and a write may carry only a keychain marker bound to the
17087
+ * row's own ref.
17088
+ *
17089
+ * Internal: addons reach it through `ctx.records`, never directly.
17090
+ */
17091
+ var RecordIdSchema = string().min(1).max(256);
17092
+ /** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
17093
+ var RecordBodySchema = record(string(), unknown());
17094
+ var RecordInputSchema = object({
17095
+ id: RecordIdSchema,
17096
+ /** The device this row belongs to (D865 removal, per-device reads). */
17097
+ deviceId: number().int().optional(),
17098
+ /** The domain clock, ms: observed / created / opened. */
17099
+ at: number().int().optional(),
17100
+ /** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
17101
+ expiresAt: number().int().optional(),
17102
+ data: RecordBodySchema
17103
+ });
17104
+ var RecordViewSchema = object({
17105
+ id: string(),
17106
+ deviceId: number().int().nullable(),
17107
+ at: number().nullable(),
17108
+ expiresAt: number().nullable(),
17109
+ rev: number().int(),
17110
+ updatedAt: number(),
17111
+ data: RecordBodySchema
17112
+ });
17113
+ /** Declared index fields only. Anything else does not compile, by design. */
17114
+ var RecordWhereSchema = record(RecordFieldPathSchema, union([
17115
+ string(),
17116
+ number(),
17117
+ boolean(),
17118
+ object({ in: array(union([
17119
+ string(),
17120
+ number(),
17121
+ boolean()
17122
+ ])).min(1).max(RECORD_BATCH_MAX) }),
17123
+ object({
17124
+ gt: union([string(), number()]).optional(),
17125
+ gte: union([string(), number()]).optional(),
17126
+ lt: union([string(), number()]).optional(),
17127
+ lte: union([string(), number()]).optional()
17128
+ }).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
17129
+ ]));
17130
+ var KindShape = { kind: RecordKindNameSchema };
17131
+ var TimeWindowShape = {
17132
+ deviceId: number().int().optional(),
17133
+ atFrom: number().optional(),
17134
+ atTo: number().optional()
17135
+ };
17136
+ var RecordQueryInputSchema = object({
17137
+ ...KindShape,
17138
+ ...TimeWindowShape,
17139
+ where: RecordWhereSchema.optional(),
17140
+ /** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
17141
+ order: _enum(["at", "id"]).optional(),
17142
+ desc: boolean().optional(),
17143
+ limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
17144
+ /** Opaque keyset cursor from a previous answer. There is no offset. */
17145
+ cursor: string().min(1).optional()
17146
+ });
17147
+ var RecordQueryResultSchema = object({
17148
+ records: array(RecordViewSchema),
17149
+ nextCursor: string().nullable()
17150
+ });
17151
+ var RecordKindStateSchema = _enum([
17152
+ "created",
17153
+ "unchanged",
17154
+ "updated"
17155
+ ]);
17156
+ var DeclareKindResultSchema = object({
17157
+ kindId: number().int(),
17158
+ state: RecordKindStateSchema,
17159
+ createdIndexes: array(string()),
17160
+ /** The boot guard: the kind is empty while its legacy table still holds rows. */
17161
+ adoptionPending: object({
17162
+ legacyTable: string(),
17163
+ legacyRows: number().int()
17164
+ }).nullable()
17165
+ });
17166
+ /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
17167
+ var SecretRefsList = array(string());
17168
+ var RecordSecretRefsSchema = record(string(), record(string(), string()));
17169
+ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
17170
+ kind: "mutation",
17171
+ auth: "admin"
17172
+ }), method(object({
17173
+ ...KindShape,
17174
+ id: RecordIdSchema
17175
+ }), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
17176
+ ...KindShape,
17177
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
17178
+ }), array(RecordViewSchema), { auth: "admin" }), method(object({
17179
+ ...KindShape,
17180
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
17181
+ }), RecordSecretRefsSchema, { auth: "admin" }), method(object({
17182
+ ...KindShape,
17183
+ record: RecordInputSchema,
17184
+ /** Compare-and-set. `0` means "the row must not exist". */
17185
+ ifRev: number().int().min(0).optional()
17186
+ }), object({
17187
+ rev: number().int(),
17188
+ releasedRefs: SecretRefsList
17189
+ }), {
17190
+ kind: "mutation",
17191
+ auth: "admin"
17192
+ }), method(object({
17193
+ ...KindShape,
17194
+ records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
17195
+ }), object({
17196
+ count: number().int(),
17197
+ releasedRefs: SecretRefsList
17198
+ }), {
17199
+ kind: "mutation",
17200
+ auth: "admin"
17201
+ }), method(object({
17202
+ ...KindShape,
17203
+ id: RecordIdSchema,
17204
+ data: RecordBodySchema,
17205
+ at: number().int().nullable().optional(),
17206
+ expiresAt: number().int().nullable().optional(),
17207
+ ifRev: number().int().min(1).optional()
17208
+ }), object({
17209
+ rev: number().int(),
17210
+ releasedRefs: SecretRefsList
17211
+ }), {
17212
+ kind: "mutation",
17213
+ auth: "admin"
17214
+ }), method(object({
17215
+ ...KindShape,
17216
+ id: RecordIdSchema
17217
+ }), object({
17218
+ deleted: boolean(),
17219
+ secretRefs: SecretRefsList
17220
+ }), {
17221
+ kind: "mutation",
17222
+ auth: "admin"
17223
+ }), method(object({
17224
+ ...KindShape,
17225
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
17226
+ }), object({
17227
+ deleted: number().int(),
17228
+ secretRefs: SecretRefsList
17229
+ }), {
17230
+ kind: "mutation",
17231
+ auth: "admin"
17232
+ }), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
17233
+ ...KindShape,
17234
+ ...TimeWindowShape,
17235
+ where: RecordWhereSchema.optional()
17236
+ }), object({ count: number().int() }), { auth: "admin" }), method(object({
17237
+ ...KindShape,
17238
+ deviceId: number().int().optional(),
17239
+ from: number(),
17240
+ to: number(),
17241
+ bucketMs: number().int().positive()
17242
+ }), object({ buckets: array(object({
17243
+ bucketStart: number(),
17244
+ count: number().int()
17245
+ })) }), { auth: "admin" }), method(object({
17246
+ ...KindShape,
17247
+ op: _enum([
17248
+ "sum",
17249
+ "min",
17250
+ "max",
17251
+ "count"
17252
+ ]),
17253
+ field: RecordFieldPathSchema.optional(),
17254
+ groupBy: RecordFieldPathSchema.optional(),
17255
+ where: RecordWhereSchema.optional()
17256
+ }), object({ rows: array(object({
17257
+ group: union([
17258
+ string(),
17259
+ number(),
17260
+ _null()
17261
+ ]),
17262
+ value: number().nullable()
17263
+ })) }), { auth: "admin" }), method(object({
17264
+ ...KindShape,
17265
+ deviceId: number().int().optional(),
17266
+ atBefore: number().optional(),
17267
+ where: RecordWhereSchema.optional()
17268
+ }), object({
17269
+ deleted: number().int(),
17270
+ secretRefs: SecretRefsList
17271
+ }), {
17272
+ kind: "mutation",
17273
+ auth: "admin"
17274
+ }), method(object({
17275
+ deviceId: number().int(),
17276
+ kinds: array(RecordKindNameSchema).optional()
17277
+ }), object({
17278
+ deleted: record(string(), number().int()),
17279
+ secretRefs: SecretRefsList
17280
+ }), {
17281
+ kind: "mutation",
17282
+ auth: "admin"
17283
+ }), method(object(KindShape), object({ id: number().int() }), {
17284
+ kind: "mutation",
17285
+ auth: "admin"
17286
+ });
17287
+ /** One table of the settings database. */
17288
+ var StorageTableSchema = object({
17289
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
17290
+ name: string(),
17291
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
17292
+ owner: string().nullable(),
17293
+ rows: number().int().nonnegative(),
17294
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
17295
+ bytes: number().int().nonnegative().nullable(),
17296
+ /** Declared by an addon since this engine process started (a running addon claims it). */
17297
+ declaredThisBoot: boolean()
17298
+ });
17299
+ /** One declared record kind (D881) as System -> Data shows it. */
17300
+ var StorageKindSchema = object({
17301
+ owner: string(),
17302
+ kind: string(),
17303
+ database: _enum(["settings", "events"]),
17304
+ class: string(),
17305
+ state: _enum([
17306
+ "declared",
17307
+ "orphaned",
17308
+ "dropped"
17309
+ ]),
17310
+ /** `null` when the caller asked for no counts (`counts: false`). */
17311
+ rows: number().int().nonnegative().nullable(),
17312
+ /** Rows x the average size of a sample; null for an empty kind. */
17313
+ bytesEstimate: number().int().nonnegative().nullable(),
17314
+ writeTrigger: string(),
17315
+ retention: RecordRetentionSchema,
17316
+ /** `true` when the kind is an entry kind (the generic entries UI lists it). */
17317
+ entry: boolean(),
17318
+ /** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
17319
+ declaration: RecordKindDeclarationSchema
17320
+ });
17321
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
17322
+ collection: string().min(1),
17323
+ expectRows: number().int().nonnegative()
17324
+ }), object({
17325
+ dropped: literal(true),
17326
+ rows: number().int().nonnegative()
17327
+ }), {
17328
+ kind: "mutation",
17329
+ auth: "admin"
17330
+ }), method(object({
17331
+ owner: string().min(1).optional(),
17332
+ /** `false` skips the per-kind row count (a catalog read only). Default true. */
17333
+ counts: boolean().optional()
17334
+ }), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
17335
+ owner: string().min(1),
17336
+ kind: RecordKindNameSchema
17337
+ }), RecordQueryResultSchema, { auth: "admin" }), method(object({
17338
+ owner: string().min(1),
17339
+ kind: RecordKindNameSchema,
17340
+ records: array(RecordInputSchema).max(RECORD_BATCH_MAX),
17341
+ /** The LAST batch: the copy is complete, the boot guard stops asking. */
17342
+ complete: boolean().optional()
17343
+ }).refine((input) => input.records.length > 0 || input.complete === true, { message: "records must be non-empty unless complete is true" }), object({ adopted: number().int() }), {
17344
+ kind: "mutation",
17345
+ auth: "admin"
17346
+ }), method(object({
17347
+ collection: string().min(1),
17348
+ cursor: string().optional(),
17349
+ limit: number().int().min(1).max(1e3).optional()
17350
+ }), object({
17351
+ rows: array(object({
17352
+ id: string(),
17353
+ data: record(string(), unknown())
17354
+ })),
17355
+ nextCursor: string().nullable()
17356
+ }), { auth: "admin" }), method(object({ collection: string().min(1) }), object({ adopted: boolean() }), { auth: "admin" });
17357
+ /**
17358
+ * record-entries - the ONE admin surface for every list of user-created entries
17359
+ * (D881): notification targets, brokers, LLM profiles, anything an addon
17360
+ * declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
17361
+ *
17362
+ * The UI is generic. `listEntryKinds` hands it, per kind, the JSON Schema of
17363
+ * the entry body with `secret: true` on every credential field, so a password
17364
+ * field is derived from the schema and never from a name. `putEntry` takes the
17365
+ * redaction token for an untouched secret (the stored value is kept), a string
17366
+ * for a new one, and the clear instruction to remove one. A read never carries
17367
+ * a secret value, and a locked keychain refuses the write with
17368
+ * `records/secret-unavailable` instead of storing plaintext.
17369
+ *
17370
+ * Hub-only, served in-process (it acts for each entry's owner on the keychain).
17371
+ */
17372
+ var EntryKindInfoSchema = object({
17373
+ owner: string(),
17374
+ kind: RecordKindNameSchema,
17375
+ title: string(),
17376
+ nameField: string(),
17377
+ /** JSON Schema of the body; `secret: true` marks a password field. */
17378
+ fields: record(string(), unknown()),
17379
+ secretPaths: array(string()),
17380
+ rows: number().int().nonnegative()
17381
+ });
17382
+ var EntryViewSchema = object({
17383
+ id: RecordIdSchema,
17384
+ name: string(),
17385
+ rev: number().int(),
17386
+ updatedAt: number(),
17387
+ /** Secret fields hold the redaction token. */
17388
+ data: RecordBodySchema
17389
+ });
17390
+ var EntryAddress = {
17391
+ owner: string().min(1),
17392
+ kind: RecordKindNameSchema
17393
+ };
17394
+ method(object({}), object({ kinds: array(EntryKindInfoSchema) }), { auth: "admin" }), method(object({ ...EntryAddress }), object({ entries: array(EntryViewSchema) }), { auth: "admin" }), method(object({
17395
+ ...EntryAddress,
17396
+ id: RecordIdSchema.optional(),
17397
+ data: RecordBodySchema,
17398
+ ifRev: number().int().min(1).optional()
17399
+ }), object({
17400
+ id: string(),
17401
+ rev: number().int()
17402
+ }), {
17403
+ kind: "mutation",
17404
+ auth: "admin"
17405
+ }), method(object({
17406
+ ...EntryAddress,
17407
+ id: RecordIdSchema
17408
+ }), object({ deleted: boolean() }), {
17409
+ kind: "mutation",
17410
+ auth: "admin"
17411
+ });
17412
+ /**
16825
17413
  * `keychain` — the hub's encrypted store of secret values (D787).
16826
17414
  *
16827
17415
  * Settings blobs never hold a secret: they hold a marker
@@ -17362,6 +17950,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
17362
17950
  kind: "mutation",
17363
17951
  auth: "admin",
17364
17952
  caller: "required"
17953
+ }), method(object({
17954
+ ref: string().min(1),
17955
+ value: string().min(1)
17956
+ }), object({
17957
+ ref: string(),
17958
+ version: number().int()
17959
+ }), {
17960
+ kind: "mutation",
17961
+ auth: "admin",
17962
+ caller: "required"
17365
17963
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
17366
17964
  deleted: array(string()).readonly(),
17367
17965
  kept: array(object({
@@ -17770,7 +18368,11 @@ var deviceDiscoveryCapability = {
17770
18368
  };
17771
18369
  /**
17772
18370
  * `device-adoption` — generic discovery + adoption surface,
17773
- * system-scoped singleton. The de-HA-ified successor to `ha-discovery`:
18371
+ * system-scoped collection (one provider per integration addon), routed
18372
+ * by explicit `{ addonId }` on direct `deviceAdoption.*` calls — the same
18373
+ * pin model as `connection-test`. The admin UI keeps using
18374
+ * `deviceManager.adoption*` (which already threads `addonId`). The de-HA-ified
18375
+ * successor to `ha-discovery`:
17774
18376
  * keyed by `integrationId` (the addon resolves integrationId → broker
17775
18377
  * internally) rather than a connection-specific `brokerId`, and it
17776
18378
  * REUSES the shared `DiscoveredChildDevice` candidate type from
@@ -17935,7 +18537,7 @@ var ResyncResultSchema = object({
17935
18537
  var deviceAdoptionCapability = {
17936
18538
  name: "device-adoption",
17937
18539
  scope: "system",
17938
- mode: "singleton",
18540
+ mode: "collection",
17939
18541
  status: {
17940
18542
  schema: AdoptionStatusSchema,
17941
18543
  kind: "poll"
@@ -40040,6 +40642,9 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
40040
40642
  }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
40041
40643
  kind: "mutation",
40042
40644
  auth: "admin"
40645
+ }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
40646
+ kind: "mutation",
40647
+ auth: "admin"
40043
40648
  }), method(ProfileRefInputSchema, _void(), {
40044
40649
  kind: "mutation",
40045
40650
  auth: "admin"
@@ -50549,6 +51154,41 @@ var ServerRollbackInfoSchema = object({
50549
51154
  atMs: number(),
50550
51155
  reason: string()
50551
51156
  });
51157
+ /**
51158
+ * The runner boot check (D877): the runners this node PLANNED (hub: one per
51159
+ * `resolveRunnerId` over its forked addons; agent: the runners its process
51160
+ * service holds, because only the hub plans) against the states the process
51161
+ * service reports. Kept in memory, recomputed by the same one-shot check after
51162
+ * every boot, served on `getServerPackageStatus`.
51163
+ *
51164
+ * `failed` is the D6 breaker's terminal state, `refused` a deterministic Init
51165
+ * refusal (an addon that said "I will not start, and here is why"), `missing` a
51166
+ * planned runner the process service has no entry for.
51167
+ */
51168
+ var RunnerRefusalSchema = object({
51169
+ runnerId: string(),
51170
+ addonIds: array(string()),
51171
+ reason: string()
51172
+ });
51173
+ var RunnerBootCheckSchema = object({
51174
+ checkedAtMs: number(),
51175
+ /** Runners planned for this node. */
51176
+ expected: number(),
51177
+ /** Planned runners whose process is `running`. */
51178
+ running: number(),
51179
+ failed: array(string()),
51180
+ refused: array(RunnerRefusalSchema),
51181
+ missing: array(string()),
51182
+ /**
51183
+ * Runners still on their way: `starting`, or `crashed` waiting on a backoff
51184
+ * respawn. The check is FINAL only when this is empty; until then nothing is
51185
+ * pushed and the verdict is re-evaluated on the next runner state change.
51186
+ * Optional for version skew (absent = settled).
51187
+ */
51188
+ pending: array(string()).optional(),
51189
+ /** True when the plan is the hub's (`missing` is then meaningful); false on an agent. */
51190
+ planKnown: boolean()
51191
+ });
50552
51192
  var ServerPackageStatusSchema = object({
50553
51193
  /** Root package name (`@camstack/server` on the hub). */
50554
51194
  packageName: string(),
@@ -50611,7 +51251,14 @@ var ServerPackageStatusSchema = object({
50611
51251
  * for version skew: an older provider's payload simply omits it, and an
50612
51252
  * absent wrapper is unknown — never `native`.
50613
51253
  */
50614
- wrapper: WrapperIdentitySchema.optional()
51254
+ wrapper: WrapperIdentitySchema.optional(),
51255
+ /**
51256
+ * What this node PLANNED to run against what its process service reports
51257
+ * (D877). Absent until the one-shot boot check has run (~60 s after boot) and
51258
+ * on a provider that predates it. Absent is "not checked yet", never "all
51259
+ * well": a consumer that needs the answer treats it as pending.
51260
+ */
51261
+ runners: RunnerBootCheckSchema.optional()
50615
51262
  });
50616
51263
  var ServerUpdateCheckResultSchema = object({
50617
51264
  packageName: string(),
@@ -50662,6 +51309,13 @@ var ServerUpdateOutcomeSchema = _enum([
50662
51309
  "refused",
50663
51310
  "failed"
50664
51311
  ]);
51312
+ /** One addon the pre-flight found the target version would refuse (D877). */
51313
+ var UpdateBlockerSchema = object({
51314
+ addonId: string(),
51315
+ reason: string(),
51316
+ /** What the operator does about it. */
51317
+ remedy: string()
51318
+ });
50665
51319
  var ServerUpdateActionResultSchema = object({
50666
51320
  accepted: boolean(),
50667
51321
  targetVersion: string().nullable(),
@@ -50679,14 +51333,27 @@ var ServerUpdateActionResultSchema = object({
50679
51333
  * The failure itself, verbatim enough to act on (the npm output for a
50680
51334
  * staging install). Non-null only for `failed`.
50681
51335
  */
50682
- error: string().nullable().optional()
51336
+ error: string().nullable().optional(),
51337
+ /**
51338
+ * The addons the target version would refuse to start on this node's live
51339
+ * store (D877), present when the pre-flight refused the update
51340
+ * (`reason: 'addon-would-refuse'`). Optional for version skew.
51341
+ */
51342
+ blockers: array(UpdateBlockerSchema).optional()
50683
51343
  });
50684
51344
  method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
50685
51345
  kind: "mutation",
50686
51346
  auth: "admin"
50687
- }), method(object({
50688
- /** Explicit target version; omitted = latest from the registry. */
50689
- version: string().optional() }), ServerUpdateActionResultSchema, {
51347
+ }), method(object({
51348
+ /** Explicit target version; omitted = latest from the registry. */
51349
+ version: string().optional(),
51350
+ /**
51351
+ * Apply even though the pre-flight (D877) found addons the target would
51352
+ * refuse to start. Off by default: those addons stay down until the
51353
+ * remedy is run, which is the incident this gate exists to prevent.
51354
+ */
51355
+ acknowledgeRefusals: boolean().optional()
51356
+ }), ServerUpdateActionResultSchema, {
50690
51357
  kind: "mutation",
50691
51358
  auth: "admin",
50692
51359
  timeoutMs: 16 * 6e4
@@ -53132,7 +53799,9 @@ var ManifestShapeSchema = object({
53132
53799
  var AddonProcessSchema = object({
53133
53800
  pid: number().optional(),
53134
53801
  mode: string(),
53135
- state: string()
53802
+ state: string(),
53803
+ /** Why the runner is `refused` (an addon's own words), when it is (D877). */
53804
+ reason: string().optional()
53136
53805
  });
53137
53806
  /**
53138
53807
  * Per-addon health snapshot from the kernel's AddonHealthMonitor.
@@ -53900,7 +54569,8 @@ var batteryCapability = {
53900
54569
  DeviceType.Button,
53901
54570
  DeviceType.Switch,
53902
54571
  DeviceType.PetFeeder,
53903
- DeviceType.Container
54572
+ DeviceType.Container,
54573
+ DeviceType.Thermostat
53904
54574
  ],
53905
54575
  methods: {
53906
54576
  /**
@@ -54521,6 +55191,12 @@ var ClimateControlStatusSchema = object({
54521
55191
  /** Horizontal louver swing on/off. Null when the device has no
54522
55192
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
54523
55193
  swingHorizontal: boolean().nullable(),
55194
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
55195
+ minTemp: number().nullable().optional(),
55196
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
55197
+ maxTemp: number().nullable().optional(),
55198
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
55199
+ tempStep: number().positive().nullable().optional(),
54524
55200
  /** Ms epoch when the slice was last updated (push or command). */
54525
55201
  lastFetchedAt: number()
54526
55202
  });
@@ -55375,6 +56051,161 @@ onStatusChanged: { data: object({
55375
56051
  * whether persisting is worth a SQLite commit. */
55376
56052
  volatileStateFields: ["lastChangedAt"]
55377
56053
  };
56054
+ /**
56055
+ * Smart-dishwasher cap — program lifecycle, door / remote-start gates,
56056
+ * consumable levels (salt, rinse aid) and common program options.
56057
+ *
56058
+ * Sources: native Bosch Home Connect (`dishwasher` device type). Reusable
56059
+ * by any integration that exposes the same program / option surface.
56060
+ *
56061
+ * `kind: 'poll'` — cloud REST APIs typically have no push channel; the
56062
+ * provider refreshes the slice on an interval and eagerly after commands.
56063
+ */
56064
+ var DishwasherStateSchema = _enum([
56065
+ "idle",
56066
+ "running",
56067
+ "paused",
56068
+ "finished",
56069
+ "error",
56070
+ "unavailable"
56071
+ ]);
56072
+ var DishwasherConsumableLevelSchema = _enum([
56073
+ "ok",
56074
+ "low",
56075
+ "empty"
56076
+ ]);
56077
+ var DishwasherStatusSchema = object({
56078
+ state: DishwasherStateSchema,
56079
+ /** Active program id/name, or null when none selected / unknown. */
56080
+ program: string().nullable(),
56081
+ /** Program tokens the hardware accepts — drives the UI selector. */
56082
+ availablePrograms: array(string()),
56083
+ /** Minutes remaining on the active cycle, or null when not running. */
56084
+ remainingMinutes: number().int().nonnegative().nullable(),
56085
+ /** Cycle completion 0..100, or null when not applicable. */
56086
+ progressPercent: number().min(0).max(100).nullable(),
56087
+ /** True when the door is reported open. */
56088
+ doorOpen: boolean(),
56089
+ /** True when remote start is allowed (door closed, power on, etc.). */
56090
+ remoteStartAllowed: boolean(),
56091
+ /** Main power / switched-on state. */
56092
+ powerOn: boolean(),
56093
+ saltLevel: DishwasherConsumableLevelSchema.nullable(),
56094
+ rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
56095
+ halfLoad: boolean(),
56096
+ hygienePlus: boolean(),
56097
+ /** Extra dry option when the appliance exposes it. */
56098
+ extraDry: boolean(),
56099
+ /** Intensive zone (lower basket) when the appliance exposes it. */
56100
+ intensiveZone: boolean(),
56101
+ /** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
56102
+ varioSpeedPlus: boolean(),
56103
+ /** Child lock (controls disabled on the appliance). */
56104
+ childLock: boolean(),
56105
+ errorCode: number().nullable(),
56106
+ errorLabel: string().nullable(),
56107
+ lastFetchedAt: number()
56108
+ });
56109
+ var dishwasherCapability = {
56110
+ name: "dishwasher",
56111
+ scope: "device",
56112
+ deviceNative: true,
56113
+ mode: "singleton",
56114
+ deviceTypes: [DeviceType.Dishwasher],
56115
+ methods: {
56116
+ start: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56117
+ kind: "mutation",
56118
+ auth: "protected"
56119
+ }),
56120
+ pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56121
+ kind: "mutation",
56122
+ auth: "protected"
56123
+ }),
56124
+ stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56125
+ kind: "mutation",
56126
+ auth: "protected"
56127
+ }),
56128
+ setProgram: method(object({
56129
+ deviceId: number().int().nonnegative(),
56130
+ program: string().min(1)
56131
+ }), _void(), {
56132
+ kind: "mutation",
56133
+ auth: "protected"
56134
+ }),
56135
+ setPower: method(object({
56136
+ deviceId: number().int().nonnegative(),
56137
+ on: boolean()
56138
+ }), _void(), {
56139
+ kind: "mutation",
56140
+ auth: "protected"
56141
+ }),
56142
+ setHalfLoad: method(object({
56143
+ deviceId: number().int().nonnegative(),
56144
+ on: boolean()
56145
+ }), _void(), {
56146
+ kind: "mutation",
56147
+ auth: "protected"
56148
+ }),
56149
+ setHygienePlus: method(object({
56150
+ deviceId: number().int().nonnegative(),
56151
+ on: boolean()
56152
+ }), _void(), {
56153
+ kind: "mutation",
56154
+ auth: "protected"
56155
+ }),
56156
+ setExtraDry: method(object({
56157
+ deviceId: number().int().nonnegative(),
56158
+ on: boolean()
56159
+ }), _void(), {
56160
+ kind: "mutation",
56161
+ auth: "protected"
56162
+ }),
56163
+ setIntensiveZone: method(object({
56164
+ deviceId: number().int().nonnegative(),
56165
+ on: boolean()
56166
+ }), _void(), {
56167
+ kind: "mutation",
56168
+ auth: "protected"
56169
+ }),
56170
+ setVarioSpeedPlus: method(object({
56171
+ deviceId: number().int().nonnegative(),
56172
+ on: boolean()
56173
+ }), _void(), {
56174
+ kind: "mutation",
56175
+ auth: "protected"
56176
+ }),
56177
+ setChildLock: method(object({
56178
+ deviceId: number().int().nonnegative(),
56179
+ on: boolean()
56180
+ }), _void(), {
56181
+ kind: "mutation",
56182
+ auth: "protected"
56183
+ }),
56184
+ setOption: method(object({
56185
+ deviceId: number().int().nonnegative(),
56186
+ key: string().min(1),
56187
+ value: union([
56188
+ boolean(),
56189
+ string(),
56190
+ number()
56191
+ ])
56192
+ }), _void(), {
56193
+ kind: "mutation",
56194
+ auth: "protected"
56195
+ })
56196
+ },
56197
+ status: {
56198
+ schema: DishwasherStatusSchema,
56199
+ kind: "poll"
56200
+ },
56201
+ runtimeState: DishwasherStatusSchema,
56202
+ durability: "session",
56203
+ history: { fields: { progressPercent: {
56204
+ kind: "gauge",
56205
+ unit: "%",
56206
+ deadband: 2
56207
+ } } }
56208
+ };
55378
56209
  var DoorbellStatusSchema = object({
55379
56210
  /** Ms epoch of the last press. null = never observed since this provider started. */
55380
56211
  lastPressedAt: number().nullable(),
@@ -56177,6 +57008,32 @@ var gasCapability = {
56177
57008
  * whether persisting is worth a SQLite commit. */
56178
57009
  volatileStateFields: ["lastChangedAt"]
56179
57010
  };
57011
+ var HeatingValveStatusSchema = object({
57012
+ /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
57013
+ position: number().min(0).max(100).nullable(),
57014
+ /** Ms epoch when the slice was last updated (push or poll). */
57015
+ lastFetchedAt: number()
57016
+ });
57017
+ var heatingValveCapability = {
57018
+ name: "heating-valve",
57019
+ scope: "device",
57020
+ deviceNative: true,
57021
+ mode: "singleton",
57022
+ deviceTypes: [DeviceType.Thermostat],
57023
+ methods: {},
57024
+ status: {
57025
+ schema: HeatingValveStatusSchema,
57026
+ kind: "push"
57027
+ },
57028
+ runtimeState: HeatingValveStatusSchema,
57029
+ durability: "session",
57030
+ history: { fields: { position: {
57031
+ kind: "gauge",
57032
+ unit: "%",
57033
+ deadband: 1
57034
+ } } },
57035
+ volatileStateFields: ["lastFetchedAt"]
57036
+ };
56180
57037
  /**
56181
57038
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
56182
57039
  * an on/off actuator with an optional target-humidity setpoint and an
@@ -57309,7 +58166,19 @@ var TlsStatusSchema = object({
57309
58166
  sans: array(string()),
57310
58167
  caCertPem: string().nullable(),
57311
58168
  reissueError: string().nullable(),
57312
- restartRequired: boolean()
58169
+ restartRequired: boolean(),
58170
+ /** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
58171
+ caMissing: boolean(),
58172
+ /** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
58173
+ localCaConstrained: boolean().nullable(),
58174
+ /** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
58175
+ caReplacedAt: string().nullable(),
58176
+ /** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
58177
+ storedUpload: object({
58178
+ leafFingerprintSha256: string(),
58179
+ validTo: string(),
58180
+ hasCa: boolean()
58181
+ }).nullable()
57313
58182
  });
57314
58183
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
57315
58184
  /**
@@ -57365,6 +58234,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
57365
58234
  }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
57366
58235
  kind: "mutation",
57367
58236
  auth: "admin"
58237
+ }), method(_void(), TlsStatusSchema, {
58238
+ kind: "mutation",
58239
+ auth: "admin"
57368
58240
  });
57369
58241
  var LockControlStatusSchema = object({
57370
58242
  /** Lifecycle state of the lock. `jammed` means the motor reported
@@ -63858,7 +64730,11 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
63858
64730
  kind: "mutation",
63859
64731
  auth: "admin",
63860
64732
  access: "delete"
63861
- }), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(object({
64733
+ }), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
64734
+ kind: "mutation",
64735
+ auth: "admin",
64736
+ access: "create"
64737
+ }), method(object({
63862
64738
  userId: string(),
63863
64739
  code: string()
63864
64740
  }), object({ valid: boolean() }), {
@@ -65578,6 +66454,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
65578
66454
  dayNight: dayNightCapability,
65579
66455
  deviceDiscovery: deviceDiscoveryCapability,
65580
66456
  deviceStatus: deviceStatusCapability,
66457
+ dishwasher: dishwasherCapability,
65581
66458
  doorbell: doorbellCapability,
65582
66459
  enumSensor: enumSensorCapability,
65583
66460
  eventEmitter: eventEmitterCapability,
@@ -65585,6 +66462,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
65585
66462
  featureProbe: featureProbeCapability,
65586
66463
  flood: floodCapability,
65587
66464
  gas: gasCapability,
66465
+ heatingValve: heatingValveCapability,
65588
66466
  humidifier: humidifierCapability,
65589
66467
  humiditySensor: humiditySensorCapability,
65590
66468
  image: imageCapability,
@@ -65670,13 +66548,14 @@ var BaseDevice = class {
65670
66548
  get state() {
65671
66549
  if (!this._stateProxyCache) {
65672
66550
  const cache = {};
65673
- const handler = { get: (_target, key) => {
66551
+ const handler = { get: (target, key) => {
65674
66552
  const k = key;
65675
- if (k in cache) return cache[k];
66553
+ const existing = Reflect.get(target, k);
66554
+ if (existing !== void 0) return existing;
65676
66555
  const cap = DEVICE_LOCAL_STATE_CAPS[k];
65677
66556
  if (!cap) return void 0;
65678
66557
  const proxy = this.sliceProxy(cap);
65679
- cache[k] = proxy;
66558
+ Reflect.set(target, k, proxy);
65680
66559
  return proxy;
65681
66560
  } };
65682
66561
  this._stateProxyCache = new Proxy(cache, handler);
@@ -65875,7 +66754,7 @@ var BaseDevice = class {
65875
66754
  async updateSourceInfo(patch) {
65876
66755
  const next = mergeSourceInfo(this.sourceInfo, patch);
65877
66756
  this._sourceInfoCache = Object.freeze({ ...next });
65878
- const action = this.ctx.api?.deviceManager?.setMetadata;
66757
+ const action = this.ctx.api.deviceManager?.setMetadata;
65879
66758
  if (action) try {
65880
66759
  await action.mutate({
65881
66760
  deviceId: this.id,
@@ -65910,7 +66789,7 @@ var BaseDevice = class {
65910
66789
  * device boot on a meta refresh.
65911
66790
  */
65912
66791
  async refreshFeatures() {
65913
- const action = this.ctx.api?.deviceManager?.registerDevice;
66792
+ const action = this.ctx.api.deviceManager?.registerDevice;
65914
66793
  if (!action) return;
65915
66794
  try {
65916
66795
  await action.mutate({
@@ -66309,6 +67188,12 @@ var BaseDeviceProvider = class extends BaseAddon {
66309
67188
  provider: this
66310
67189
  }];
66311
67190
  }
67191
+ /** Flatten `super.onInitialize()` whether it returned an array or an envelope. */
67192
+ providerRegistrationsFromSuper(result) {
67193
+ if (Array.isArray(result)) return result;
67194
+ if (result !== void 0 && result.providers !== void 0) return [...result.providers];
67195
+ return [];
67196
+ }
66312
67197
  async onShutdown() {
66313
67198
  this._restoreShuttingDown = true;
66314
67199
  this.cancelRestoreRetries();
@@ -66835,7 +67720,7 @@ var BaseDeviceProvider = class extends BaseAddon {
66835
67720
  return toDeviceSummary(device, this.addonId);
66836
67721
  }
66837
67722
  };
66838
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
67723
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
66839
67724
  new Set(Object.values(DeviceType));
66840
67725
  Object.freeze({
66841
67726
  "accessories.getStatus": {
@@ -67984,6 +68869,12 @@ Object.freeze({
67984
68869
  addonId: null,
67985
68870
  access: "view"
67986
68871
  },
68872
+ "coreBlocks.migrateRawSecrets": {
68873
+ capName: "core-blocks",
68874
+ capScope: "system",
68875
+ addonId: null,
68876
+ access: "create"
68877
+ },
67987
68878
  "coreBlocks.restart": {
67988
68879
  capName: "core-blocks",
67989
68880
  capScope: "system",
@@ -69250,6 +70141,84 @@ Object.freeze({
69250
70141
  addonId: null,
69251
70142
  access: "view"
69252
70143
  },
70144
+ "dishwasher.getStatus": {
70145
+ capName: "dishwasher",
70146
+ capScope: "device",
70147
+ addonId: null,
70148
+ access: "view"
70149
+ },
70150
+ "dishwasher.pause": {
70151
+ capName: "dishwasher",
70152
+ capScope: "device",
70153
+ addonId: null,
70154
+ access: "create"
70155
+ },
70156
+ "dishwasher.setChildLock": {
70157
+ capName: "dishwasher",
70158
+ capScope: "device",
70159
+ addonId: null,
70160
+ access: "create"
70161
+ },
70162
+ "dishwasher.setExtraDry": {
70163
+ capName: "dishwasher",
70164
+ capScope: "device",
70165
+ addonId: null,
70166
+ access: "create"
70167
+ },
70168
+ "dishwasher.setHalfLoad": {
70169
+ capName: "dishwasher",
70170
+ capScope: "device",
70171
+ addonId: null,
70172
+ access: "create"
70173
+ },
70174
+ "dishwasher.setHygienePlus": {
70175
+ capName: "dishwasher",
70176
+ capScope: "device",
70177
+ addonId: null,
70178
+ access: "create"
70179
+ },
70180
+ "dishwasher.setIntensiveZone": {
70181
+ capName: "dishwasher",
70182
+ capScope: "device",
70183
+ addonId: null,
70184
+ access: "create"
70185
+ },
70186
+ "dishwasher.setOption": {
70187
+ capName: "dishwasher",
70188
+ capScope: "device",
70189
+ addonId: null,
70190
+ access: "create"
70191
+ },
70192
+ "dishwasher.setPower": {
70193
+ capName: "dishwasher",
70194
+ capScope: "device",
70195
+ addonId: null,
70196
+ access: "create"
70197
+ },
70198
+ "dishwasher.setProgram": {
70199
+ capName: "dishwasher",
70200
+ capScope: "device",
70201
+ addonId: null,
70202
+ access: "create"
70203
+ },
70204
+ "dishwasher.setVarioSpeedPlus": {
70205
+ capName: "dishwasher",
70206
+ capScope: "device",
70207
+ addonId: null,
70208
+ access: "create"
70209
+ },
70210
+ "dishwasher.start": {
70211
+ capName: "dishwasher",
70212
+ capScope: "device",
70213
+ addonId: null,
70214
+ access: "create"
70215
+ },
70216
+ "dishwasher.stop": {
70217
+ capName: "dishwasher",
70218
+ capScope: "device",
70219
+ addonId: null,
70220
+ access: "create"
70221
+ },
69253
70222
  "doorbell.getStatus": {
69254
70223
  capName: "doorbell",
69255
70224
  capScope: "device",
@@ -69460,6 +70429,12 @@ Object.freeze({
69460
70429
  addonId: null,
69461
70430
  access: "view"
69462
70431
  },
70432
+ "heatingValve.getStatus": {
70433
+ capName: "heating-valve",
70434
+ capScope: "device",
70435
+ addonId: null,
70436
+ access: "view"
70437
+ },
69463
70438
  "humidifier.getStatus": {
69464
70439
  capName: "humidifier",
69465
70440
  capScope: "device",
@@ -69670,6 +70645,12 @@ Object.freeze({
69670
70645
  addonId: null,
69671
70646
  access: "create"
69672
70647
  },
70648
+ "keychainAdmin.importSecret": {
70649
+ capName: "keychain-admin",
70650
+ capScope: "system",
70651
+ addonId: null,
70652
+ access: "create"
70653
+ },
69673
70654
  "keychainAdmin.listAudit": {
69674
70655
  capName: "keychain-admin",
69675
70656
  capScope: "system",
@@ -69838,6 +70819,12 @@ Object.freeze({
69838
70819
  addonId: null,
69839
70820
  access: "view"
69840
70821
  },
70822
+ "llm.migrateRawSecrets": {
70823
+ capName: "llm",
70824
+ capScope: "system",
70825
+ addonId: null,
70826
+ access: "create"
70827
+ },
69841
70828
  "llm.resolveModelRef": {
69842
70829
  capName: "llm",
69843
70830
  capScope: "system",
@@ -70066,6 +71053,12 @@ Object.freeze({
70066
71053
  addonId: null,
70067
71054
  access: "create"
70068
71055
  },
71056
+ "localNetwork.useStoredUploadedCertificate": {
71057
+ capName: "local-network",
71058
+ capScope: "system",
71059
+ addonId: null,
71060
+ access: "create"
71061
+ },
70069
71062
  "lockControl.getStatus": {
70070
71063
  capName: "lock-control",
70071
71064
  capScope: "device",
@@ -72718,6 +73711,30 @@ Object.freeze({
72718
73711
  addonId: null,
72719
73712
  access: "create"
72720
73713
  },
73714
+ "recordEntries.deleteEntry": {
73715
+ capName: "record-entries",
73716
+ capScope: "system",
73717
+ addonId: null,
73718
+ access: "delete"
73719
+ },
73720
+ "recordEntries.listEntries": {
73721
+ capName: "record-entries",
73722
+ capScope: "system",
73723
+ addonId: null,
73724
+ access: "view"
73725
+ },
73726
+ "recordEntries.listEntryKinds": {
73727
+ capName: "record-entries",
73728
+ capScope: "system",
73729
+ addonId: null,
73730
+ access: "view"
73731
+ },
73732
+ "recordEntries.putEntry": {
73733
+ capName: "record-entries",
73734
+ capScope: "system",
73735
+ addonId: null,
73736
+ access: "create"
73737
+ },
72721
73738
  "recording.getAvailability": {
72722
73739
  capName: "recording",
72723
73740
  capScope: "device",
@@ -73018,6 +74035,102 @@ Object.freeze({
73018
74035
  addonId: null,
73019
74036
  access: "view"
73020
74037
  },
74038
+ "records.aggregate": {
74039
+ capName: "records",
74040
+ capScope: "system",
74041
+ addonId: null,
74042
+ access: "view"
74043
+ },
74044
+ "records.allocateId": {
74045
+ capName: "records",
74046
+ capScope: "system",
74047
+ addonId: null,
74048
+ access: "create"
74049
+ },
74050
+ "records.count": {
74051
+ capName: "records",
74052
+ capScope: "system",
74053
+ addonId: null,
74054
+ access: "view"
74055
+ },
74056
+ "records.declareKinds": {
74057
+ capName: "records",
74058
+ capScope: "system",
74059
+ addonId: null,
74060
+ access: "create"
74061
+ },
74062
+ "records.delete": {
74063
+ capName: "records",
74064
+ capScope: "system",
74065
+ addonId: null,
74066
+ access: "delete"
74067
+ },
74068
+ "records.deleteDevice": {
74069
+ capName: "records",
74070
+ capScope: "system",
74071
+ addonId: null,
74072
+ access: "delete"
74073
+ },
74074
+ "records.deleteMany": {
74075
+ capName: "records",
74076
+ capScope: "system",
74077
+ addonId: null,
74078
+ access: "delete"
74079
+ },
74080
+ "records.deleteWhere": {
74081
+ capName: "records",
74082
+ capScope: "system",
74083
+ addonId: null,
74084
+ access: "delete"
74085
+ },
74086
+ "records.get": {
74087
+ capName: "records",
74088
+ capScope: "system",
74089
+ addonId: null,
74090
+ access: "view"
74091
+ },
74092
+ "records.getMany": {
74093
+ capName: "records",
74094
+ capScope: "system",
74095
+ addonId: null,
74096
+ access: "view"
74097
+ },
74098
+ "records.histogram": {
74099
+ capName: "records",
74100
+ capScope: "system",
74101
+ addonId: null,
74102
+ access: "view"
74103
+ },
74104
+ "records.patch": {
74105
+ capName: "records",
74106
+ capScope: "system",
74107
+ addonId: null,
74108
+ access: "create"
74109
+ },
74110
+ "records.put": {
74111
+ capName: "records",
74112
+ capScope: "system",
74113
+ addonId: null,
74114
+ access: "create"
74115
+ },
74116
+ "records.putMany": {
74117
+ capName: "records",
74118
+ capScope: "system",
74119
+ addonId: null,
74120
+ access: "create"
74121
+ },
74122
+ "records.query": {
74123
+ capName: "records",
74124
+ capScope: "system",
74125
+ addonId: null,
74126
+ access: "view"
74127
+ },
74128
+ "records.secretRefs": {
74129
+ capName: "records",
74130
+ capScope: "system",
74131
+ addonId: null,
74132
+ access: "view"
74133
+ },
73021
74134
  "sceneMonitor.captureReference": {
73022
74135
  capName: "scene-monitor",
73023
74136
  capScope: "device",
@@ -73450,6 +74563,48 @@ Object.freeze({
73450
74563
  addonId: null,
73451
74564
  access: "create"
73452
74565
  },
74566
+ "storageAdmin.adoptRecords": {
74567
+ capName: "storage-admin",
74568
+ capScope: "system",
74569
+ addonId: null,
74570
+ access: "create"
74571
+ },
74572
+ "storageAdmin.dropCollection": {
74573
+ capName: "storage-admin",
74574
+ capScope: "system",
74575
+ addonId: null,
74576
+ access: "delete"
74577
+ },
74578
+ "storageAdmin.isAdoptedLegacyTable": {
74579
+ capName: "storage-admin",
74580
+ capScope: "system",
74581
+ addonId: null,
74582
+ access: "view"
74583
+ },
74584
+ "storageAdmin.listKinds": {
74585
+ capName: "storage-admin",
74586
+ capScope: "system",
74587
+ addonId: null,
74588
+ access: "view"
74589
+ },
74590
+ "storageAdmin.listTables": {
74591
+ capName: "storage-admin",
74592
+ capScope: "system",
74593
+ addonId: null,
74594
+ access: "view"
74595
+ },
74596
+ "storageAdmin.readLegacyCollection": {
74597
+ capName: "storage-admin",
74598
+ capScope: "system",
74599
+ addonId: null,
74600
+ access: "view"
74601
+ },
74602
+ "storageAdmin.readRecords": {
74603
+ capName: "storage-admin",
74604
+ capScope: "system",
74605
+ addonId: null,
74606
+ access: "view"
74607
+ },
73453
74608
  "storageEvictable.evict": {
73454
74609
  capName: "storage-evictable",
73455
74610
  capScope: "system",
@@ -74362,6 +75517,12 @@ Object.freeze({
74362
75517
  addonId: null,
74363
75518
  access: "view"
74364
75519
  },
75520
+ "userManagement.migrateRawSecrets": {
75521
+ capName: "user-management",
75522
+ capScope: "system",
75523
+ addonId: null,
75524
+ access: "create"
75525
+ },
74365
75526
  "userManagement.oauthExchangeCode": {
74366
75527
  capName: "user-management",
74367
75528
  capScope: "system",
@@ -74861,7 +76022,7 @@ Object.freeze({
74861
76022
  access: "create"
74862
76023
  }
74863
76024
  });
74864
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
76025
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
74865
76026
  DeviceFeature.BatteryOperated;
74866
76027
  Object.freeze({
74867
76028
  "accessories.getStatus": [{
@@ -75685,6 +76846,71 @@ Object.freeze({
75685
76846
  form: "single",
75686
76847
  optional: false
75687
76848
  }],
76849
+ "dishwasher.getStatus": [{
76850
+ name: "deviceId",
76851
+ form: "single",
76852
+ optional: false
76853
+ }],
76854
+ "dishwasher.pause": [{
76855
+ name: "deviceId",
76856
+ form: "single",
76857
+ optional: false
76858
+ }],
76859
+ "dishwasher.setChildLock": [{
76860
+ name: "deviceId",
76861
+ form: "single",
76862
+ optional: false
76863
+ }],
76864
+ "dishwasher.setExtraDry": [{
76865
+ name: "deviceId",
76866
+ form: "single",
76867
+ optional: false
76868
+ }],
76869
+ "dishwasher.setHalfLoad": [{
76870
+ name: "deviceId",
76871
+ form: "single",
76872
+ optional: false
76873
+ }],
76874
+ "dishwasher.setHygienePlus": [{
76875
+ name: "deviceId",
76876
+ form: "single",
76877
+ optional: false
76878
+ }],
76879
+ "dishwasher.setIntensiveZone": [{
76880
+ name: "deviceId",
76881
+ form: "single",
76882
+ optional: false
76883
+ }],
76884
+ "dishwasher.setOption": [{
76885
+ name: "deviceId",
76886
+ form: "single",
76887
+ optional: false
76888
+ }],
76889
+ "dishwasher.setPower": [{
76890
+ name: "deviceId",
76891
+ form: "single",
76892
+ optional: false
76893
+ }],
76894
+ "dishwasher.setProgram": [{
76895
+ name: "deviceId",
76896
+ form: "single",
76897
+ optional: false
76898
+ }],
76899
+ "dishwasher.setVarioSpeedPlus": [{
76900
+ name: "deviceId",
76901
+ form: "single",
76902
+ optional: false
76903
+ }],
76904
+ "dishwasher.start": [{
76905
+ name: "deviceId",
76906
+ form: "single",
76907
+ optional: false
76908
+ }],
76909
+ "dishwasher.stop": [{
76910
+ name: "deviceId",
76911
+ form: "single",
76912
+ optional: false
76913
+ }],
75688
76914
  "doorbell.getStatus": [{
75689
76915
  name: "deviceId",
75690
76916
  form: "single",
@@ -75754,6 +76980,11 @@ Object.freeze({
75754
76980
  form: "single",
75755
76981
  optional: false
75756
76982
  }],
76983
+ "heatingValve.getStatus": [{
76984
+ name: "deviceId",
76985
+ form: "single",
76986
+ optional: false
76987
+ }],
75757
76988
  "humidifier.getStatus": [{
75758
76989
  name: "deviceId",
75759
76990
  form: "single",
@@ -77038,6 +78269,37 @@ Object.freeze({
77038
78269
  form: "single",
77039
78270
  optional: false
77040
78271
  }],
78272
+ "records.count": [{
78273
+ name: "deviceId",
78274
+ form: "single",
78275
+ optional: true
78276
+ }],
78277
+ "records.deleteDevice": [{
78278
+ name: "deviceId",
78279
+ form: "single",
78280
+ optional: false
78281
+ }],
78282
+ "records.deleteWhere": [{
78283
+ name: "deviceId",
78284
+ form: "single",
78285
+ optional: true
78286
+ }],
78287
+ "records.histogram": [{
78288
+ name: "deviceId",
78289
+ form: "single",
78290
+ optional: true
78291
+ }],
78292
+ "records.putMany": [{
78293
+ name: "records",
78294
+ form: "object-array",
78295
+ optional: false,
78296
+ itemField: "deviceId"
78297
+ }],
78298
+ "records.query": [{
78299
+ name: "deviceId",
78300
+ form: "single",
78301
+ optional: true
78302
+ }],
77041
78303
  "sceneMonitor.captureReference": [{
77042
78304
  name: "deviceId",
77043
78305
  form: "single",
@@ -77154,6 +78416,17 @@ Object.freeze({
77154
78416
  form: "single",
77155
78417
  optional: false
77156
78418
  }],
78419
+ "storageAdmin.adoptRecords": [{
78420
+ name: "records",
78421
+ form: "object-array",
78422
+ optional: false,
78423
+ itemField: "deviceId"
78424
+ }],
78425
+ "storageAdmin.readRecords": [{
78426
+ name: "deviceId",
78427
+ form: "single",
78428
+ optional: true
78429
+ }],
77157
78430
  "streamBroker.acquireEgressTranscode": [{
77158
78431
  name: "deviceId",
77159
78432
  form: "single",
@@ -80020,7 +81293,7 @@ var TuyaProviderAddon = class extends BaseDeviceProvider {
80020
81293
  super({});
80021
81294
  }
80022
81295
  async onInitialize() {
80023
- const regs = await super.onInitialize();
81296
+ const regs = this.providerRegistrationsFromSuper(await super.onInitialize());
80024
81297
  this.clients = new TuyaClientRegistry(this.ctx.logger, tuyaConnections);
80025
81298
  this.clients.setOnConnected((integrationId) => {
80026
81299
  this.ctx.logger.info("Tuya: integration connected", { meta: { integrationId } });