@camstack/addon-provider-petkit 0.2.126 → 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 +1318 -129
  2. package/dist/addon.mjs +1318 -129
  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-BOjuyoPC.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({
@@ -40322,6 +40920,9 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
40322
40920
  }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
40323
40921
  kind: "mutation",
40324
40922
  auth: "admin"
40923
+ }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
40924
+ kind: "mutation",
40925
+ auth: "admin"
40325
40926
  }), method(ProfileRefInputSchema, _void(), {
40326
40927
  kind: "mutation",
40327
40928
  auth: "admin"
@@ -50831,6 +51432,41 @@ var ServerRollbackInfoSchema = object({
50831
51432
  atMs: number(),
50832
51433
  reason: string()
50833
51434
  });
51435
+ /**
51436
+ * The runner boot check (D877): the runners this node PLANNED (hub: one per
51437
+ * `resolveRunnerId` over its forked addons; agent: the runners its process
51438
+ * service holds, because only the hub plans) against the states the process
51439
+ * service reports. Kept in memory, recomputed by the same one-shot check after
51440
+ * every boot, served on `getServerPackageStatus`.
51441
+ *
51442
+ * `failed` is the D6 breaker's terminal state, `refused` a deterministic Init
51443
+ * refusal (an addon that said "I will not start, and here is why"), `missing` a
51444
+ * planned runner the process service has no entry for.
51445
+ */
51446
+ var RunnerRefusalSchema = object({
51447
+ runnerId: string(),
51448
+ addonIds: array(string()),
51449
+ reason: string()
51450
+ });
51451
+ var RunnerBootCheckSchema = object({
51452
+ checkedAtMs: number(),
51453
+ /** Runners planned for this node. */
51454
+ expected: number(),
51455
+ /** Planned runners whose process is `running`. */
51456
+ running: number(),
51457
+ failed: array(string()),
51458
+ refused: array(RunnerRefusalSchema),
51459
+ missing: array(string()),
51460
+ /**
51461
+ * Runners still on their way: `starting`, or `crashed` waiting on a backoff
51462
+ * respawn. The check is FINAL only when this is empty; until then nothing is
51463
+ * pushed and the verdict is re-evaluated on the next runner state change.
51464
+ * Optional for version skew (absent = settled).
51465
+ */
51466
+ pending: array(string()).optional(),
51467
+ /** True when the plan is the hub's (`missing` is then meaningful); false on an agent. */
51468
+ planKnown: boolean()
51469
+ });
50834
51470
  var ServerPackageStatusSchema = object({
50835
51471
  /** Root package name (`@camstack/server` on the hub). */
50836
51472
  packageName: string(),
@@ -50893,7 +51529,14 @@ var ServerPackageStatusSchema = object({
50893
51529
  * for version skew: an older provider's payload simply omits it, and an
50894
51530
  * absent wrapper is unknown — never `native`.
50895
51531
  */
50896
- wrapper: WrapperIdentitySchema.optional()
51532
+ wrapper: WrapperIdentitySchema.optional(),
51533
+ /**
51534
+ * What this node PLANNED to run against what its process service reports
51535
+ * (D877). Absent until the one-shot boot check has run (~60 s after boot) and
51536
+ * on a provider that predates it. Absent is "not checked yet", never "all
51537
+ * well": a consumer that needs the answer treats it as pending.
51538
+ */
51539
+ runners: RunnerBootCheckSchema.optional()
50897
51540
  });
50898
51541
  var ServerUpdateCheckResultSchema = object({
50899
51542
  packageName: string(),
@@ -50944,6 +51587,13 @@ var ServerUpdateOutcomeSchema = _enum([
50944
51587
  "refused",
50945
51588
  "failed"
50946
51589
  ]);
51590
+ /** One addon the pre-flight found the target version would refuse (D877). */
51591
+ var UpdateBlockerSchema = object({
51592
+ addonId: string(),
51593
+ reason: string(),
51594
+ /** What the operator does about it. */
51595
+ remedy: string()
51596
+ });
50947
51597
  var ServerUpdateActionResultSchema = object({
50948
51598
  accepted: boolean(),
50949
51599
  targetVersion: string().nullable(),
@@ -50961,14 +51611,27 @@ var ServerUpdateActionResultSchema = object({
50961
51611
  * The failure itself, verbatim enough to act on (the npm output for a
50962
51612
  * staging install). Non-null only for `failed`.
50963
51613
  */
50964
- error: string().nullable().optional()
51614
+ error: string().nullable().optional(),
51615
+ /**
51616
+ * The addons the target version would refuse to start on this node's live
51617
+ * store (D877), present when the pre-flight refused the update
51618
+ * (`reason: 'addon-would-refuse'`). Optional for version skew.
51619
+ */
51620
+ blockers: array(UpdateBlockerSchema).optional()
50965
51621
  });
50966
51622
  method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
50967
51623
  kind: "mutation",
50968
51624
  auth: "admin"
50969
- }), method(object({
50970
- /** Explicit target version; omitted = latest from the registry. */
50971
- version: string().optional() }), ServerUpdateActionResultSchema, {
51625
+ }), method(object({
51626
+ /** Explicit target version; omitted = latest from the registry. */
51627
+ version: string().optional(),
51628
+ /**
51629
+ * Apply even though the pre-flight (D877) found addons the target would
51630
+ * refuse to start. Off by default: those addons stay down until the
51631
+ * remedy is run, which is the incident this gate exists to prevent.
51632
+ */
51633
+ acknowledgeRefusals: boolean().optional()
51634
+ }), ServerUpdateActionResultSchema, {
50972
51635
  kind: "mutation",
50973
51636
  auth: "admin",
50974
51637
  timeoutMs: 16 * 6e4
@@ -53414,7 +54077,9 @@ var ManifestShapeSchema = object({
53414
54077
  var AddonProcessSchema = object({
53415
54078
  pid: number().optional(),
53416
54079
  mode: string(),
53417
- state: string()
54080
+ state: string(),
54081
+ /** Why the runner is `refused` (an addon's own words), when it is (D877). */
54082
+ reason: string().optional()
53418
54083
  });
53419
54084
  /**
53420
54085
  * Per-addon health snapshot from the kernel's AddonHealthMonitor.
@@ -54182,7 +54847,8 @@ var batteryCapability = {
54182
54847
  DeviceType.Button,
54183
54848
  DeviceType.Switch,
54184
54849
  DeviceType.PetFeeder,
54185
- DeviceType.Container
54850
+ DeviceType.Container,
54851
+ DeviceType.Thermostat
54186
54852
  ],
54187
54853
  methods: {
54188
54854
  /**
@@ -54803,6 +55469,12 @@ var ClimateControlStatusSchema = object({
54803
55469
  /** Horizontal louver swing on/off. Null when the device has no
54804
55470
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
54805
55471
  swingHorizontal: boolean().nullable(),
55472
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
55473
+ minTemp: number().nullable().optional(),
55474
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
55475
+ maxTemp: number().nullable().optional(),
55476
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
55477
+ tempStep: number().positive().nullable().optional(),
54806
55478
  /** Ms epoch when the slice was last updated (push or command). */
54807
55479
  lastFetchedAt: number()
54808
55480
  });
@@ -55657,6 +56329,125 @@ onStatusChanged: { data: object({
55657
56329
  * whether persisting is worth a SQLite commit. */
55658
56330
  volatileStateFields: ["lastChangedAt"]
55659
56331
  };
56332
+ /**
56333
+ * Smart-dishwasher cap — program lifecycle, door / remote-start gates,
56334
+ * consumable levels (salt, rinse aid) and common program options.
56335
+ *
56336
+ * Sources: native Bosch Home Connect (`dishwasher` device type). Reusable
56337
+ * by any integration that exposes the same program / option surface.
56338
+ *
56339
+ * `kind: 'poll'` — cloud REST APIs typically have no push channel; the
56340
+ * provider refreshes the slice on an interval and eagerly after commands.
56341
+ */
56342
+ var DishwasherStateSchema = _enum([
56343
+ "idle",
56344
+ "running",
56345
+ "paused",
56346
+ "finished",
56347
+ "error",
56348
+ "unavailable"
56349
+ ]);
56350
+ var DishwasherConsumableLevelSchema = _enum([
56351
+ "ok",
56352
+ "low",
56353
+ "empty"
56354
+ ]);
56355
+ var DishwasherStatusSchema = object({
56356
+ state: DishwasherStateSchema,
56357
+ /** Active program id/name, or null when none selected / unknown. */
56358
+ program: string().nullable(),
56359
+ /** Program tokens the hardware accepts — drives the UI selector. */
56360
+ availablePrograms: array(string()),
56361
+ /** Minutes remaining on the active cycle, or null when not running. */
56362
+ remainingMinutes: number().int().nonnegative().nullable(),
56363
+ /** Cycle completion 0..100, or null when not applicable. */
56364
+ progressPercent: number().min(0).max(100).nullable(),
56365
+ /** True when the door is reported open. */
56366
+ doorOpen: boolean(),
56367
+ /** True when remote start is allowed (door closed, power on, etc.). */
56368
+ remoteStartAllowed: boolean(),
56369
+ /** Main power / switched-on state. */
56370
+ powerOn: boolean(),
56371
+ saltLevel: DishwasherConsumableLevelSchema.nullable(),
56372
+ rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
56373
+ halfLoad: boolean(),
56374
+ hygienePlus: boolean(),
56375
+ errorCode: number().nullable(),
56376
+ errorLabel: string().nullable(),
56377
+ lastFetchedAt: number()
56378
+ });
56379
+ var dishwasherCapability = {
56380
+ name: "dishwasher",
56381
+ scope: "device",
56382
+ deviceNative: true,
56383
+ mode: "singleton",
56384
+ deviceTypes: [DeviceType.Dishwasher],
56385
+ methods: {
56386
+ start: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56387
+ kind: "mutation",
56388
+ auth: "protected"
56389
+ }),
56390
+ pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56391
+ kind: "mutation",
56392
+ auth: "protected"
56393
+ }),
56394
+ stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
56395
+ kind: "mutation",
56396
+ auth: "protected"
56397
+ }),
56398
+ setProgram: method(object({
56399
+ deviceId: number().int().nonnegative(),
56400
+ program: string().min(1)
56401
+ }), _void(), {
56402
+ kind: "mutation",
56403
+ auth: "protected"
56404
+ }),
56405
+ setPower: method(object({
56406
+ deviceId: number().int().nonnegative(),
56407
+ on: boolean()
56408
+ }), _void(), {
56409
+ kind: "mutation",
56410
+ auth: "protected"
56411
+ }),
56412
+ setHalfLoad: method(object({
56413
+ deviceId: number().int().nonnegative(),
56414
+ on: boolean()
56415
+ }), _void(), {
56416
+ kind: "mutation",
56417
+ auth: "protected"
56418
+ }),
56419
+ setHygienePlus: method(object({
56420
+ deviceId: number().int().nonnegative(),
56421
+ on: boolean()
56422
+ }), _void(), {
56423
+ kind: "mutation",
56424
+ auth: "protected"
56425
+ }),
56426
+ setOption: method(object({
56427
+ deviceId: number().int().nonnegative(),
56428
+ key: string().min(1),
56429
+ value: union([
56430
+ boolean(),
56431
+ string(),
56432
+ number()
56433
+ ])
56434
+ }), _void(), {
56435
+ kind: "mutation",
56436
+ auth: "protected"
56437
+ })
56438
+ },
56439
+ status: {
56440
+ schema: DishwasherStatusSchema,
56441
+ kind: "poll"
56442
+ },
56443
+ runtimeState: DishwasherStatusSchema,
56444
+ durability: "session",
56445
+ history: { fields: { progressPercent: {
56446
+ kind: "gauge",
56447
+ unit: "%",
56448
+ deadband: 2
56449
+ } } }
56450
+ };
55660
56451
  var DoorbellStatusSchema = object({
55661
56452
  /** Ms epoch of the last press. null = never observed since this provider started. */
55662
56453
  lastPressedAt: number().nullable(),
@@ -56459,6 +57250,32 @@ var gasCapability = {
56459
57250
  * whether persisting is worth a SQLite commit. */
56460
57251
  volatileStateFields: ["lastChangedAt"]
56461
57252
  };
57253
+ var HeatingValveStatusSchema = object({
57254
+ /** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
57255
+ position: number().min(0).max(100).nullable(),
57256
+ /** Ms epoch when the slice was last updated (push or poll). */
57257
+ lastFetchedAt: number()
57258
+ });
57259
+ var heatingValveCapability = {
57260
+ name: "heating-valve",
57261
+ scope: "device",
57262
+ deviceNative: true,
57263
+ mode: "singleton",
57264
+ deviceTypes: [DeviceType.Thermostat],
57265
+ methods: {},
57266
+ status: {
57267
+ schema: HeatingValveStatusSchema,
57268
+ kind: "push"
57269
+ },
57270
+ runtimeState: HeatingValveStatusSchema,
57271
+ durability: "session",
57272
+ history: { fields: { position: {
57273
+ kind: "gauge",
57274
+ unit: "%",
57275
+ deadband: 1
57276
+ } } },
57277
+ volatileStateFields: ["lastFetchedAt"]
57278
+ };
56462
57279
  /**
56463
57280
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
56464
57281
  * an on/off actuator with an optional target-humidity setpoint and an
@@ -57591,7 +58408,19 @@ var TlsStatusSchema = object({
57591
58408
  sans: array(string()),
57592
58409
  caCertPem: string().nullable(),
57593
58410
  reissueError: string().nullable(),
57594
- restartRequired: boolean()
58411
+ restartRequired: boolean(),
58412
+ /** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
58413
+ caMissing: boolean(),
58414
+ /** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
58415
+ localCaConstrained: boolean().nullable(),
58416
+ /** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
58417
+ caReplacedAt: string().nullable(),
58418
+ /** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
58419
+ storedUpload: object({
58420
+ leafFingerprintSha256: string(),
58421
+ validTo: string(),
58422
+ hasCa: boolean()
58423
+ }).nullable()
57595
58424
  });
57596
58425
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
57597
58426
  /**
@@ -57647,6 +58476,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
57647
58476
  }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
57648
58477
  kind: "mutation",
57649
58478
  auth: "admin"
58479
+ }), method(_void(), TlsStatusSchema, {
58480
+ kind: "mutation",
58481
+ auth: "admin"
57650
58482
  });
57651
58483
  var LockControlStatusSchema = object({
57652
58484
  /** Lifecycle state of the lock. `jammed` means the motor reported
@@ -64140,7 +64972,11 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
64140
64972
  kind: "mutation",
64141
64973
  auth: "admin",
64142
64974
  access: "delete"
64143
- }), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(object({
64975
+ }), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
64976
+ kind: "mutation",
64977
+ auth: "admin",
64978
+ access: "create"
64979
+ }), method(object({
64144
64980
  userId: string(),
64145
64981
  code: string()
64146
64982
  }), object({ valid: boolean() }), {
@@ -65860,6 +66696,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
65860
66696
  dayNight: dayNightCapability,
65861
66697
  deviceDiscovery: deviceDiscoveryCapability,
65862
66698
  deviceStatus: deviceStatusCapability,
66699
+ dishwasher: dishwasherCapability,
65863
66700
  doorbell: doorbellCapability,
65864
66701
  enumSensor: enumSensorCapability,
65865
66702
  eventEmitter: eventEmitterCapability,
@@ -65867,6 +66704,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
65867
66704
  featureProbe: featureProbeCapability,
65868
66705
  flood: floodCapability,
65869
66706
  gas: gasCapability,
66707
+ heatingValve: heatingValveCapability,
65870
66708
  humidifier: humidifierCapability,
65871
66709
  humiditySensor: humiditySensorCapability,
65872
66710
  image: imageCapability,
@@ -65952,13 +66790,14 @@ var BaseDevice = class {
65952
66790
  get state() {
65953
66791
  if (!this._stateProxyCache) {
65954
66792
  const cache = {};
65955
- const handler = { get: (_target, key) => {
66793
+ const handler = { get: (target, key) => {
65956
66794
  const k = key;
65957
- if (k in cache) return cache[k];
66795
+ const existing = Reflect.get(target, k);
66796
+ if (existing !== void 0) return existing;
65958
66797
  const cap = DEVICE_LOCAL_STATE_CAPS[k];
65959
66798
  if (!cap) return void 0;
65960
66799
  const proxy = this.sliceProxy(cap);
65961
- cache[k] = proxy;
66800
+ Reflect.set(target, k, proxy);
65962
66801
  return proxy;
65963
66802
  } };
65964
66803
  this._stateProxyCache = new Proxy(cache, handler);
@@ -66157,7 +66996,7 @@ var BaseDevice = class {
66157
66996
  async updateSourceInfo(patch) {
66158
66997
  const next = mergeSourceInfo(this.sourceInfo, patch);
66159
66998
  this._sourceInfoCache = Object.freeze({ ...next });
66160
- const action = this.ctx.api?.deviceManager?.setMetadata;
66999
+ const action = this.ctx.api.deviceManager?.setMetadata;
66161
67000
  if (action) try {
66162
67001
  await action.mutate({
66163
67002
  deviceId: this.id,
@@ -66192,7 +67031,7 @@ var BaseDevice = class {
66192
67031
  * device boot on a meta refresh.
66193
67032
  */
66194
67033
  async refreshFeatures() {
66195
- const action = this.ctx.api?.deviceManager?.registerDevice;
67034
+ const action = this.ctx.api.deviceManager?.registerDevice;
66196
67035
  if (!action) return;
66197
67036
  try {
66198
67037
  await action.mutate({
@@ -66591,6 +67430,12 @@ var BaseDeviceProvider = class extends BaseAddon {
66591
67430
  provider: this
66592
67431
  }];
66593
67432
  }
67433
+ /** Flatten `super.onInitialize()` whether it returned an array or an envelope. */
67434
+ providerRegistrationsFromSuper(result) {
67435
+ if (Array.isArray(result)) return result;
67436
+ if (result !== void 0 && result.providers !== void 0) return [...result.providers];
67437
+ return [];
67438
+ }
66594
67439
  async onShutdown() {
66595
67440
  this._restoreShuttingDown = true;
66596
67441
  this.cancelRestoreRetries();
@@ -67117,7 +67962,7 @@ var BaseDeviceProvider = class extends BaseAddon {
67117
67962
  return toDeviceSummary(device, this.addonId);
67118
67963
  }
67119
67964
  };
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;
67965
+ 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
67966
  new Set(Object.values(DeviceType));
67122
67967
  Object.freeze({
67123
67968
  "accessories.getStatus": {
@@ -68266,6 +69111,12 @@ Object.freeze({
68266
69111
  addonId: null,
68267
69112
  access: "view"
68268
69113
  },
69114
+ "coreBlocks.migrateRawSecrets": {
69115
+ capName: "core-blocks",
69116
+ capScope: "system",
69117
+ addonId: null,
69118
+ access: "create"
69119
+ },
68269
69120
  "coreBlocks.restart": {
68270
69121
  capName: "core-blocks",
68271
69122
  capScope: "system",
@@ -69532,6 +70383,60 @@ Object.freeze({
69532
70383
  addonId: null,
69533
70384
  access: "view"
69534
70385
  },
70386
+ "dishwasher.getStatus": {
70387
+ capName: "dishwasher",
70388
+ capScope: "device",
70389
+ addonId: null,
70390
+ access: "view"
70391
+ },
70392
+ "dishwasher.pause": {
70393
+ capName: "dishwasher",
70394
+ capScope: "device",
70395
+ addonId: null,
70396
+ access: "create"
70397
+ },
70398
+ "dishwasher.setHalfLoad": {
70399
+ capName: "dishwasher",
70400
+ capScope: "device",
70401
+ addonId: null,
70402
+ access: "create"
70403
+ },
70404
+ "dishwasher.setHygienePlus": {
70405
+ capName: "dishwasher",
70406
+ capScope: "device",
70407
+ addonId: null,
70408
+ access: "create"
70409
+ },
70410
+ "dishwasher.setOption": {
70411
+ capName: "dishwasher",
70412
+ capScope: "device",
70413
+ addonId: null,
70414
+ access: "create"
70415
+ },
70416
+ "dishwasher.setPower": {
70417
+ capName: "dishwasher",
70418
+ capScope: "device",
70419
+ addonId: null,
70420
+ access: "create"
70421
+ },
70422
+ "dishwasher.setProgram": {
70423
+ capName: "dishwasher",
70424
+ capScope: "device",
70425
+ addonId: null,
70426
+ access: "create"
70427
+ },
70428
+ "dishwasher.start": {
70429
+ capName: "dishwasher",
70430
+ capScope: "device",
70431
+ addonId: null,
70432
+ access: "create"
70433
+ },
70434
+ "dishwasher.stop": {
70435
+ capName: "dishwasher",
70436
+ capScope: "device",
70437
+ addonId: null,
70438
+ access: "create"
70439
+ },
69535
70440
  "doorbell.getStatus": {
69536
70441
  capName: "doorbell",
69537
70442
  capScope: "device",
@@ -69742,6 +70647,12 @@ Object.freeze({
69742
70647
  addonId: null,
69743
70648
  access: "view"
69744
70649
  },
70650
+ "heatingValve.getStatus": {
70651
+ capName: "heating-valve",
70652
+ capScope: "device",
70653
+ addonId: null,
70654
+ access: "view"
70655
+ },
69745
70656
  "humidifier.getStatus": {
69746
70657
  capName: "humidifier",
69747
70658
  capScope: "device",
@@ -69952,6 +70863,12 @@ Object.freeze({
69952
70863
  addonId: null,
69953
70864
  access: "create"
69954
70865
  },
70866
+ "keychainAdmin.importSecret": {
70867
+ capName: "keychain-admin",
70868
+ capScope: "system",
70869
+ addonId: null,
70870
+ access: "create"
70871
+ },
69955
70872
  "keychainAdmin.listAudit": {
69956
70873
  capName: "keychain-admin",
69957
70874
  capScope: "system",
@@ -70120,6 +71037,12 @@ Object.freeze({
70120
71037
  addonId: null,
70121
71038
  access: "view"
70122
71039
  },
71040
+ "llm.migrateRawSecrets": {
71041
+ capName: "llm",
71042
+ capScope: "system",
71043
+ addonId: null,
71044
+ access: "create"
71045
+ },
70123
71046
  "llm.resolveModelRef": {
70124
71047
  capName: "llm",
70125
71048
  capScope: "system",
@@ -70348,6 +71271,12 @@ Object.freeze({
70348
71271
  addonId: null,
70349
71272
  access: "create"
70350
71273
  },
71274
+ "localNetwork.useStoredUploadedCertificate": {
71275
+ capName: "local-network",
71276
+ capScope: "system",
71277
+ addonId: null,
71278
+ access: "create"
71279
+ },
70351
71280
  "lockControl.getStatus": {
70352
71281
  capName: "lock-control",
70353
71282
  capScope: "device",
@@ -73000,6 +73929,30 @@ Object.freeze({
73000
73929
  addonId: null,
73001
73930
  access: "create"
73002
73931
  },
73932
+ "recordEntries.deleteEntry": {
73933
+ capName: "record-entries",
73934
+ capScope: "system",
73935
+ addonId: null,
73936
+ access: "delete"
73937
+ },
73938
+ "recordEntries.listEntries": {
73939
+ capName: "record-entries",
73940
+ capScope: "system",
73941
+ addonId: null,
73942
+ access: "view"
73943
+ },
73944
+ "recordEntries.listEntryKinds": {
73945
+ capName: "record-entries",
73946
+ capScope: "system",
73947
+ addonId: null,
73948
+ access: "view"
73949
+ },
73950
+ "recordEntries.putEntry": {
73951
+ capName: "record-entries",
73952
+ capScope: "system",
73953
+ addonId: null,
73954
+ access: "create"
73955
+ },
73003
73956
  "recording.getAvailability": {
73004
73957
  capName: "recording",
73005
73958
  capScope: "device",
@@ -73300,6 +74253,102 @@ Object.freeze({
73300
74253
  addonId: null,
73301
74254
  access: "view"
73302
74255
  },
74256
+ "records.aggregate": {
74257
+ capName: "records",
74258
+ capScope: "system",
74259
+ addonId: null,
74260
+ access: "view"
74261
+ },
74262
+ "records.allocateId": {
74263
+ capName: "records",
74264
+ capScope: "system",
74265
+ addonId: null,
74266
+ access: "create"
74267
+ },
74268
+ "records.count": {
74269
+ capName: "records",
74270
+ capScope: "system",
74271
+ addonId: null,
74272
+ access: "view"
74273
+ },
74274
+ "records.declareKinds": {
74275
+ capName: "records",
74276
+ capScope: "system",
74277
+ addonId: null,
74278
+ access: "create"
74279
+ },
74280
+ "records.delete": {
74281
+ capName: "records",
74282
+ capScope: "system",
74283
+ addonId: null,
74284
+ access: "delete"
74285
+ },
74286
+ "records.deleteDevice": {
74287
+ capName: "records",
74288
+ capScope: "system",
74289
+ addonId: null,
74290
+ access: "delete"
74291
+ },
74292
+ "records.deleteMany": {
74293
+ capName: "records",
74294
+ capScope: "system",
74295
+ addonId: null,
74296
+ access: "delete"
74297
+ },
74298
+ "records.deleteWhere": {
74299
+ capName: "records",
74300
+ capScope: "system",
74301
+ addonId: null,
74302
+ access: "delete"
74303
+ },
74304
+ "records.get": {
74305
+ capName: "records",
74306
+ capScope: "system",
74307
+ addonId: null,
74308
+ access: "view"
74309
+ },
74310
+ "records.getMany": {
74311
+ capName: "records",
74312
+ capScope: "system",
74313
+ addonId: null,
74314
+ access: "view"
74315
+ },
74316
+ "records.histogram": {
74317
+ capName: "records",
74318
+ capScope: "system",
74319
+ addonId: null,
74320
+ access: "view"
74321
+ },
74322
+ "records.patch": {
74323
+ capName: "records",
74324
+ capScope: "system",
74325
+ addonId: null,
74326
+ access: "create"
74327
+ },
74328
+ "records.put": {
74329
+ capName: "records",
74330
+ capScope: "system",
74331
+ addonId: null,
74332
+ access: "create"
74333
+ },
74334
+ "records.putMany": {
74335
+ capName: "records",
74336
+ capScope: "system",
74337
+ addonId: null,
74338
+ access: "create"
74339
+ },
74340
+ "records.query": {
74341
+ capName: "records",
74342
+ capScope: "system",
74343
+ addonId: null,
74344
+ access: "view"
74345
+ },
74346
+ "records.secretRefs": {
74347
+ capName: "records",
74348
+ capScope: "system",
74349
+ addonId: null,
74350
+ access: "view"
74351
+ },
73303
74352
  "sceneMonitor.captureReference": {
73304
74353
  capName: "scene-monitor",
73305
74354
  capScope: "device",
@@ -73732,6 +74781,48 @@ Object.freeze({
73732
74781
  addonId: null,
73733
74782
  access: "create"
73734
74783
  },
74784
+ "storageAdmin.adoptRecords": {
74785
+ capName: "storage-admin",
74786
+ capScope: "system",
74787
+ addonId: null,
74788
+ access: "create"
74789
+ },
74790
+ "storageAdmin.dropCollection": {
74791
+ capName: "storage-admin",
74792
+ capScope: "system",
74793
+ addonId: null,
74794
+ access: "delete"
74795
+ },
74796
+ "storageAdmin.isAdoptedLegacyTable": {
74797
+ capName: "storage-admin",
74798
+ capScope: "system",
74799
+ addonId: null,
74800
+ access: "view"
74801
+ },
74802
+ "storageAdmin.listKinds": {
74803
+ capName: "storage-admin",
74804
+ capScope: "system",
74805
+ addonId: null,
74806
+ access: "view"
74807
+ },
74808
+ "storageAdmin.listTables": {
74809
+ capName: "storage-admin",
74810
+ capScope: "system",
74811
+ addonId: null,
74812
+ access: "view"
74813
+ },
74814
+ "storageAdmin.readLegacyCollection": {
74815
+ capName: "storage-admin",
74816
+ capScope: "system",
74817
+ addonId: null,
74818
+ access: "view"
74819
+ },
74820
+ "storageAdmin.readRecords": {
74821
+ capName: "storage-admin",
74822
+ capScope: "system",
74823
+ addonId: null,
74824
+ access: "view"
74825
+ },
73735
74826
  "storageEvictable.evict": {
73736
74827
  capName: "storage-evictable",
73737
74828
  capScope: "system",
@@ -74644,6 +75735,12 @@ Object.freeze({
74644
75735
  addonId: null,
74645
75736
  access: "view"
74646
75737
  },
75738
+ "userManagement.migrateRawSecrets": {
75739
+ capName: "user-management",
75740
+ capScope: "system",
75741
+ addonId: null,
75742
+ access: "create"
75743
+ },
74647
75744
  "userManagement.oauthExchangeCode": {
74648
75745
  capName: "user-management",
74649
75746
  capScope: "system",
@@ -75143,7 +76240,7 @@ Object.freeze({
75143
76240
  access: "create"
75144
76241
  }
75145
76242
  });
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;
76243
+ 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
76244
  DeviceFeature.BatteryOperated;
75148
76245
  Object.freeze({
75149
76246
  "accessories.getStatus": [{
@@ -75967,6 +77064,51 @@ Object.freeze({
75967
77064
  form: "single",
75968
77065
  optional: false
75969
77066
  }],
77067
+ "dishwasher.getStatus": [{
77068
+ name: "deviceId",
77069
+ form: "single",
77070
+ optional: false
77071
+ }],
77072
+ "dishwasher.pause": [{
77073
+ name: "deviceId",
77074
+ form: "single",
77075
+ optional: false
77076
+ }],
77077
+ "dishwasher.setHalfLoad": [{
77078
+ name: "deviceId",
77079
+ form: "single",
77080
+ optional: false
77081
+ }],
77082
+ "dishwasher.setHygienePlus": [{
77083
+ name: "deviceId",
77084
+ form: "single",
77085
+ optional: false
77086
+ }],
77087
+ "dishwasher.setOption": [{
77088
+ name: "deviceId",
77089
+ form: "single",
77090
+ optional: false
77091
+ }],
77092
+ "dishwasher.setPower": [{
77093
+ name: "deviceId",
77094
+ form: "single",
77095
+ optional: false
77096
+ }],
77097
+ "dishwasher.setProgram": [{
77098
+ name: "deviceId",
77099
+ form: "single",
77100
+ optional: false
77101
+ }],
77102
+ "dishwasher.start": [{
77103
+ name: "deviceId",
77104
+ form: "single",
77105
+ optional: false
77106
+ }],
77107
+ "dishwasher.stop": [{
77108
+ name: "deviceId",
77109
+ form: "single",
77110
+ optional: false
77111
+ }],
75970
77112
  "doorbell.getStatus": [{
75971
77113
  name: "deviceId",
75972
77114
  form: "single",
@@ -76036,6 +77178,11 @@ Object.freeze({
76036
77178
  form: "single",
76037
77179
  optional: false
76038
77180
  }],
77181
+ "heatingValve.getStatus": [{
77182
+ name: "deviceId",
77183
+ form: "single",
77184
+ optional: false
77185
+ }],
76039
77186
  "humidifier.getStatus": [{
76040
77187
  name: "deviceId",
76041
77188
  form: "single",
@@ -77320,6 +78467,37 @@ Object.freeze({
77320
78467
  form: "single",
77321
78468
  optional: false
77322
78469
  }],
78470
+ "records.count": [{
78471
+ name: "deviceId",
78472
+ form: "single",
78473
+ optional: true
78474
+ }],
78475
+ "records.deleteDevice": [{
78476
+ name: "deviceId",
78477
+ form: "single",
78478
+ optional: false
78479
+ }],
78480
+ "records.deleteWhere": [{
78481
+ name: "deviceId",
78482
+ form: "single",
78483
+ optional: true
78484
+ }],
78485
+ "records.histogram": [{
78486
+ name: "deviceId",
78487
+ form: "single",
78488
+ optional: true
78489
+ }],
78490
+ "records.putMany": [{
78491
+ name: "records",
78492
+ form: "object-array",
78493
+ optional: false,
78494
+ itemField: "deviceId"
78495
+ }],
78496
+ "records.query": [{
78497
+ name: "deviceId",
78498
+ form: "single",
78499
+ optional: true
78500
+ }],
77323
78501
  "sceneMonitor.captureReference": [{
77324
78502
  name: "deviceId",
77325
78503
  form: "single",
@@ -77436,6 +78614,17 @@ Object.freeze({
77436
78614
  form: "single",
77437
78615
  optional: false
77438
78616
  }],
78617
+ "storageAdmin.adoptRecords": [{
78618
+ name: "records",
78619
+ form: "object-array",
78620
+ optional: false,
78621
+ itemField: "deviceId"
78622
+ }],
78623
+ "storageAdmin.readRecords": [{
78624
+ name: "deviceId",
78625
+ form: "single",
78626
+ optional: true
78627
+ }],
77439
78628
  "streamBroker.acquireEgressTranscode": [{
77440
78629
  name: "deviceId",
77441
78630
  form: "single",
@@ -79961,7 +81150,7 @@ var PetkitProviderAddon = class extends BaseDeviceProvider {
79961
81150
  super({});
79962
81151
  }
79963
81152
  async onInitialize() {
79964
- const regs = await super.onInitialize();
81153
+ const regs = this.providerRegistrationsFromSuper(await super.onInitialize());
79965
81154
  this.clients = new PetkitClientRegistry(this.ctx.logger);
79966
81155
  this.clients.setOnConnected((integrationId) => {
79967
81156
  this.ctx.logger.info("PetKit: integration connected", { meta: { integrationId } });