@camstack/addon-provider-petkit 0.2.126 → 0.2.128

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
@@ -1100,7 +1100,7 @@ var Nodepetkit = class {
1100
1100
  }
1101
1101
  };
1102
1102
  //#endregion
1103
- //#region ../types/dist/event-category-C4hVnRo8.mjs
1103
+ //#region ../types/dist/event-category-BvOFi6PY.mjs
1104
1104
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
1105
1105
  EventCategory["SystemBoot"] = "system.boot";
1106
1106
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1164,6 +1164,24 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1164
1164
  * consumes this category and maps it to the `addon-crash-loop` kind.
1165
1165
  */
1166
1166
  EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
1167
+ /**
1168
+ * A runner whose addon refused to start with a typed reason (D877) - parked
1169
+ * in `refused`, never counted by the crash breaker, retried on operator
1170
+ * action. One event per refusal.
1171
+ */
1172
+ EventCategory["AddonRunnerRefused"] = "addon.runner-refused";
1173
+ /**
1174
+ * A runner settled on a state (D877): `running` (READY), `crashed`, `failed` or
1175
+ * `refused`. In-process telemetry for the boot check (re-evaluate while any
1176
+ * runner is still pending) and the alert centre (close a recovered runner).
1177
+ */
1178
+ EventCategory["ProcessStateChanged"] = "process.state-changed";
1179
+ /**
1180
+ * The node's one-shot runner boot check (D877): planned runners against the
1181
+ * process service, `degraded` when any is failed, refused or missing. The
1182
+ * alert centre turns it into one alert and a direct critical push.
1183
+ */
1184
+ EventCategory["SystemRunnerCheck"] = "system.runner-check";
1167
1185
  EventCategory["AddonError"] = "addon.error";
1168
1186
  EventCategory["AddonPageReady"] = "addon.page-ready";
1169
1187
  EventCategory["AddonWidgetReady"] = "addon.widget-ready";
@@ -6488,7 +6506,7 @@ var ZodIssueCode = {
6488
6506
  var ZodFirstPartyTypeKind;
6489
6507
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6490
6508
  //#endregion
6491
- //#region ../types/dist/sleep-DRqxtc0C.mjs
6509
+ //#region ../types/dist/sleep-BegKjwUu.mjs
6492
6510
  /**
6493
6511
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6494
6512
  * window to float samples (D455).
@@ -6605,7 +6623,7 @@ function isKeychainStoredField(field) {
6605
6623
  var SECRET_CLEAR = { $secretClear: true };
6606
6624
  /** The `'***'` token D634 redaction writes for non-admins. */
6607
6625
  var LEGACY_DEVICE_REDACTION = "***";
6608
- function isRecord$2(value) {
6626
+ function isRecord$5(value) {
6609
6627
  return typeof value === "object" && value !== null && !Array.isArray(value);
6610
6628
  }
6611
6629
  function makeSecretMarker(ref) {
@@ -6616,12 +6634,12 @@ function makeSecretMarker(ref) {
6616
6634
  }
6617
6635
  /** A stored marker: an object whose `$secret` is a non-empty string. */
6618
6636
  function isSecretMarker(value) {
6619
- if (!isRecord$2(value)) return false;
6637
+ if (!isRecord$5(value)) return false;
6620
6638
  const ref = value["$secret"];
6621
6639
  return typeof ref === "string" && ref.length > 0;
6622
6640
  }
6623
6641
  function isSecretClear(value) {
6624
- return isRecord$2(value) && value["$secretClear"] === true;
6642
+ return isRecord$5(value) && value["$secretClear"] === true;
6625
6643
  }
6626
6644
  /**
6627
6645
  * The UI view of a stored secret field value: a marker becomes "set", inline
@@ -7231,7 +7249,7 @@ var KeychainWriteRefusedError = class extends Error {
7231
7249
  }
7232
7250
  };
7233
7251
  var KEEP = { kind: "keep" };
7234
- function isRecord$1(value) {
7252
+ function isRecord$4(value) {
7235
7253
  return typeof value === "object" && value !== null && !Array.isArray(value);
7236
7254
  }
7237
7255
  function defaultMintId() {
@@ -7244,7 +7262,7 @@ function collectSecretMarkerRefs(value, out = []) {
7244
7262
  return out;
7245
7263
  }
7246
7264
  if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
7247
- else if (isRecord$1(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
7265
+ else if (isRecord$4(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
7248
7266
  return out;
7249
7267
  }
7250
7268
  async function storedValueEquals(secrets, marker, candidate) {
@@ -7299,7 +7317,7 @@ function rowId(row, idKey) {
7299
7317
  return null;
7300
7318
  }
7301
7319
  async function writeArray(ctx, node, incoming, stored, fieldPath) {
7302
- const storedRows = Array.isArray(stored) ? stored.filter(isRecord$1) : [];
7320
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord$4) : [];
7303
7321
  if (!Array.isArray(incoming)) {
7304
7322
  ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
7305
7323
  return {
@@ -7315,7 +7333,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
7315
7333
  const kept = /* @__PURE__ */ new Set();
7316
7334
  const rows = [];
7317
7335
  for (const row of incoming) {
7318
- if (!isRecord$1(row)) {
7336
+ if (!isRecord$4(row)) {
7319
7337
  rows.push(row);
7320
7338
  continue;
7321
7339
  }
@@ -7340,7 +7358,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
7340
7358
  case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
7341
7359
  case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
7342
7360
  case "object": {
7343
- if (!isRecord$1(incoming)) {
7361
+ if (!isRecord$4(incoming)) {
7344
7362
  if (incoming === void 0) return KEEP;
7345
7363
  ctx.deletes.push(...collectSecretMarkerRefs(stored));
7346
7364
  return {
@@ -7348,7 +7366,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
7348
7366
  value: incoming
7349
7367
  };
7350
7368
  }
7351
- const storedObj = isRecord$1(stored) ? stored : {};
7369
+ const storedObj = isRecord$4(stored) ? stored : {};
7352
7370
  return {
7353
7371
  kind: "value",
7354
7372
  value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
@@ -7471,7 +7489,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
7471
7489
  value: items
7472
7490
  };
7473
7491
  }
7474
- if (isRecord$1(v)) {
7492
+ if (isRecord$4(v)) {
7475
7493
  const out = {};
7476
7494
  for (const [k, child] of Object.entries(v)) {
7477
7495
  const s = substitute(child);
@@ -7503,7 +7521,7 @@ var SecretOverwriteRefusedError = class extends Error {
7503
7521
  this.name = "SecretOverwriteRefusedError";
7504
7522
  }
7505
7523
  };
7506
- function isRecord(value) {
7524
+ function isRecord$3(value) {
7507
7525
  return typeof value === "object" && value !== null && !Array.isArray(value);
7508
7526
  }
7509
7527
  function resolvedValue(pass, marker) {
@@ -7532,7 +7550,7 @@ function inArray(pass, incoming, stored) {
7532
7550
  return incoming.map((item, index) => {
7533
7551
  const twin = stored[index];
7534
7552
  if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
7535
- if (isRecord(item) && isRecord(twin)) return inArrayRow(pass, item, twin);
7553
+ if (isRecord$3(item) && isRecord$3(twin)) return inArrayRow(pass, item, twin);
7536
7554
  if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
7537
7555
  return item;
7538
7556
  });
@@ -7543,7 +7561,7 @@ function inArrayRow(pass, incoming, stored) {
7543
7561
  const twin = stored[key];
7544
7562
  if (isSecretMarker(twin)) {
7545
7563
  if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
7546
- } else if (isRecord(value) && isRecord(twin)) out[key] = inArrayRow(pass, value, twin);
7564
+ } else if (isRecord$3(value) && isRecord$3(twin)) out[key] = inArrayRow(pass, value, twin);
7547
7565
  else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
7548
7566
  }
7549
7567
  return out;
@@ -7551,7 +7569,7 @@ function inArrayRow(pass, incoming, stored) {
7551
7569
  async function walk(pass, incoming, stored) {
7552
7570
  if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
7553
7571
  if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
7554
- if (!isRecord(incoming) || !isRecord(stored)) return incoming;
7572
+ if (!isRecord$3(incoming) || !isRecord$3(stored)) return incoming;
7555
7573
  const out = { ...incoming };
7556
7574
  for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
7557
7575
  return out;
@@ -7582,7 +7600,7 @@ async function restoreUnchangedSecrets(input) {
7582
7600
  function isJsonEqual(a, b) {
7583
7601
  if (Object.is(a, b)) return true;
7584
7602
  if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
7585
- if (isRecord(a) && isRecord(b)) {
7603
+ if (isRecord$3(a) && isRecord$3(b)) {
7586
7604
  const keys = Object.keys(a);
7587
7605
  if (keys.length !== Object.keys(b).length) return false;
7588
7606
  return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
@@ -7606,7 +7624,7 @@ function markerKeysOf(blob, owner) {
7606
7624
  }
7607
7625
  function nodeFromStored(value, owner) {
7608
7626
  if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
7609
- if (!isRecord(value)) return null;
7627
+ if (!isRecord$3(value)) return null;
7610
7628
  const children = /* @__PURE__ */ new Map();
7611
7629
  for (const [key, child] of Object.entries(value)) {
7612
7630
  const node = nodeFromStored(child, owner);
@@ -7739,7 +7757,6 @@ function createHandle(raw, opts) {
7739
7757
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
7740
7758
  const readAddonStoreResult = raw.readAddonStoreResult;
7741
7759
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
7742
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
7743
7760
  const overrides = {
7744
7761
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
7745
7762
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -7763,12 +7780,12 @@ function createHandle(raw, opts) {
7763
7780
  };
7764
7781
  } } : {},
7765
7782
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
7766
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
7767
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
7783
+ readDeviceStoreBatch: async (deviceIds) => {
7784
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
7768
7785
  const out = /* @__PURE__ */ new Map();
7769
7786
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
7770
7787
  return out;
7771
- } } : {},
7788
+ },
7772
7789
  writeAddonStore: async (patch) => {
7773
7790
  await writeAddon(patch);
7774
7791
  },
@@ -8395,7 +8412,7 @@ var BaseAddon = class {
8395
8412
  if (!ctx) return;
8396
8413
  const addonId = ctx.id;
8397
8414
  setImmediate(() => {
8398
- ctx.api.addons?.restartAddon?.mutate({ addonId }).then(() => {
8415
+ ctx.api.addons.restartAddon.mutate({ addonId }).then(() => {
8399
8416
  ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: patchKeys.filter((k) => restartKeys.has(k)) } });
8400
8417
  }).catch((err) => {
8401
8418
  ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
@@ -8595,7 +8612,7 @@ var BaseAddon = class {
8595
8612
  return this._addonSecretTree;
8596
8613
  }
8597
8614
  deviceSecretTree() {
8598
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
8615
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
8599
8616
  return this._deviceSecretTree;
8600
8617
  }
8601
8618
  /**
@@ -8671,42 +8688,135 @@ var BaseAddon = class {
8671
8688
  this[timerOf] = timer;
8672
8689
  }
8673
8690
  /**
8674
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
8675
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
8676
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
8677
- * re-initialize) doesn't propagate up into `initialize()` and leave the
8691
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
8692
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
8678
8693
  * addon permanently broken.
8679
8694
  *
8680
- * Retry only the two known infra fingerprints. Anything else propagates so
8681
- * real bugs surface immediately. After the budget expires we fall back to
8682
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
8695
+ * Two kinds of outage, two budgets:
8696
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
8697
+ * initialized`, `provider not available`): a short budget, then the addon
8698
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
8699
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
8700
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
8701
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
8702
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
8703
+ * loop. A long, bounded budget; only after it the error propagates - the
8704
+ * addon never runs on defaults for a store it could not ask.
8705
+ * Anything else propagates immediately so real bugs surface.
8683
8706
  */
8684
8707
  async readAddonStoreWithRetry() {
8685
8708
  const settings = this.rawSettings();
8686
8709
  if (!settings) return {};
8687
- const delaysMs = [
8688
- 150,
8689
- 350,
8690
- 600,
8691
- 900
8692
- ];
8693
8710
  let lastErr;
8694
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
8711
+ for (let attempt = 0;; attempt++) try {
8695
8712
  const stored = await settings.readAddonStore() ?? {};
8696
8713
  this.settingsStoreReady = true;
8697
8714
  return stored;
8698
8715
  } catch (err) {
8699
8716
  lastErr = err;
8700
- const msg = err instanceof Error ? err.message : String(err);
8701
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
8702
- if (attempt === delaysMs.length) break;
8703
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
8717
+ const kind = classifyStoreError(err);
8718
+ if (kind === "permanent") throw err;
8719
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
8720
+ if (delay === void 0) {
8721
+ if (kind === "transport") throw err;
8722
+ break;
8723
+ }
8724
+ await new Promise((r) => setTimeout(r, delay));
8704
8725
  }
8705
8726
  this.settingsStoreReady = false;
8706
8727
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
8707
8728
  return {};
8708
8729
  }
8709
8730
  };
8731
+ /** The settings store's own restart fingerprints. */
8732
+ var RESTART_RETRY_DELAYS_MS = [
8733
+ 150,
8734
+ 350,
8735
+ 600,
8736
+ 900
8737
+ ];
8738
+ /** A hub restarting under a booting runner: ~100 s in total. */
8739
+ var TRANSPORT_RETRY_DELAYS_MS = [
8740
+ 150,
8741
+ 350,
8742
+ 600,
8743
+ 900,
8744
+ 2e3,
8745
+ 5e3,
8746
+ 1e4,
8747
+ 2e4,
8748
+ 3e4,
8749
+ 3e4
8750
+ ];
8751
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
8752
+ /** Node / socket error codes of a connection that did not happen or broke. */
8753
+ var TRANSPORT_CODES = new Set([
8754
+ "ECONNREFUSED",
8755
+ "ECONNRESET",
8756
+ "ETIMEDOUT",
8757
+ "EPIPE",
8758
+ "EHOSTUNREACH",
8759
+ "ENETUNREACH"
8760
+ ]);
8761
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
8762
+ var UNAVAILABLE_TRPC_CODES = new Set([
8763
+ "TIMEOUT",
8764
+ "SERVICE_UNAVAILABLE",
8765
+ "BAD_GATEWAY",
8766
+ "GATEWAY_TIMEOUT"
8767
+ ]);
8768
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
8769
+ var TRANSPORT_PHRASES = [
8770
+ "discovery deadline exceeded",
8771
+ "parent could not route child unowned cap call",
8772
+ "UDS request timed out after",
8773
+ "channel closed"
8774
+ ];
8775
+ /** Moleculer error `type`s of a call that could not be served right now. */
8776
+ var MOLECULER_TRANSIENT_TYPES = new Set([
8777
+ "REQUEST_TIMEOUT",
8778
+ "SERVICE_NOT_FOUND",
8779
+ "SERVICE_NOT_AVAILABLE",
8780
+ "REQUEST_REJECTED",
8781
+ "BROKER_DISCONNECTED"
8782
+ ]);
8783
+ function stringProp(value, key) {
8784
+ if (typeof value !== "object" || value === null) return void 0;
8785
+ const found = Reflect.get(value, key);
8786
+ return typeof found === "string" ? found : void 0;
8787
+ }
8788
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
8789
+ function errorCode(err) {
8790
+ const out = {};
8791
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
8792
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
8793
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
8794
+ if (node !== void 0) out.node = node;
8795
+ const trpc = stringProp(data, "code");
8796
+ if (trpc !== void 0) out.trpc = trpc;
8797
+ return out;
8798
+ }
8799
+ /**
8800
+ * Is a settings read failure worth retrying, and with which budget? Only
8801
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
8802
+ * one of the exact phrases our own transport raises. A message that merely
8803
+ * contains "timed out" or "unreachable" is not evidence of anything, and
8804
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
8805
+ */
8806
+ function classifyStoreError(err) {
8807
+ const message = err instanceof Error ? err.message : String(err);
8808
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
8809
+ const code = errorCode(err);
8810
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
8811
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
8812
+ if (typeof err === "object" && err !== null) {
8813
+ if (Reflect.get(err, "retryable") === true) return "transport";
8814
+ const type = stringProp(err, "type");
8815
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
8816
+ }
8817
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
8818
+ return "permanent";
8819
+ }
8710
8820
  /**
8711
8821
  * Collect the keys of every field marked `perNode: true`, recursing into
8712
8822
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8796,6 +8906,156 @@ var PeerBytesTicketSchema = object({
8796
8906
  */
8797
8907
  declaredBytes: number().int().nonnegative().nullable()
8798
8908
  });
8909
+ /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8910
+ /** The files a kind may live in. `history` is device-history's (D850), never records. */
8911
+ var RECORD_DATABASES = ["settings", "events"];
8912
+ /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8913
+ var RECORD_CLASSES = [
8914
+ "config",
8915
+ "ledger",
8916
+ "audit",
8917
+ "mirror",
8918
+ "ephemeral"
8919
+ ];
8920
+ var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8921
+ var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8922
+ var RECORD_BATCH_MAX = 1e3;
8923
+ var RECORD_QUERY_MAX = 1e3;
8924
+ function defOf(schema) {
8925
+ const zodBag = Reflect.get(schema, "_zod");
8926
+ if (typeof zodBag !== "object" || zodBag === null) return {};
8927
+ const def = Reflect.get(zodBag, "def");
8928
+ if (typeof def !== "object" || def === null) return {};
8929
+ return def;
8930
+ }
8931
+ function declaredHere(schema) {
8932
+ return globalRegistry.get(schema)?.secret === true;
8933
+ }
8934
+ /** Wrapper kinds whose `innerType` is the field itself. */
8935
+ var WRAPPERS = new Set([
8936
+ "optional",
8937
+ "nullable",
8938
+ "default",
8939
+ "prefault",
8940
+ "catch",
8941
+ "readonly",
8942
+ "nonoptional"
8943
+ ]);
8944
+ /**
8945
+ * The schema layers that make up ONE field: the schema itself and every
8946
+ * wrapper below it (optional / default / nullable / readonly / catch), plus
8947
+ * both sides of a pipe (`z.preprocess` puts the real schema on `out`,
8948
+ * `.transform` on `in`).
8949
+ */
8950
+ function layersOf(schema) {
8951
+ const out = [];
8952
+ const queue = [schema];
8953
+ while (queue.length > 0) {
8954
+ const s = queue.shift();
8955
+ if (!s || out.includes(s)) continue;
8956
+ out.push(s);
8957
+ const def = defOf(s);
8958
+ if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
8959
+ if (def.type === "pipe") {
8960
+ if (def.in) queue.push(def.in);
8961
+ if (def.out) queue.push(def.out);
8962
+ }
8963
+ }
8964
+ return out;
8965
+ }
8966
+ /** True when any layer of this field declares `.meta({ secret: true })`. */
8967
+ function isZodSecret(schema) {
8968
+ return layersOf(schema).some(declaredHere);
8969
+ }
8970
+ /**
8971
+ * Every declared secret path in a config schema: dotted keys, `[]` for "each
8972
+ * element of this array" (`connection.password`, `rows[].token`). A declared
8973
+ * object or array is reported as one path and not descended into — everything
8974
+ * under it is secret.
8975
+ */
8976
+ function collectZodSecretPaths(schema, prefix = "") {
8977
+ if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
8978
+ const paths = [];
8979
+ for (const layer of layersOf(schema)) {
8980
+ const def = defOf(layer);
8981
+ if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
8982
+ else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
8983
+ else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
8984
+ else if (def.type === "intersection") {
8985
+ if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
8986
+ if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
8987
+ }
8988
+ }
8989
+ return [...new Set(paths)];
8990
+ }
8991
+ /**
8992
+ * Record kinds (D881): the declaration an addon makes of one kind of fact, the
8993
+ * Zod row schema that describes it, and the helper that turns the two into the
8994
+ * JSON the engine stores.
8995
+ *
8996
+ * A kind is declared ONCE, next to the code that owns it, and the engine's
8997
+ * reconciler creates its indexes - nothing creates DDL at runtime from a write.
8998
+ * Everything the generic machinery needs (secret fields, the entry descriptor)
8999
+ * is DERIVED from the Zod schema: `.meta({ secret: true })` marks a credential,
9000
+ * never a field name (D879).
9001
+ */
9002
+ /** A JSON field path: `trackId`, `auth.accessToken`. Charset-restricted: it is rendered into DDL. */
9003
+ var RecordFieldPathSchema = string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}(\.[A-Za-z_][A-Za-z0-9_]{0,63}){0,3}$/);
9004
+ var RecordIndexSchema = object({
9005
+ name: string().regex(/^[a-z][a-z0-9_]{0,31}$/),
9006
+ fields: array(RecordFieldPathSchema).min(1).max(4),
9007
+ unique: boolean().optional()
9008
+ }).strict();
9009
+ var RecordRetentionSchema = union([
9010
+ object({
9011
+ ttlMs: number().int().positive(),
9012
+ from: _enum(["at", "write"])
9013
+ }).strict(),
9014
+ object({
9015
+ maxRows: number().int().positive().optional(),
9016
+ maxRowsPerDevice: number().int().positive().optional()
9017
+ }).strict().refine((r) => r.maxRows !== void 0 || r.maxRowsPerDevice !== void 0, { message: "name maxRows, maxRowsPerDevice or both" }),
9018
+ object({
9019
+ owner: string().min(1),
9020
+ reason: string().min(1)
9021
+ }).strict(),
9022
+ object({ none: string().min(1) }).strict()
9023
+ ]);
9024
+ /** The descriptor a generic entries UI reads (D881 "generic entries"). */
9025
+ var EntryDescriptorSchema = object({
9026
+ title: string().min(1),
9027
+ /** The record body field shown as the entry's name. */
9028
+ nameField: string().min(1),
9029
+ /** JSON Schema of the entry body; `secret: true` marks a password field. */
9030
+ fields: record(string(), unknown())
9031
+ }).strict();
9032
+ /** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
9033
+ var RecordKindDeclarationSchema = object({
9034
+ database: _enum(RECORD_DATABASES).default("settings"),
9035
+ class: _enum(RECORD_CLASSES),
9036
+ writeTrigger: string().min(1),
9037
+ device: _enum([
9038
+ "required",
9039
+ "optional",
9040
+ "none"
9041
+ ]).default("none"),
9042
+ at: _enum(["required", "none"]).default("none"),
9043
+ indexes: array(RecordIndexSchema).max(8).default([]),
9044
+ retention: RecordRetentionSchema,
9045
+ deviceRemoval: _enum(["delete", "none"]).default("none"),
9046
+ maxDataBytes: number().int().positive().max(RECORD_MAX_DATA_BYTES).default(RECORD_DEFAULT_MAX_DATA_BYTES),
9047
+ /** Derived from the Zod schema; never written by hand. */
9048
+ secretPaths: array(string().min(1)).default([]),
9049
+ /** The id key of each array that carries a secret (`{ headers: 'name' }`). */
9050
+ secretArrayKeys: record(string(), string()).default({}),
9051
+ /** The legacy table this kind adopts from; the boot guard reads it (D853: no migration code). */
9052
+ adoptsFrom: string().min(1).optional(),
9053
+ /** Kinds that must agree through `transact` sit in one file under one family name. */
9054
+ transactFamily: string().min(1).optional(),
9055
+ entry: EntryDescriptorSchema.optional()
9056
+ }).strict();
9057
+ /** The kind names: lower-case, dash separated, short enough for an index name. */
9058
+ var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
8799
9059
  /** Shared Zod schemas used across streaming capabilities. */
8800
9060
  var CamProfileSchema = _enum([
8801
9061
  "high",
@@ -9260,75 +9520,12 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
9260
9520
  kind: "mutation",
9261
9521
  auth: "admin"
9262
9522
  }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
9263
- function defOf(schema) {
9264
- return schema._zod?.def ?? {};
9265
- }
9266
- function declaredHere(schema) {
9267
- return globalRegistry.get(schema)?.secret === true;
9268
- }
9269
- /** Wrapper kinds whose `innerType` is the field itself. */
9270
- var WRAPPERS = new Set([
9271
- "optional",
9272
- "nullable",
9273
- "default",
9274
- "prefault",
9275
- "catch",
9276
- "readonly",
9277
- "nonoptional"
9278
- ]);
9279
- /**
9280
- * The schema layers that make up ONE field: the schema itself and every
9281
- * wrapper below it (optional / default / nullable / readonly / catch), plus
9282
- * both sides of a pipe (`z.preprocess` puts the real schema on `out`,
9283
- * `.transform` on `in`).
9284
- */
9285
- function layersOf(schema) {
9286
- const out = [];
9287
- const queue = [schema];
9288
- while (queue.length > 0) {
9289
- const s = queue.shift();
9290
- if (!s || out.includes(s)) continue;
9291
- out.push(s);
9292
- const def = defOf(s);
9293
- if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
9294
- if (def.type === "pipe") {
9295
- if (def.in) queue.push(def.in);
9296
- if (def.out) queue.push(def.out);
9297
- }
9298
- }
9299
- return out;
9300
- }
9301
- /** True when any layer of this field declares `.meta({ secret: true })`. */
9302
- function isZodSecret(schema) {
9303
- return layersOf(schema).some(declaredHere);
9304
- }
9305
- /**
9306
- * Every declared secret path in a config schema: dotted keys, `[]` for "each
9307
- * element of this array" (`connection.password`, `rows[].token`). A declared
9308
- * object or array is reported as one path and not descended into — everything
9309
- * under it is secret.
9310
- */
9311
- function collectZodSecretPaths(schema, prefix = "") {
9312
- if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
9313
- const paths = [];
9314
- for (const layer of layersOf(schema)) {
9315
- const def = defOf(layer);
9316
- if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
9317
- else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
9318
- else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
9319
- else if (def.type === "intersection") {
9320
- if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
9321
- if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
9322
- }
9323
- }
9324
- return [...new Set(paths)];
9325
- }
9326
9523
  object({
9327
9524
  code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
9328
9525
  params: record(string(), union([string(), number()])).optional()
9329
9526
  });
9330
9527
  //#endregion
9331
- //#region ../types/dist/err-msg-DLerKiVO.mjs
9528
+ //#region ../types/dist/err-msg-BXKzRBwg.mjs
9332
9529
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
9333
9530
  DeviceType["Camera"] = "camera";
9334
9531
  DeviceType["Hub"] = "hub";
@@ -9420,6 +9617,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
9420
9617
  * native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
9421
9618
  * integrations sharing the same food/desiccant/hopper surface. */
9422
9619
  DeviceType["PetFeeder"] = "pet-feeder";
9620
+ /** Built-in dishwasher — program lifecycle, door / remote-start gates,
9621
+ * salt and rinse-aid levels. Installed with the `dishwasher` cap.
9622
+ * Sources: native Bosch Home Connect, reusable by similar integrations. */
9623
+ DeviceType["Dishwasher"] = "dishwasher";
9423
9624
  return DeviceType;
9424
9625
  }({});
9425
9626
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -9597,6 +9798,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
9597
9798
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
9598
9799
  * real Switch device for UI rendering / export adapters. */
9599
9800
  DeviceRole["BinaryHelper"] = "binary-helper";
9801
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
9802
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
9803
+ DeviceRole["Outlet"] = "outlet";
9600
9804
  /** Generic motion / occupancy / moving event source. Distinct from
9601
9805
  * the camera accessory PirSensor role: that one is a camera child;
9602
9806
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -9674,7 +9878,7 @@ function errMsg(err) {
9674
9878
  return String(err);
9675
9879
  }
9676
9880
  //#endregion
9677
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
9881
+ //#region ../types/dist/composition-CB6PMT9p.mjs
9678
9882
  /**
9679
9883
  * The ffmpeg build every CamStack node downloads, per platform and arch.
9680
9884
  *
@@ -12878,11 +13082,10 @@ var ClipModelMetaSchema = object({
12878
13082
  /**
12879
13083
  * The `vector-store` index this model's vectors live in. ONE feature space
12880
13084
  * per index: two models share an index only when their vectors are ranked
12881
- * against each other, which for CLIP means never across families. MobileCLIP
12882
- * S1 and S2 share `object-clip` for history (separated by the row's
12883
- * `modelId`); any other model gets its own.
13085
+ * against each other, which for CLIP means never across families. Every
13086
+ * model gets its own.
12884
13087
  */
12885
- vectorIndex: string().regex(/^object-clip(-[a-z0-9][a-z0-9-]*)?$/),
13088
+ vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
12886
13089
  /**
12887
13090
  * The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
12888
13091
  * must be unique per tokenizer: every model's siblings land in ONE flat
@@ -14061,8 +14264,13 @@ var AlertSeveritySchema = _enum([
14061
14264
  "info",
14062
14265
  "success",
14063
14266
  "warning",
14064
- "error"
14267
+ "error",
14268
+ "critical"
14065
14269
  ]);
14270
+ var AlertActionSchema = object({
14271
+ label: string(),
14272
+ href: string().optional()
14273
+ });
14066
14274
  var AlertStatusSchema = _enum([
14067
14275
  "active",
14068
14276
  "in-progress",
@@ -14080,6 +14288,7 @@ var AlertSchema = object({
14080
14288
  severity: AlertSeveritySchema,
14081
14289
  title: string(),
14082
14290
  message: string(),
14291
+ action: AlertActionSchema.optional(),
14083
14292
  status: AlertStatusSchema,
14084
14293
  progress: number().optional(),
14085
14294
  read: boolean(),
@@ -16615,6 +16824,41 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
16615
16824
  keyField: string()
16616
16825
  }).optional()
16617
16826
  })) });
16827
+ /**
16828
+ * The report of a one-shot "move this owner's plaintext secrets into the
16829
+ * keychain" run (D878). COUNTS only - never a value, never a ref.
16830
+ *
16831
+ * The owner runs it on its own rows, through its own write path; an operator
16832
+ * script (`scripts/hub/migrate-raw-secrets.ts`) calls the owner's cap method.
16833
+ * Dry run (`apply: false`) reports what WOULD move and changes nothing.
16834
+ */
16835
+ var RawSecretMigrationInputSchema = object({
16836
+ /** `false` (the default of the script) = report only. */
16837
+ apply: boolean() });
16838
+ var RawSecretMigrationReportSchema = object({
16839
+ apply: boolean(),
16840
+ /** Rows that can carry a secret (a profile, an enrolment). */
16841
+ rows: number().int(),
16842
+ /** Rows whose secret was plaintext when the run started. */
16843
+ raw: number().int(),
16844
+ /** Rows that already held a keychain marker when the run started. */
16845
+ markers: number().int(),
16846
+ /** Plaintext rows moved by THIS run (always 0 on a dry run). */
16847
+ migrated: number().int(),
16848
+ /** Plaintext rows left in place because the move or its read-back failed. */
16849
+ failed: number().int(),
16850
+ /** After the run: rows still holding plaintext. */
16851
+ remainingRaw: number().int(),
16852
+ /** After the run: markers the keychain could not resolve right now. */
16853
+ unresolvedMarkers: number().int(),
16854
+ /**
16855
+ * A second, redundant copy of the secrets' rows that the run found (and, on
16856
+ * `apply`, deleted after verifying the first). Owners with a single copy omit it.
16857
+ */
16858
+ redundantCopies: number().int().optional(),
16859
+ /** True when nothing is raw and every marker resolves. */
16860
+ verified: boolean()
16861
+ });
16618
16862
  /** Where a block runs. The operator chooses — a block driving a device on an
16619
16863
  * agent is the reason placement is not fixed to the hub. */
16620
16864
  var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
@@ -16841,6 +17085,9 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
16841
17085
  }), SettingsUpdateResultSchema, {
16842
17086
  kind: "mutation",
16843
17087
  auth: "admin"
17088
+ }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
17089
+ kind: "mutation",
17090
+ auth: "admin"
16844
17091
  }), method(object({}), object({ libs: array(object({
16845
17092
  filePath: string(),
16846
17093
  content: string()
@@ -17104,6 +17351,347 @@ id: union([string(), number()]) }), {
17104
17351
  auth: "admin"
17105
17352
  });
17106
17353
  /**
17354
+ * records - an addon's own facts and documents (D881): ledgers, audit rows,
17355
+ * config documents, mirrors. The third storage API next to `ctx.settings`
17356
+ * (operator intent, D870) and device-history (numbers over time, D850).
17357
+ *
17358
+ * Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
17359
+ * crossed a process and stated by a hub in-process caller (`ctx.records` does
17360
+ * that with the addon id closed over). It is deliberately NOT in any input
17361
+ * schema, so a session can never supply it, and a call with no stamp is refused
17362
+ * `records/no-caller`. A kind is readable and writable only by its owner; other
17363
+ * addons go through the owner's capability.
17364
+ *
17365
+ * Every read is three-way: a row, `null`, or the call THROWS. A failed read is
17366
+ * never `[]`. Secret fields are never on the wire: a read shows the
17367
+ * redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
17368
+ * resolves itself, and a write may carry only a keychain marker bound to the
17369
+ * row's own ref.
17370
+ *
17371
+ * Internal: addons reach it through `ctx.records`, never directly.
17372
+ */
17373
+ var RecordIdSchema = string().min(1).max(256);
17374
+ /** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
17375
+ var RecordBodySchema = record(string(), unknown());
17376
+ var RecordInputSchema = object({
17377
+ id: RecordIdSchema,
17378
+ /** The device this row belongs to (D865 removal, per-device reads). */
17379
+ deviceId: number().int().optional(),
17380
+ /** The domain clock, ms: observed / created / opened. */
17381
+ at: number().int().optional(),
17382
+ /** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
17383
+ expiresAt: number().int().optional(),
17384
+ data: RecordBodySchema
17385
+ });
17386
+ var RecordViewSchema = object({
17387
+ id: string(),
17388
+ deviceId: number().int().nullable(),
17389
+ at: number().nullable(),
17390
+ expiresAt: number().nullable(),
17391
+ rev: number().int(),
17392
+ updatedAt: number(),
17393
+ data: RecordBodySchema
17394
+ });
17395
+ /** Declared index fields only. Anything else does not compile, by design. */
17396
+ var RecordWhereSchema = record(RecordFieldPathSchema, union([
17397
+ string(),
17398
+ number(),
17399
+ boolean(),
17400
+ object({ in: array(union([
17401
+ string(),
17402
+ number(),
17403
+ boolean()
17404
+ ])).min(1).max(RECORD_BATCH_MAX) }),
17405
+ object({
17406
+ gt: union([string(), number()]).optional(),
17407
+ gte: union([string(), number()]).optional(),
17408
+ lt: union([string(), number()]).optional(),
17409
+ lte: union([string(), number()]).optional()
17410
+ }).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
17411
+ ]));
17412
+ var KindShape = { kind: RecordKindNameSchema };
17413
+ var TimeWindowShape = {
17414
+ deviceId: number().int().optional(),
17415
+ atFrom: number().optional(),
17416
+ atTo: number().optional()
17417
+ };
17418
+ var RecordQueryInputSchema = object({
17419
+ ...KindShape,
17420
+ ...TimeWindowShape,
17421
+ where: RecordWhereSchema.optional(),
17422
+ /** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
17423
+ order: _enum(["at", "id"]).optional(),
17424
+ desc: boolean().optional(),
17425
+ limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
17426
+ /** Opaque keyset cursor from a previous answer. There is no offset. */
17427
+ cursor: string().min(1).optional()
17428
+ });
17429
+ var RecordQueryResultSchema = object({
17430
+ records: array(RecordViewSchema),
17431
+ nextCursor: string().nullable()
17432
+ });
17433
+ var RecordKindStateSchema = _enum([
17434
+ "created",
17435
+ "unchanged",
17436
+ "updated"
17437
+ ]);
17438
+ var DeclareKindResultSchema = object({
17439
+ kindId: number().int(),
17440
+ state: RecordKindStateSchema,
17441
+ createdIndexes: array(string()),
17442
+ /** The boot guard: the kind is empty while its legacy table still holds rows. */
17443
+ adoptionPending: object({
17444
+ legacyTable: string(),
17445
+ legacyRows: number().int()
17446
+ }).nullable()
17447
+ });
17448
+ /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
17449
+ var SecretRefsList = array(string());
17450
+ var RecordSecretRefsSchema = record(string(), record(string(), string()));
17451
+ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
17452
+ kind: "mutation",
17453
+ auth: "admin"
17454
+ }), method(object({
17455
+ ...KindShape,
17456
+ id: RecordIdSchema
17457
+ }), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
17458
+ ...KindShape,
17459
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
17460
+ }), array(RecordViewSchema), { auth: "admin" }), method(object({
17461
+ ...KindShape,
17462
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
17463
+ }), RecordSecretRefsSchema, { auth: "admin" }), method(object({
17464
+ ...KindShape,
17465
+ record: RecordInputSchema,
17466
+ /** Compare-and-set. `0` means "the row must not exist". */
17467
+ ifRev: number().int().min(0).optional()
17468
+ }), object({
17469
+ rev: number().int(),
17470
+ releasedRefs: SecretRefsList
17471
+ }), {
17472
+ kind: "mutation",
17473
+ auth: "admin"
17474
+ }), method(object({
17475
+ ...KindShape,
17476
+ records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
17477
+ }), object({
17478
+ count: number().int(),
17479
+ releasedRefs: SecretRefsList
17480
+ }), {
17481
+ kind: "mutation",
17482
+ auth: "admin"
17483
+ }), method(object({
17484
+ ...KindShape,
17485
+ id: RecordIdSchema,
17486
+ data: RecordBodySchema,
17487
+ at: number().int().nullable().optional(),
17488
+ expiresAt: number().int().nullable().optional(),
17489
+ ifRev: number().int().min(1).optional()
17490
+ }), object({
17491
+ rev: number().int(),
17492
+ releasedRefs: SecretRefsList
17493
+ }), {
17494
+ kind: "mutation",
17495
+ auth: "admin"
17496
+ }), method(object({
17497
+ ...KindShape,
17498
+ id: RecordIdSchema
17499
+ }), object({
17500
+ deleted: boolean(),
17501
+ secretRefs: SecretRefsList
17502
+ }), {
17503
+ kind: "mutation",
17504
+ auth: "admin"
17505
+ }), method(object({
17506
+ ...KindShape,
17507
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
17508
+ }), object({
17509
+ deleted: number().int(),
17510
+ secretRefs: SecretRefsList
17511
+ }), {
17512
+ kind: "mutation",
17513
+ auth: "admin"
17514
+ }), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
17515
+ ...KindShape,
17516
+ ...TimeWindowShape,
17517
+ where: RecordWhereSchema.optional()
17518
+ }), object({ count: number().int() }), { auth: "admin" }), method(object({
17519
+ ...KindShape,
17520
+ deviceId: number().int().optional(),
17521
+ from: number(),
17522
+ to: number(),
17523
+ bucketMs: number().int().positive()
17524
+ }), object({ buckets: array(object({
17525
+ bucketStart: number(),
17526
+ count: number().int()
17527
+ })) }), { auth: "admin" }), method(object({
17528
+ ...KindShape,
17529
+ op: _enum([
17530
+ "sum",
17531
+ "min",
17532
+ "max",
17533
+ "count"
17534
+ ]),
17535
+ field: RecordFieldPathSchema.optional(),
17536
+ groupBy: RecordFieldPathSchema.optional(),
17537
+ where: RecordWhereSchema.optional()
17538
+ }), object({ rows: array(object({
17539
+ group: union([
17540
+ string(),
17541
+ number(),
17542
+ _null()
17543
+ ]),
17544
+ value: number().nullable()
17545
+ })) }), { auth: "admin" }), method(object({
17546
+ ...KindShape,
17547
+ deviceId: number().int().optional(),
17548
+ atBefore: number().optional(),
17549
+ where: RecordWhereSchema.optional()
17550
+ }), object({
17551
+ deleted: number().int(),
17552
+ secretRefs: SecretRefsList
17553
+ }), {
17554
+ kind: "mutation",
17555
+ auth: "admin"
17556
+ }), method(object({
17557
+ deviceId: number().int(),
17558
+ kinds: array(RecordKindNameSchema).optional()
17559
+ }), object({
17560
+ deleted: record(string(), number().int()),
17561
+ secretRefs: SecretRefsList
17562
+ }), {
17563
+ kind: "mutation",
17564
+ auth: "admin"
17565
+ }), method(object(KindShape), object({ id: number().int() }), {
17566
+ kind: "mutation",
17567
+ auth: "admin"
17568
+ });
17569
+ /** One table of the settings database. */
17570
+ var StorageTableSchema = object({
17571
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
17572
+ name: string(),
17573
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
17574
+ owner: string().nullable(),
17575
+ rows: number().int().nonnegative(),
17576
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
17577
+ bytes: number().int().nonnegative().nullable(),
17578
+ /** Declared by an addon since this engine process started (a running addon claims it). */
17579
+ declaredThisBoot: boolean()
17580
+ });
17581
+ /** One declared record kind (D881) as System -> Data shows it. */
17582
+ var StorageKindSchema = object({
17583
+ owner: string(),
17584
+ kind: string(),
17585
+ database: _enum(["settings", "events"]),
17586
+ class: string(),
17587
+ state: _enum([
17588
+ "declared",
17589
+ "orphaned",
17590
+ "dropped"
17591
+ ]),
17592
+ /** `null` when the caller asked for no counts (`counts: false`). */
17593
+ rows: number().int().nonnegative().nullable(),
17594
+ /** Rows x the average size of a sample; null for an empty kind. */
17595
+ bytesEstimate: number().int().nonnegative().nullable(),
17596
+ writeTrigger: string(),
17597
+ retention: RecordRetentionSchema,
17598
+ /** `true` when the kind is an entry kind (the generic entries UI lists it). */
17599
+ entry: boolean(),
17600
+ /** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
17601
+ declaration: RecordKindDeclarationSchema
17602
+ });
17603
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
17604
+ collection: string().min(1),
17605
+ expectRows: number().int().nonnegative()
17606
+ }), object({
17607
+ dropped: literal(true),
17608
+ rows: number().int().nonnegative()
17609
+ }), {
17610
+ kind: "mutation",
17611
+ auth: "admin"
17612
+ }), method(object({
17613
+ owner: string().min(1).optional(),
17614
+ /** `false` skips the per-kind row count (a catalog read only). Default true. */
17615
+ counts: boolean().optional()
17616
+ }), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
17617
+ owner: string().min(1),
17618
+ kind: RecordKindNameSchema
17619
+ }), RecordQueryResultSchema, { auth: "admin" }), method(object({
17620
+ owner: string().min(1),
17621
+ kind: RecordKindNameSchema,
17622
+ records: array(RecordInputSchema).max(RECORD_BATCH_MAX),
17623
+ /** The LAST batch: the copy is complete, the boot guard stops asking. */
17624
+ complete: boolean().optional()
17625
+ }).refine((input) => input.records.length > 0 || input.complete === true, { message: "records must be non-empty unless complete is true" }), object({ adopted: number().int() }), {
17626
+ kind: "mutation",
17627
+ auth: "admin"
17628
+ }), method(object({
17629
+ collection: string().min(1),
17630
+ cursor: string().optional(),
17631
+ limit: number().int().min(1).max(1e3).optional()
17632
+ }), object({
17633
+ rows: array(object({
17634
+ id: string(),
17635
+ data: record(string(), unknown())
17636
+ })),
17637
+ nextCursor: string().nullable()
17638
+ }), { auth: "admin" }), method(object({ collection: string().min(1) }), object({ adopted: boolean() }), { auth: "admin" });
17639
+ /**
17640
+ * record-entries - the ONE admin surface for every list of user-created entries
17641
+ * (D881): notification targets, brokers, LLM profiles, anything an addon
17642
+ * declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
17643
+ *
17644
+ * The UI is generic. `listEntryKinds` hands it, per kind, the JSON Schema of
17645
+ * the entry body with `secret: true` on every credential field, so a password
17646
+ * field is derived from the schema and never from a name. `putEntry` takes the
17647
+ * redaction token for an untouched secret (the stored value is kept), a string
17648
+ * for a new one, and the clear instruction to remove one. A read never carries
17649
+ * a secret value, and a locked keychain refuses the write with
17650
+ * `records/secret-unavailable` instead of storing plaintext.
17651
+ *
17652
+ * Hub-only, served in-process (it acts for each entry's owner on the keychain).
17653
+ */
17654
+ var EntryKindInfoSchema = object({
17655
+ owner: string(),
17656
+ kind: RecordKindNameSchema,
17657
+ title: string(),
17658
+ nameField: string(),
17659
+ /** JSON Schema of the body; `secret: true` marks a password field. */
17660
+ fields: record(string(), unknown()),
17661
+ secretPaths: array(string()),
17662
+ rows: number().int().nonnegative()
17663
+ });
17664
+ var EntryViewSchema = object({
17665
+ id: RecordIdSchema,
17666
+ name: string(),
17667
+ rev: number().int(),
17668
+ updatedAt: number(),
17669
+ /** Secret fields hold the redaction token. */
17670
+ data: RecordBodySchema
17671
+ });
17672
+ var EntryAddress = {
17673
+ owner: string().min(1),
17674
+ kind: RecordKindNameSchema
17675
+ };
17676
+ method(object({}), object({ kinds: array(EntryKindInfoSchema) }), { auth: "admin" }), method(object({ ...EntryAddress }), object({ entries: array(EntryViewSchema) }), { auth: "admin" }), method(object({
17677
+ ...EntryAddress,
17678
+ id: RecordIdSchema.optional(),
17679
+ data: RecordBodySchema,
17680
+ ifRev: number().int().min(1).optional()
17681
+ }), object({
17682
+ id: string(),
17683
+ rev: number().int()
17684
+ }), {
17685
+ kind: "mutation",
17686
+ auth: "admin"
17687
+ }), method(object({
17688
+ ...EntryAddress,
17689
+ id: RecordIdSchema
17690
+ }), object({ deleted: boolean() }), {
17691
+ kind: "mutation",
17692
+ auth: "admin"
17693
+ });
17694
+ /**
17107
17695
  * `keychain` — the hub's encrypted store of secret values (D787).
17108
17696
  *
17109
17697
  * Settings blobs never hold a secret: they hold a marker
@@ -17644,6 +18232,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
17644
18232
  kind: "mutation",
17645
18233
  auth: "admin",
17646
18234
  caller: "required"
18235
+ }), method(object({
18236
+ ref: string().min(1),
18237
+ value: string().min(1)
18238
+ }), object({
18239
+ ref: string(),
18240
+ version: number().int()
18241
+ }), {
18242
+ kind: "mutation",
18243
+ auth: "admin",
18244
+ caller: "required"
17647
18245
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
17648
18246
  deleted: array(string()).readonly(),
17649
18247
  kept: array(object({
@@ -18052,7 +18650,11 @@ var deviceDiscoveryCapability = {
18052
18650
  };
18053
18651
  /**
18054
18652
  * `device-adoption` — generic discovery + adoption surface,
18055
- * system-scoped singleton. The de-HA-ified successor to `ha-discovery`:
18653
+ * system-scoped collection (one provider per integration addon), routed
18654
+ * by explicit `{ addonId }` on direct `deviceAdoption.*` calls — the same
18655
+ * pin model as `connection-test`. The admin UI keeps using
18656
+ * `deviceManager.adoption*` (which already threads `addonId`). The de-HA-ified
18657
+ * successor to `ha-discovery`:
18056
18658
  * keyed by `integrationId` (the addon resolves integrationId → broker
18057
18659
  * internally) rather than a connection-specific `brokerId`, and it
18058
18660
  * REUSES the shared `DiscoveredChildDevice` candidate type from
@@ -18217,7 +18819,7 @@ var ResyncResultSchema = object({
18217
18819
  var deviceAdoptionCapability = {
18218
18820
  name: "device-adoption",
18219
18821
  scope: "system",
18220
- mode: "singleton",
18822
+ mode: "collection",
18221
18823
  status: {
18222
18824
  schema: AdoptionStatusSchema,
18223
18825
  kind: "poll"
@@ -40322,6 +40924,9 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
40322
40924
  }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
40323
40925
  kind: "mutation",
40324
40926
  auth: "admin"
40927
+ }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
40928
+ kind: "mutation",
40929
+ auth: "admin"
40325
40930
  }), method(ProfileRefInputSchema, _void(), {
40326
40931
  kind: "mutation",
40327
40932
  auth: "admin"
@@ -50831,6 +51436,41 @@ var ServerRollbackInfoSchema = object({
50831
51436
  atMs: number(),
50832
51437
  reason: string()
50833
51438
  });
51439
+ /**
51440
+ * The runner boot check (D877): the runners this node PLANNED (hub: one per
51441
+ * `resolveRunnerId` over its forked addons; agent: the runners its process
51442
+ * service holds, because only the hub plans) against the states the process
51443
+ * service reports. Kept in memory, recomputed by the same one-shot check after
51444
+ * every boot, served on `getServerPackageStatus`.
51445
+ *
51446
+ * `failed` is the D6 breaker's terminal state, `refused` a deterministic Init
51447
+ * refusal (an addon that said "I will not start, and here is why"), `missing` a
51448
+ * planned runner the process service has no entry for.
51449
+ */
51450
+ var RunnerRefusalSchema = object({
51451
+ runnerId: string(),
51452
+ addonIds: array(string()),
51453
+ reason: string()
51454
+ });
51455
+ var RunnerBootCheckSchema = object({
51456
+ checkedAtMs: number(),
51457
+ /** Runners planned for this node. */
51458
+ expected: number(),
51459
+ /** Planned runners whose process is `running`. */
51460
+ running: number(),
51461
+ failed: array(string()),
51462
+ refused: array(RunnerRefusalSchema),
51463
+ missing: array(string()),
51464
+ /**
51465
+ * Runners still on their way: `starting`, or `crashed` waiting on a backoff
51466
+ * respawn. The check is FINAL only when this is empty; until then nothing is
51467
+ * pushed and the verdict is re-evaluated on the next runner state change.
51468
+ * Optional for version skew (absent = settled).
51469
+ */
51470
+ pending: array(string()).optional(),
51471
+ /** True when the plan is the hub's (`missing` is then meaningful); false on an agent. */
51472
+ planKnown: boolean()
51473
+ });
50834
51474
  var ServerPackageStatusSchema = object({
50835
51475
  /** Root package name (`@camstack/server` on the hub). */
50836
51476
  packageName: string(),
@@ -50893,7 +51533,14 @@ var ServerPackageStatusSchema = object({
50893
51533
  * for version skew: an older provider's payload simply omits it, and an
50894
51534
  * absent wrapper is unknown — never `native`.
50895
51535
  */
50896
- wrapper: WrapperIdentitySchema.optional()
51536
+ wrapper: WrapperIdentitySchema.optional(),
51537
+ /**
51538
+ * What this node PLANNED to run against what its process service reports
51539
+ * (D877). Absent until the one-shot boot check has run (~60 s after boot) and
51540
+ * on a provider that predates it. Absent is "not checked yet", never "all
51541
+ * well": a consumer that needs the answer treats it as pending.
51542
+ */
51543
+ runners: RunnerBootCheckSchema.optional()
50897
51544
  });
50898
51545
  var ServerUpdateCheckResultSchema = object({
50899
51546
  packageName: string(),
@@ -50944,6 +51591,13 @@ var ServerUpdateOutcomeSchema = _enum([
50944
51591
  "refused",
50945
51592
  "failed"
50946
51593
  ]);
51594
+ /** One addon the pre-flight found the target version would refuse (D877). */
51595
+ var UpdateBlockerSchema = object({
51596
+ addonId: string(),
51597
+ reason: string(),
51598
+ /** What the operator does about it. */
51599
+ remedy: string()
51600
+ });
50947
51601
  var ServerUpdateActionResultSchema = object({
50948
51602
  accepted: boolean(),
50949
51603
  targetVersion: string().nullable(),
@@ -50961,14 +51615,27 @@ var ServerUpdateActionResultSchema = object({
50961
51615
  * The failure itself, verbatim enough to act on (the npm output for a
50962
51616
  * staging install). Non-null only for `failed`.
50963
51617
  */
50964
- error: string().nullable().optional()
51618
+ error: string().nullable().optional(),
51619
+ /**
51620
+ * The addons the target version would refuse to start on this node's live
51621
+ * store (D877), present when the pre-flight refused the update
51622
+ * (`reason: 'addon-would-refuse'`). Optional for version skew.
51623
+ */
51624
+ blockers: array(UpdateBlockerSchema).optional()
50965
51625
  });
50966
51626
  method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
50967
51627
  kind: "mutation",
50968
51628
  auth: "admin"
50969
- }), method(object({
50970
- /** Explicit target version; omitted = latest from the registry. */
50971
- version: string().optional() }), ServerUpdateActionResultSchema, {
51629
+ }), method(object({
51630
+ /** Explicit target version; omitted = latest from the registry. */
51631
+ version: string().optional(),
51632
+ /**
51633
+ * Apply even though the pre-flight (D877) found addons the target would
51634
+ * refuse to start. Off by default: those addons stay down until the
51635
+ * remedy is run, which is the incident this gate exists to prevent.
51636
+ */
51637
+ acknowledgeRefusals: boolean().optional()
51638
+ }), ServerUpdateActionResultSchema, {
50972
51639
  kind: "mutation",
50973
51640
  auth: "admin",
50974
51641
  timeoutMs: 16 * 6e4
@@ -53414,7 +54081,9 @@ var ManifestShapeSchema = object({
53414
54081
  var AddonProcessSchema = object({
53415
54082
  pid: number().optional(),
53416
54083
  mode: string(),
53417
- state: string()
54084
+ state: string(),
54085
+ /** Why the runner is `refused` (an addon's own words), when it is (D877). */
54086
+ reason: string().optional()
53418
54087
  });
53419
54088
  /**
53420
54089
  * Per-addon health snapshot from the kernel's AddonHealthMonitor.
@@ -54182,7 +54851,8 @@ var batteryCapability = {
54182
54851
  DeviceType.Button,
54183
54852
  DeviceType.Switch,
54184
54853
  DeviceType.PetFeeder,
54185
- DeviceType.Container
54854
+ DeviceType.Container,
54855
+ DeviceType.Thermostat
54186
54856
  ],
54187
54857
  methods: {
54188
54858
  /**
@@ -54803,6 +55473,12 @@ var ClimateControlStatusSchema = object({
54803
55473
  /** Horizontal louver swing on/off. Null when the device has no
54804
55474
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
54805
55475
  swingHorizontal: boolean().nullable(),
55476
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
55477
+ minTemp: number().nullable().optional(),
55478
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
55479
+ maxTemp: number().nullable().optional(),
55480
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
55481
+ tempStep: number().positive().nullable().optional(),
54806
55482
  /** Ms epoch when the slice was last updated (push or command). */
54807
55483
  lastFetchedAt: number()
54808
55484
  });
@@ -55657,6 +56333,161 @@ onStatusChanged: { data: object({
55657
56333
  * whether persisting is worth a SQLite commit. */
55658
56334
  volatileStateFields: ["lastChangedAt"]
55659
56335
  };
56336
+ /**
56337
+ * Smart-dishwasher cap — program lifecycle, door / remote-start gates,
56338
+ * consumable levels (salt, rinse aid) and common program options.
56339
+ *
56340
+ * Sources: native Bosch Home Connect (`dishwasher` device type). Reusable
56341
+ * by any integration that exposes the same program / option surface.
56342
+ *
56343
+ * `kind: 'poll'` — cloud REST APIs typically have no push channel; the
56344
+ * provider refreshes the slice on an interval and eagerly after commands.
56345
+ */
56346
+ var DishwasherStateSchema = _enum([
56347
+ "idle",
56348
+ "running",
56349
+ "paused",
56350
+ "finished",
56351
+ "error",
56352
+ "unavailable"
56353
+ ]);
56354
+ var DishwasherConsumableLevelSchema = _enum([
56355
+ "ok",
56356
+ "low",
56357
+ "empty"
56358
+ ]);
56359
+ var DishwasherStatusSchema = object({
56360
+ state: DishwasherStateSchema,
56361
+ /** Active program id/name, or null when none selected / unknown. */
56362
+ program: string().nullable(),
56363
+ /** Program tokens the hardware accepts — drives the UI selector. */
56364
+ availablePrograms: array(string()),
56365
+ /** Minutes remaining on the active cycle, or null when not running. */
56366
+ remainingMinutes: number().int().nonnegative().nullable(),
56367
+ /** Cycle completion 0..100, or null when not applicable. */
56368
+ progressPercent: number().min(0).max(100).nullable(),
56369
+ /** True when the door is reported open. */
56370
+ doorOpen: boolean(),
56371
+ /** True when remote start is allowed (door closed, power on, etc.). */
56372
+ remoteStartAllowed: boolean(),
56373
+ /** Main power / switched-on state. */
56374
+ powerOn: boolean(),
56375
+ saltLevel: DishwasherConsumableLevelSchema.nullable(),
56376
+ rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
56377
+ halfLoad: boolean(),
56378
+ hygienePlus: boolean(),
56379
+ /** Extra dry option when the appliance exposes it. */
56380
+ extraDry: boolean(),
56381
+ /** Intensive zone (lower basket) when the appliance exposes it. */
56382
+ intensiveZone: boolean(),
56383
+ /** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
56384
+ varioSpeedPlus: boolean(),
56385
+ /** Child lock (controls disabled on the appliance). */
56386
+ childLock: boolean(),
56387
+ errorCode: number().nullable(),
56388
+ errorLabel: string().nullable(),
56389
+ lastFetchedAt: number()
56390
+ });
56391
+ var dishwasherCapability = {
56392
+ name: "dishwasher",
56393
+ scope: "device",
56394
+ deviceNative: true,
56395
+ mode: "singleton",
56396
+ deviceTypes: [DeviceType.Dishwasher],
56397
+ methods: {
56398
+ start: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56399
+ kind: "mutation",
56400
+ auth: "protected"
56401
+ }),
56402
+ pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56403
+ kind: "mutation",
56404
+ auth: "protected"
56405
+ }),
56406
+ stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56407
+ kind: "mutation",
56408
+ auth: "protected"
56409
+ }),
56410
+ setProgram: method(object({
56411
+ deviceId: number().int().nonnegative(),
56412
+ program: string().min(1)
56413
+ }), _void(), {
56414
+ kind: "mutation",
56415
+ auth: "protected"
56416
+ }),
56417
+ setPower: method(object({
56418
+ deviceId: number().int().nonnegative(),
56419
+ on: boolean()
56420
+ }), _void(), {
56421
+ kind: "mutation",
56422
+ auth: "protected"
56423
+ }),
56424
+ setHalfLoad: method(object({
56425
+ deviceId: number().int().nonnegative(),
56426
+ on: boolean()
56427
+ }), _void(), {
56428
+ kind: "mutation",
56429
+ auth: "protected"
56430
+ }),
56431
+ setHygienePlus: method(object({
56432
+ deviceId: number().int().nonnegative(),
56433
+ on: boolean()
56434
+ }), _void(), {
56435
+ kind: "mutation",
56436
+ auth: "protected"
56437
+ }),
56438
+ setExtraDry: method(object({
56439
+ deviceId: number().int().nonnegative(),
56440
+ on: boolean()
56441
+ }), _void(), {
56442
+ kind: "mutation",
56443
+ auth: "protected"
56444
+ }),
56445
+ setIntensiveZone: method(object({
56446
+ deviceId: number().int().nonnegative(),
56447
+ on: boolean()
56448
+ }), _void(), {
56449
+ kind: "mutation",
56450
+ auth: "protected"
56451
+ }),
56452
+ setVarioSpeedPlus: method(object({
56453
+ deviceId: number().int().nonnegative(),
56454
+ on: boolean()
56455
+ }), _void(), {
56456
+ kind: "mutation",
56457
+ auth: "protected"
56458
+ }),
56459
+ setChildLock: method(object({
56460
+ deviceId: number().int().nonnegative(),
56461
+ on: boolean()
56462
+ }), _void(), {
56463
+ kind: "mutation",
56464
+ auth: "protected"
56465
+ }),
56466
+ setOption: method(object({
56467
+ deviceId: number().int().nonnegative(),
56468
+ key: string().min(1),
56469
+ value: union([
56470
+ boolean(),
56471
+ string(),
56472
+ number()
56473
+ ])
56474
+ }), _void(), {
56475
+ kind: "mutation",
56476
+ auth: "protected"
56477
+ })
56478
+ },
56479
+ status: {
56480
+ schema: DishwasherStatusSchema,
56481
+ kind: "poll"
56482
+ },
56483
+ runtimeState: DishwasherStatusSchema,
56484
+ durability: "session",
56485
+ history: { fields: { progressPercent: {
56486
+ kind: "gauge",
56487
+ unit: "%",
56488
+ deadband: 2
56489
+ } } }
56490
+ };
55660
56491
  var DoorbellStatusSchema = object({
55661
56492
  /** Ms epoch of the last press. null = never observed since this provider started. */
55662
56493
  lastPressedAt: number().nullable(),
@@ -56459,6 +57290,32 @@ var gasCapability = {
56459
57290
  * whether persisting is worth a SQLite commit. */
56460
57291
  volatileStateFields: ["lastChangedAt"]
56461
57292
  };
57293
+ var HeatingValveStatusSchema = object({
57294
+ /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
57295
+ position: number().min(0).max(100).nullable(),
57296
+ /** Ms epoch when the slice was last updated (push or poll). */
57297
+ lastFetchedAt: number()
57298
+ });
57299
+ var heatingValveCapability = {
57300
+ name: "heating-valve",
57301
+ scope: "device",
57302
+ deviceNative: true,
57303
+ mode: "singleton",
57304
+ deviceTypes: [DeviceType.Thermostat],
57305
+ methods: {},
57306
+ status: {
57307
+ schema: HeatingValveStatusSchema,
57308
+ kind: "push"
57309
+ },
57310
+ runtimeState: HeatingValveStatusSchema,
57311
+ durability: "session",
57312
+ history: { fields: { position: {
57313
+ kind: "gauge",
57314
+ unit: "%",
57315
+ deadband: 1
57316
+ } } },
57317
+ volatileStateFields: ["lastFetchedAt"]
57318
+ };
56462
57319
  /**
56463
57320
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
56464
57321
  * an on/off actuator with an optional target-humidity setpoint and an
@@ -57591,7 +58448,19 @@ var TlsStatusSchema = object({
57591
58448
  sans: array(string()),
57592
58449
  caCertPem: string().nullable(),
57593
58450
  reissueError: string().nullable(),
57594
- restartRequired: boolean()
58451
+ restartRequired: boolean(),
58452
+ /** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
58453
+ caMissing: boolean(),
58454
+ /** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
58455
+ localCaConstrained: boolean().nullable(),
58456
+ /** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
58457
+ caReplacedAt: string().nullable(),
58458
+ /** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
58459
+ storedUpload: object({
58460
+ leafFingerprintSha256: string(),
58461
+ validTo: string(),
58462
+ hasCa: boolean()
58463
+ }).nullable()
57595
58464
  });
57596
58465
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
57597
58466
  /**
@@ -57647,6 +58516,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
57647
58516
  }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
57648
58517
  kind: "mutation",
57649
58518
  auth: "admin"
58519
+ }), method(_void(), TlsStatusSchema, {
58520
+ kind: "mutation",
58521
+ auth: "admin"
57650
58522
  });
57651
58523
  var LockControlStatusSchema = object({
57652
58524
  /** Lifecycle state of the lock. `jammed` means the motor reported
@@ -64140,7 +65012,11 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
64140
65012
  kind: "mutation",
64141
65013
  auth: "admin",
64142
65014
  access: "delete"
64143
- }), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(object({
65015
+ }), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
65016
+ kind: "mutation",
65017
+ auth: "admin",
65018
+ access: "create"
65019
+ }), method(object({
64144
65020
  userId: string(),
64145
65021
  code: string()
64146
65022
  }), object({ valid: boolean() }), {
@@ -65860,6 +66736,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
65860
66736
  dayNight: dayNightCapability,
65861
66737
  deviceDiscovery: deviceDiscoveryCapability,
65862
66738
  deviceStatus: deviceStatusCapability,
66739
+ dishwasher: dishwasherCapability,
65863
66740
  doorbell: doorbellCapability,
65864
66741
  enumSensor: enumSensorCapability,
65865
66742
  eventEmitter: eventEmitterCapability,
@@ -65867,6 +66744,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
65867
66744
  featureProbe: featureProbeCapability,
65868
66745
  flood: floodCapability,
65869
66746
  gas: gasCapability,
66747
+ heatingValve: heatingValveCapability,
65870
66748
  humidifier: humidifierCapability,
65871
66749
  humiditySensor: humiditySensorCapability,
65872
66750
  image: imageCapability,
@@ -65952,13 +66830,14 @@ var BaseDevice = class {
65952
66830
  get state() {
65953
66831
  if (!this._stateProxyCache) {
65954
66832
  const cache = {};
65955
- const handler = { get: (_target, key) => {
66833
+ const handler = { get: (target, key) => {
65956
66834
  const k = key;
65957
- if (k in cache) return cache[k];
66835
+ const existing = Reflect.get(target, k);
66836
+ if (existing !== void 0) return existing;
65958
66837
  const cap = DEVICE_LOCAL_STATE_CAPS[k];
65959
66838
  if (!cap) return void 0;
65960
66839
  const proxy = this.sliceProxy(cap);
65961
- cache[k] = proxy;
66840
+ Reflect.set(target, k, proxy);
65962
66841
  return proxy;
65963
66842
  } };
65964
66843
  this._stateProxyCache = new Proxy(cache, handler);
@@ -66157,7 +67036,7 @@ var BaseDevice = class {
66157
67036
  async updateSourceInfo(patch) {
66158
67037
  const next = mergeSourceInfo(this.sourceInfo, patch);
66159
67038
  this._sourceInfoCache = Object.freeze({ ...next });
66160
- const action = this.ctx.api?.deviceManager?.setMetadata;
67039
+ const action = this.ctx.api.deviceManager?.setMetadata;
66161
67040
  if (action) try {
66162
67041
  await action.mutate({
66163
67042
  deviceId: this.id,
@@ -66192,7 +67071,7 @@ var BaseDevice = class {
66192
67071
  * device boot on a meta refresh.
66193
67072
  */
66194
67073
  async refreshFeatures() {
66195
- const action = this.ctx.api?.deviceManager?.registerDevice;
67074
+ const action = this.ctx.api.deviceManager?.registerDevice;
66196
67075
  if (!action) return;
66197
67076
  try {
66198
67077
  await action.mutate({
@@ -66591,6 +67470,12 @@ var BaseDeviceProvider = class extends BaseAddon {
66591
67470
  provider: this
66592
67471
  }];
66593
67472
  }
67473
+ /** Flatten `super.onInitialize()` whether it returned an array or an envelope. */
67474
+ providerRegistrationsFromSuper(result) {
67475
+ if (Array.isArray(result)) return result;
67476
+ if (result !== void 0 && result.providers !== void 0) return [...result.providers];
67477
+ return [];
67478
+ }
66594
67479
  async onShutdown() {
66595
67480
  this._restoreShuttingDown = true;
66596
67481
  this.cancelRestoreRetries();
@@ -67117,7 +68002,7 @@ var BaseDeviceProvider = class extends BaseAddon {
67117
68002
  return toDeviceSummary(device, this.addonId);
67118
68003
  }
67119
68004
  };
67120
- 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;
68005
+ 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;
67121
68006
  new Set(Object.values(DeviceType));
67122
68007
  Object.freeze({
67123
68008
  "accessories.getStatus": {
@@ -68266,6 +69151,12 @@ Object.freeze({
68266
69151
  addonId: null,
68267
69152
  access: "view"
68268
69153
  },
69154
+ "coreBlocks.migrateRawSecrets": {
69155
+ capName: "core-blocks",
69156
+ capScope: "system",
69157
+ addonId: null,
69158
+ access: "create"
69159
+ },
68269
69160
  "coreBlocks.restart": {
68270
69161
  capName: "core-blocks",
68271
69162
  capScope: "system",
@@ -69532,6 +70423,84 @@ Object.freeze({
69532
70423
  addonId: null,
69533
70424
  access: "view"
69534
70425
  },
70426
+ "dishwasher.getStatus": {
70427
+ capName: "dishwasher",
70428
+ capScope: "device",
70429
+ addonId: null,
70430
+ access: "view"
70431
+ },
70432
+ "dishwasher.pause": {
70433
+ capName: "dishwasher",
70434
+ capScope: "device",
70435
+ addonId: null,
70436
+ access: "create"
70437
+ },
70438
+ "dishwasher.setChildLock": {
70439
+ capName: "dishwasher",
70440
+ capScope: "device",
70441
+ addonId: null,
70442
+ access: "create"
70443
+ },
70444
+ "dishwasher.setExtraDry": {
70445
+ capName: "dishwasher",
70446
+ capScope: "device",
70447
+ addonId: null,
70448
+ access: "create"
70449
+ },
70450
+ "dishwasher.setHalfLoad": {
70451
+ capName: "dishwasher",
70452
+ capScope: "device",
70453
+ addonId: null,
70454
+ access: "create"
70455
+ },
70456
+ "dishwasher.setHygienePlus": {
70457
+ capName: "dishwasher",
70458
+ capScope: "device",
70459
+ addonId: null,
70460
+ access: "create"
70461
+ },
70462
+ "dishwasher.setIntensiveZone": {
70463
+ capName: "dishwasher",
70464
+ capScope: "device",
70465
+ addonId: null,
70466
+ access: "create"
70467
+ },
70468
+ "dishwasher.setOption": {
70469
+ capName: "dishwasher",
70470
+ capScope: "device",
70471
+ addonId: null,
70472
+ access: "create"
70473
+ },
70474
+ "dishwasher.setPower": {
70475
+ capName: "dishwasher",
70476
+ capScope: "device",
70477
+ addonId: null,
70478
+ access: "create"
70479
+ },
70480
+ "dishwasher.setProgram": {
70481
+ capName: "dishwasher",
70482
+ capScope: "device",
70483
+ addonId: null,
70484
+ access: "create"
70485
+ },
70486
+ "dishwasher.setVarioSpeedPlus": {
70487
+ capName: "dishwasher",
70488
+ capScope: "device",
70489
+ addonId: null,
70490
+ access: "create"
70491
+ },
70492
+ "dishwasher.start": {
70493
+ capName: "dishwasher",
70494
+ capScope: "device",
70495
+ addonId: null,
70496
+ access: "create"
70497
+ },
70498
+ "dishwasher.stop": {
70499
+ capName: "dishwasher",
70500
+ capScope: "device",
70501
+ addonId: null,
70502
+ access: "create"
70503
+ },
69535
70504
  "doorbell.getStatus": {
69536
70505
  capName: "doorbell",
69537
70506
  capScope: "device",
@@ -69742,6 +70711,12 @@ Object.freeze({
69742
70711
  addonId: null,
69743
70712
  access: "view"
69744
70713
  },
70714
+ "heatingValve.getStatus": {
70715
+ capName: "heating-valve",
70716
+ capScope: "device",
70717
+ addonId: null,
70718
+ access: "view"
70719
+ },
69745
70720
  "humidifier.getStatus": {
69746
70721
  capName: "humidifier",
69747
70722
  capScope: "device",
@@ -69952,6 +70927,12 @@ Object.freeze({
69952
70927
  addonId: null,
69953
70928
  access: "create"
69954
70929
  },
70930
+ "keychainAdmin.importSecret": {
70931
+ capName: "keychain-admin",
70932
+ capScope: "system",
70933
+ addonId: null,
70934
+ access: "create"
70935
+ },
69955
70936
  "keychainAdmin.listAudit": {
69956
70937
  capName: "keychain-admin",
69957
70938
  capScope: "system",
@@ -70120,6 +71101,12 @@ Object.freeze({
70120
71101
  addonId: null,
70121
71102
  access: "view"
70122
71103
  },
71104
+ "llm.migrateRawSecrets": {
71105
+ capName: "llm",
71106
+ capScope: "system",
71107
+ addonId: null,
71108
+ access: "create"
71109
+ },
70123
71110
  "llm.resolveModelRef": {
70124
71111
  capName: "llm",
70125
71112
  capScope: "system",
@@ -70348,6 +71335,12 @@ Object.freeze({
70348
71335
  addonId: null,
70349
71336
  access: "create"
70350
71337
  },
71338
+ "localNetwork.useStoredUploadedCertificate": {
71339
+ capName: "local-network",
71340
+ capScope: "system",
71341
+ addonId: null,
71342
+ access: "create"
71343
+ },
70351
71344
  "lockControl.getStatus": {
70352
71345
  capName: "lock-control",
70353
71346
  capScope: "device",
@@ -73000,6 +73993,30 @@ Object.freeze({
73000
73993
  addonId: null,
73001
73994
  access: "create"
73002
73995
  },
73996
+ "recordEntries.deleteEntry": {
73997
+ capName: "record-entries",
73998
+ capScope: "system",
73999
+ addonId: null,
74000
+ access: "delete"
74001
+ },
74002
+ "recordEntries.listEntries": {
74003
+ capName: "record-entries",
74004
+ capScope: "system",
74005
+ addonId: null,
74006
+ access: "view"
74007
+ },
74008
+ "recordEntries.listEntryKinds": {
74009
+ capName: "record-entries",
74010
+ capScope: "system",
74011
+ addonId: null,
74012
+ access: "view"
74013
+ },
74014
+ "recordEntries.putEntry": {
74015
+ capName: "record-entries",
74016
+ capScope: "system",
74017
+ addonId: null,
74018
+ access: "create"
74019
+ },
73003
74020
  "recording.getAvailability": {
73004
74021
  capName: "recording",
73005
74022
  capScope: "device",
@@ -73300,6 +74317,102 @@ Object.freeze({
73300
74317
  addonId: null,
73301
74318
  access: "view"
73302
74319
  },
74320
+ "records.aggregate": {
74321
+ capName: "records",
74322
+ capScope: "system",
74323
+ addonId: null,
74324
+ access: "view"
74325
+ },
74326
+ "records.allocateId": {
74327
+ capName: "records",
74328
+ capScope: "system",
74329
+ addonId: null,
74330
+ access: "create"
74331
+ },
74332
+ "records.count": {
74333
+ capName: "records",
74334
+ capScope: "system",
74335
+ addonId: null,
74336
+ access: "view"
74337
+ },
74338
+ "records.declareKinds": {
74339
+ capName: "records",
74340
+ capScope: "system",
74341
+ addonId: null,
74342
+ access: "create"
74343
+ },
74344
+ "records.delete": {
74345
+ capName: "records",
74346
+ capScope: "system",
74347
+ addonId: null,
74348
+ access: "delete"
74349
+ },
74350
+ "records.deleteDevice": {
74351
+ capName: "records",
74352
+ capScope: "system",
74353
+ addonId: null,
74354
+ access: "delete"
74355
+ },
74356
+ "records.deleteMany": {
74357
+ capName: "records",
74358
+ capScope: "system",
74359
+ addonId: null,
74360
+ access: "delete"
74361
+ },
74362
+ "records.deleteWhere": {
74363
+ capName: "records",
74364
+ capScope: "system",
74365
+ addonId: null,
74366
+ access: "delete"
74367
+ },
74368
+ "records.get": {
74369
+ capName: "records",
74370
+ capScope: "system",
74371
+ addonId: null,
74372
+ access: "view"
74373
+ },
74374
+ "records.getMany": {
74375
+ capName: "records",
74376
+ capScope: "system",
74377
+ addonId: null,
74378
+ access: "view"
74379
+ },
74380
+ "records.histogram": {
74381
+ capName: "records",
74382
+ capScope: "system",
74383
+ addonId: null,
74384
+ access: "view"
74385
+ },
74386
+ "records.patch": {
74387
+ capName: "records",
74388
+ capScope: "system",
74389
+ addonId: null,
74390
+ access: "create"
74391
+ },
74392
+ "records.put": {
74393
+ capName: "records",
74394
+ capScope: "system",
74395
+ addonId: null,
74396
+ access: "create"
74397
+ },
74398
+ "records.putMany": {
74399
+ capName: "records",
74400
+ capScope: "system",
74401
+ addonId: null,
74402
+ access: "create"
74403
+ },
74404
+ "records.query": {
74405
+ capName: "records",
74406
+ capScope: "system",
74407
+ addonId: null,
74408
+ access: "view"
74409
+ },
74410
+ "records.secretRefs": {
74411
+ capName: "records",
74412
+ capScope: "system",
74413
+ addonId: null,
74414
+ access: "view"
74415
+ },
73303
74416
  "sceneMonitor.captureReference": {
73304
74417
  capName: "scene-monitor",
73305
74418
  capScope: "device",
@@ -73732,6 +74845,48 @@ Object.freeze({
73732
74845
  addonId: null,
73733
74846
  access: "create"
73734
74847
  },
74848
+ "storageAdmin.adoptRecords": {
74849
+ capName: "storage-admin",
74850
+ capScope: "system",
74851
+ addonId: null,
74852
+ access: "create"
74853
+ },
74854
+ "storageAdmin.dropCollection": {
74855
+ capName: "storage-admin",
74856
+ capScope: "system",
74857
+ addonId: null,
74858
+ access: "delete"
74859
+ },
74860
+ "storageAdmin.isAdoptedLegacyTable": {
74861
+ capName: "storage-admin",
74862
+ capScope: "system",
74863
+ addonId: null,
74864
+ access: "view"
74865
+ },
74866
+ "storageAdmin.listKinds": {
74867
+ capName: "storage-admin",
74868
+ capScope: "system",
74869
+ addonId: null,
74870
+ access: "view"
74871
+ },
74872
+ "storageAdmin.listTables": {
74873
+ capName: "storage-admin",
74874
+ capScope: "system",
74875
+ addonId: null,
74876
+ access: "view"
74877
+ },
74878
+ "storageAdmin.readLegacyCollection": {
74879
+ capName: "storage-admin",
74880
+ capScope: "system",
74881
+ addonId: null,
74882
+ access: "view"
74883
+ },
74884
+ "storageAdmin.readRecords": {
74885
+ capName: "storage-admin",
74886
+ capScope: "system",
74887
+ addonId: null,
74888
+ access: "view"
74889
+ },
73735
74890
  "storageEvictable.evict": {
73736
74891
  capName: "storage-evictable",
73737
74892
  capScope: "system",
@@ -74644,6 +75799,12 @@ Object.freeze({
74644
75799
  addonId: null,
74645
75800
  access: "view"
74646
75801
  },
75802
+ "userManagement.migrateRawSecrets": {
75803
+ capName: "user-management",
75804
+ capScope: "system",
75805
+ addonId: null,
75806
+ access: "create"
75807
+ },
74647
75808
  "userManagement.oauthExchangeCode": {
74648
75809
  capName: "user-management",
74649
75810
  capScope: "system",
@@ -75143,7 +76304,7 @@ Object.freeze({
75143
76304
  access: "create"
75144
76305
  }
75145
76306
  });
75146
- 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;
76307
+ 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;
75147
76308
  DeviceFeature.BatteryOperated;
75148
76309
  Object.freeze({
75149
76310
  "accessories.getStatus": [{
@@ -75967,6 +77128,71 @@ Object.freeze({
75967
77128
  form: "single",
75968
77129
  optional: false
75969
77130
  }],
77131
+ "dishwasher.getStatus": [{
77132
+ name: "deviceId",
77133
+ form: "single",
77134
+ optional: false
77135
+ }],
77136
+ "dishwasher.pause": [{
77137
+ name: "deviceId",
77138
+ form: "single",
77139
+ optional: false
77140
+ }],
77141
+ "dishwasher.setChildLock": [{
77142
+ name: "deviceId",
77143
+ form: "single",
77144
+ optional: false
77145
+ }],
77146
+ "dishwasher.setExtraDry": [{
77147
+ name: "deviceId",
77148
+ form: "single",
77149
+ optional: false
77150
+ }],
77151
+ "dishwasher.setHalfLoad": [{
77152
+ name: "deviceId",
77153
+ form: "single",
77154
+ optional: false
77155
+ }],
77156
+ "dishwasher.setHygienePlus": [{
77157
+ name: "deviceId",
77158
+ form: "single",
77159
+ optional: false
77160
+ }],
77161
+ "dishwasher.setIntensiveZone": [{
77162
+ name: "deviceId",
77163
+ form: "single",
77164
+ optional: false
77165
+ }],
77166
+ "dishwasher.setOption": [{
77167
+ name: "deviceId",
77168
+ form: "single",
77169
+ optional: false
77170
+ }],
77171
+ "dishwasher.setPower": [{
77172
+ name: "deviceId",
77173
+ form: "single",
77174
+ optional: false
77175
+ }],
77176
+ "dishwasher.setProgram": [{
77177
+ name: "deviceId",
77178
+ form: "single",
77179
+ optional: false
77180
+ }],
77181
+ "dishwasher.setVarioSpeedPlus": [{
77182
+ name: "deviceId",
77183
+ form: "single",
77184
+ optional: false
77185
+ }],
77186
+ "dishwasher.start": [{
77187
+ name: "deviceId",
77188
+ form: "single",
77189
+ optional: false
77190
+ }],
77191
+ "dishwasher.stop": [{
77192
+ name: "deviceId",
77193
+ form: "single",
77194
+ optional: false
77195
+ }],
75970
77196
  "doorbell.getStatus": [{
75971
77197
  name: "deviceId",
75972
77198
  form: "single",
@@ -76036,6 +77262,11 @@ Object.freeze({
76036
77262
  form: "single",
76037
77263
  optional: false
76038
77264
  }],
77265
+ "heatingValve.getStatus": [{
77266
+ name: "deviceId",
77267
+ form: "single",
77268
+ optional: false
77269
+ }],
76039
77270
  "humidifier.getStatus": [{
76040
77271
  name: "deviceId",
76041
77272
  form: "single",
@@ -77320,6 +78551,37 @@ Object.freeze({
77320
78551
  form: "single",
77321
78552
  optional: false
77322
78553
  }],
78554
+ "records.count": [{
78555
+ name: "deviceId",
78556
+ form: "single",
78557
+ optional: true
78558
+ }],
78559
+ "records.deleteDevice": [{
78560
+ name: "deviceId",
78561
+ form: "single",
78562
+ optional: false
78563
+ }],
78564
+ "records.deleteWhere": [{
78565
+ name: "deviceId",
78566
+ form: "single",
78567
+ optional: true
78568
+ }],
78569
+ "records.histogram": [{
78570
+ name: "deviceId",
78571
+ form: "single",
78572
+ optional: true
78573
+ }],
78574
+ "records.putMany": [{
78575
+ name: "records",
78576
+ form: "object-array",
78577
+ optional: false,
78578
+ itemField: "deviceId"
78579
+ }],
78580
+ "records.query": [{
78581
+ name: "deviceId",
78582
+ form: "single",
78583
+ optional: true
78584
+ }],
77323
78585
  "sceneMonitor.captureReference": [{
77324
78586
  name: "deviceId",
77325
78587
  form: "single",
@@ -77436,6 +78698,17 @@ Object.freeze({
77436
78698
  form: "single",
77437
78699
  optional: false
77438
78700
  }],
78701
+ "storageAdmin.adoptRecords": [{
78702
+ name: "records",
78703
+ form: "object-array",
78704
+ optional: false,
78705
+ itemField: "deviceId"
78706
+ }],
78707
+ "storageAdmin.readRecords": [{
78708
+ name: "deviceId",
78709
+ form: "single",
78710
+ optional: true
78711
+ }],
77439
78712
  "streamBroker.acquireEgressTranscode": [{
77440
78713
  name: "deviceId",
77441
78714
  form: "single",
@@ -79961,7 +81234,7 @@ var PetkitProviderAddon = class extends BaseDeviceProvider {
79961
81234
  super({});
79962
81235
  }
79963
81236
  async onInitialize() {
79964
- const regs = await super.onInitialize();
81237
+ const regs = this.providerRegistrationsFromSuper(await super.onInitialize());
79965
81238
  this.clients = new PetkitClientRegistry(this.ctx.logger);
79966
81239
  this.clients.setOnConnected((integrationId) => {
79967
81240
  this.ctx.logger.info("PetKit: integration connected", { meta: { integrationId } });