@camstack/addon-provider-homeassistant 1.2.647 → 1.2.649

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.
@@ -5972,7 +5972,7 @@ var ZodIssueCode = {
5972
5972
  var ZodFirstPartyTypeKind;
5973
5973
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5974
5974
  //#endregion
5975
- //#region ../types/dist/sleep-B-vp7x15.mjs
5975
+ //#region ../types/dist/sleep-CdJHPm-k.mjs
5976
5976
  /**
5977
5977
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5978
5978
  * window to float samples (D455).
@@ -6089,7 +6089,7 @@ function isKeychainStoredField(field) {
6089
6089
  var SECRET_CLEAR = { $secretClear: true };
6090
6090
  /** The `'***'` token D634 redaction writes for non-admins. */
6091
6091
  var LEGACY_DEVICE_REDACTION = "***";
6092
- function isRecord$6(value) {
6092
+ function isRecord$7(value) {
6093
6093
  return typeof value === "object" && value !== null && !Array.isArray(value);
6094
6094
  }
6095
6095
  function makeSecretMarker(ref) {
@@ -6100,12 +6100,12 @@ function makeSecretMarker(ref) {
6100
6100
  }
6101
6101
  /** A stored marker: an object whose `$secret` is a non-empty string. */
6102
6102
  function isSecretMarker(value) {
6103
- if (!isRecord$6(value)) return false;
6103
+ if (!isRecord$7(value)) return false;
6104
6104
  const ref = value["$secret"];
6105
6105
  return typeof ref === "string" && ref.length > 0;
6106
6106
  }
6107
6107
  function isSecretClear(value) {
6108
- return isRecord$6(value) && value["$secretClear"] === true;
6108
+ return isRecord$7(value) && value["$secretClear"] === true;
6109
6109
  }
6110
6110
  /**
6111
6111
  * The UI view of a stored secret field value: a marker becomes "set", inline
@@ -6488,6 +6488,53 @@ function emitReadiness(bus, params) {
6488
6488
  ts
6489
6489
  }));
6490
6490
  }
6491
+ /** Why a catalogue entry is not a runnable body of its declared kind. */
6492
+ var AddonMigrationDefinitionError = class extends Error {
6493
+ id;
6494
+ constructor(id, message) {
6495
+ super(`${id}: ${message}`);
6496
+ this.id = id;
6497
+ this.name = "AddonMigrationDefinitionError";
6498
+ }
6499
+ };
6500
+ /**
6501
+ * Bind a catalogue to the addon's ports. Throws {@link AddonMigrationDefinitionError} when
6502
+ * an entry's `bodyKind` does not match the body it carries, or is `sql` (a hub-only
6503
+ * body). A throwing body never escapes `execute`: it is reported as a failed result.
6504
+ */
6505
+ function defineAddonMigrations(catalogue, ports) {
6506
+ return catalogue.map((entry) => {
6507
+ const { meta } = entry;
6508
+ if ("plan" in entry) {
6509
+ if (meta.bodyKind !== "plan") throw new AddonMigrationDefinitionError(meta.id, `carries a plan body but declares "${meta.bodyKind}"`);
6510
+ return {
6511
+ meta,
6512
+ execute: () => guarded(async () => ({
6513
+ kind: "plan",
6514
+ plan: await entry.plan(ports)
6515
+ }))
6516
+ };
6517
+ }
6518
+ if (meta.bodyKind !== "procedure") throw new AddonMigrationDefinitionError(meta.id, `carries a procedure body but declares "${meta.bodyKind}"`);
6519
+ return {
6520
+ meta,
6521
+ execute: () => guarded(async () => ({
6522
+ kind: "procedure",
6523
+ result: await entry.procedure(ports)
6524
+ }))
6525
+ };
6526
+ });
6527
+ }
6528
+ async function guarded(work) {
6529
+ try {
6530
+ return await work();
6531
+ } catch (err) {
6532
+ return {
6533
+ kind: "failed",
6534
+ error: errMsg(err)
6535
+ };
6536
+ }
6537
+ }
6491
6538
  var NOT_RUN = {
6492
6539
  declared: false,
6493
6540
  snapshot: null,
@@ -6811,7 +6858,7 @@ var KeychainWriteRefusedError = class extends Error {
6811
6858
  }
6812
6859
  };
6813
6860
  var KEEP = { kind: "keep" };
6814
- function isRecord$5(value) {
6861
+ function isRecord$6(value) {
6815
6862
  return typeof value === "object" && value !== null && !Array.isArray(value);
6816
6863
  }
6817
6864
  function defaultMintId() {
@@ -6824,7 +6871,7 @@ function collectSecretMarkerRefs(value, out = []) {
6824
6871
  return out;
6825
6872
  }
6826
6873
  if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
6827
- else if (isRecord$5(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6874
+ else if (isRecord$6(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6828
6875
  return out;
6829
6876
  }
6830
6877
  async function storedValueEquals(secrets, marker, candidate) {
@@ -6879,7 +6926,7 @@ function rowId(row, idKey) {
6879
6926
  return null;
6880
6927
  }
6881
6928
  async function writeArray(ctx, node, incoming, stored, fieldPath) {
6882
- const storedRows = Array.isArray(stored) ? stored.filter(isRecord$5) : [];
6929
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord$6) : [];
6883
6930
  if (!Array.isArray(incoming)) {
6884
6931
  ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
6885
6932
  return {
@@ -6895,7 +6942,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
6895
6942
  const kept = /* @__PURE__ */ new Set();
6896
6943
  const rows = [];
6897
6944
  for (const row of incoming) {
6898
- if (!isRecord$5(row)) {
6945
+ if (!isRecord$6(row)) {
6899
6946
  rows.push(row);
6900
6947
  continue;
6901
6948
  }
@@ -6920,7 +6967,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6920
6967
  case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
6921
6968
  case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
6922
6969
  case "object": {
6923
- if (!isRecord$5(incoming)) {
6970
+ if (!isRecord$6(incoming)) {
6924
6971
  if (incoming === void 0) return KEEP;
6925
6972
  ctx.deletes.push(...collectSecretMarkerRefs(stored));
6926
6973
  return {
@@ -6928,7 +6975,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6928
6975
  value: incoming
6929
6976
  };
6930
6977
  }
6931
- const storedObj = isRecord$5(stored) ? stored : {};
6978
+ const storedObj = isRecord$6(stored) ? stored : {};
6932
6979
  return {
6933
6980
  kind: "value",
6934
6981
  value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
@@ -7051,7 +7098,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
7051
7098
  value: items
7052
7099
  };
7053
7100
  }
7054
- if (isRecord$5(v)) {
7101
+ if (isRecord$6(v)) {
7055
7102
  const out = {};
7056
7103
  for (const [k, child] of Object.entries(v)) {
7057
7104
  const s = substitute(child);
@@ -7083,7 +7130,7 @@ var SecretOverwriteRefusedError = class extends Error {
7083
7130
  this.name = "SecretOverwriteRefusedError";
7084
7131
  }
7085
7132
  };
7086
- function isRecord$4(value) {
7133
+ function isRecord$5(value) {
7087
7134
  return typeof value === "object" && value !== null && !Array.isArray(value);
7088
7135
  }
7089
7136
  function resolvedValue(pass, marker) {
@@ -7112,7 +7159,7 @@ function inArray(pass, incoming, stored) {
7112
7159
  return incoming.map((item, index) => {
7113
7160
  const twin = stored[index];
7114
7161
  if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
7115
- if (isRecord$4(item) && isRecord$4(twin)) return inArrayRow(pass, item, twin);
7162
+ if (isRecord$5(item) && isRecord$5(twin)) return inArrayRow(pass, item, twin);
7116
7163
  if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
7117
7164
  return item;
7118
7165
  });
@@ -7123,7 +7170,7 @@ function inArrayRow(pass, incoming, stored) {
7123
7170
  const twin = stored[key];
7124
7171
  if (isSecretMarker(twin)) {
7125
7172
  if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
7126
- } else if (isRecord$4(value) && isRecord$4(twin)) out[key] = inArrayRow(pass, value, twin);
7173
+ } else if (isRecord$5(value) && isRecord$5(twin)) out[key] = inArrayRow(pass, value, twin);
7127
7174
  else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
7128
7175
  }
7129
7176
  return out;
@@ -7131,7 +7178,7 @@ function inArrayRow(pass, incoming, stored) {
7131
7178
  async function walk(pass, incoming, stored) {
7132
7179
  if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
7133
7180
  if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
7134
- if (!isRecord$4(incoming) || !isRecord$4(stored)) return incoming;
7181
+ if (!isRecord$5(incoming) || !isRecord$5(stored)) return incoming;
7135
7182
  const out = { ...incoming };
7136
7183
  for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
7137
7184
  return out;
@@ -7162,7 +7209,7 @@ async function restoreUnchangedSecrets(input) {
7162
7209
  function isJsonEqual(a, b) {
7163
7210
  if (Object.is(a, b)) return true;
7164
7211
  if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
7165
- if (isRecord$4(a) && isRecord$4(b)) {
7212
+ if (isRecord$5(a) && isRecord$5(b)) {
7166
7213
  const keys = Object.keys(a);
7167
7214
  if (keys.length !== Object.keys(b).length) return false;
7168
7215
  return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
@@ -7186,7 +7233,7 @@ function markerKeysOf(blob, owner) {
7186
7233
  }
7187
7234
  function nodeFromStored(value, owner) {
7188
7235
  if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
7189
- if (!isRecord$4(value)) return null;
7236
+ if (!isRecord$5(value)) return null;
7190
7237
  const children = /* @__PURE__ */ new Map();
7191
7238
  for (const [key, child] of Object.entries(value)) {
7192
7239
  const node = nodeFromStored(child, owner);
@@ -7805,6 +7852,44 @@ var BaseAddon = class {
7805
7852
  return null;
7806
7853
  }
7807
7854
  /**
7855
+ * D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
7856
+ * provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
7857
+ * declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
7858
+ * {@link nodeSettingsSchema} and of {@link deviceDefaultsSchema} are disjoint, and a key never
7859
+ * contains `@` (guard `check-scoped-settings-declared`). SHAPE only: values are read by scope.
7860
+ */
7861
+ async clusterSettingsSchema(_cap) {
7862
+ return null;
7863
+ }
7864
+ /**
7865
+ * D986: the form shape of the settings one capability owns PER NODE, or `null`. Stored as
7866
+ * `addon-settings` rows `<addonId>.<field>@<nodeId>` carrying `nodeId` in the row data, so a
7867
+ * forgotten node's rows go in one central delete.
7868
+ */
7869
+ async nodeSettingsSchema(_cap) {
7870
+ return null;
7871
+ }
7872
+ /**
7873
+ * D986: the form shape of the FLEET DEFAULTS of the per-device settings one capability owns,
7874
+ * or `null`. Stored as `addon-settings` rows `<addonId>.<field>`; a device resolves its value
7875
+ * as device row, then this default, then the schema default (`resolveDeviceSettings`).
7876
+ */
7877
+ async deviceDefaultsSchema(_cap) {
7878
+ return null;
7879
+ }
7880
+ /**
7881
+ * The host's read of the scoped declarations: the schema one capability declares for `scope`,
7882
+ * or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
7883
+ * host asks this method.
7884
+ */
7885
+ async getScopedSettingsSchema(scope, cap) {
7886
+ switch (scope) {
7887
+ case "cluster": return this.clusterSettingsSchema(cap);
7888
+ case "node": return this.nodeSettingsSchema(cap);
7889
+ case "device-defaults": return this.deviceDefaultsSchema(cap);
7890
+ }
7891
+ }
7892
+ /**
7808
7893
  * What the create wizard and the integration edit page render for one instance: the addon's
7809
7894
  * per-instance schema when it declares one, else its global schema (what they always showed).
7810
7895
  */
@@ -8598,47 +8683,6 @@ function normalizeAddonInitResult(result) {
8598
8683
  if (Array.isArray(result)) return { providers: result };
8599
8684
  return result;
8600
8685
  }
8601
- /**
8602
- * The share-view scope kinds a share-link token can carry. The hub mints
8603
- * and validates them (`ShareViewGrantSchema` builds its
8604
- * enum from this list); a data plane names the kinds it ACCEPTS in `share`.
8605
- */
8606
- var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
8607
- /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
8608
- var PeerBytesTicketSchema = object({
8609
- /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
8610
- url: string().min(1),
8611
- /**
8612
- * The HOST node this URL means something on — the hub or a named agent,
8613
- * never a runner. {@link AddonPeerBytes.open} compares it to its own and
8614
- * refuses `cross-node` by name when they differ, without dialling.
8615
- */
8616
- hostNodeId: string().min(1),
8617
- expiresAtMs: number().int().nonnegative(),
8618
- /**
8619
- * What the producer DECLARED the body to be, when it knows — `null` when it
8620
- * does not. Never `0` for unknown (D393): a consumer sizing a bound off this
8621
- * must be able to tell "the producer did not say" from "the body is empty".
8622
- */
8623
- declaredBytes: number().int().nonnegative().nullable()
8624
- });
8625
- /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8626
- /** The files a kind may live in. `history` is device-history's (D850), never records. */
8627
- var RECORD_DATABASES = ["settings", "events"];
8628
- /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8629
- var RECORD_CLASSES = [
8630
- "config",
8631
- "ledger",
8632
- "audit",
8633
- "mirror",
8634
- "ephemeral"
8635
- ];
8636
- var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8637
- var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8638
- var RECORD_BATCH_MAX = 1e3;
8639
- var RECORD_QUERY_MAX = 1e3;
8640
- /** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
8641
- var RECORD_SETTINGS_MAX_ROWS = 5e4;
8642
8686
  var PREFIX = "records/";
8643
8687
  /** The base of every records failure. `detail` is the human part of the message. */
8644
8688
  var RecordsError = class extends Error {
@@ -8671,6 +8715,83 @@ var DeclarationRefusedError = class extends RecordsError {
8671
8715
  this.name = "DeclarationRefusedError";
8672
8716
  }
8673
8717
  };
8718
+ /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8719
+ /** The files a kind may live in. `history` is device-history's (D850), never records. */
8720
+ var RECORD_DATABASES = ["settings", "events"];
8721
+ /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8722
+ var RECORD_CLASSES = [
8723
+ "config",
8724
+ "ledger",
8725
+ "audit",
8726
+ "mirror",
8727
+ "ephemeral"
8728
+ ];
8729
+ var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8730
+ var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8731
+ var RECORD_BATCH_MAX = 1e3;
8732
+ var RECORD_QUERY_MAX = 1e3;
8733
+ /** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
8734
+ var RECORD_SETTINGS_MAX_ROWS = 5e4;
8735
+ /**
8736
+ * Secret fields of a record, addressed the way a Zod schema declares them
8737
+ * (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
8738
+ * (`auth.accessToken`, `headers[].value`).
8739
+ *
8740
+ * Array elements are matched by a DECLARED id key (`secretArrayKeys`:
8741
+ * `{ headers: 'name' }`), never by index, so reordering a list keeps every
8742
+ * keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
8743
+ * leaf names its element: `headers[X-Api].value`.
8744
+ *
8745
+ * Pure and browser-safe. Nothing here mutates: every transform returns a new value.
8746
+ */
8747
+ /** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
8748
+ function parseSecretPath(path) {
8749
+ if (path.length === 0) throw new InvalidRecordError("an empty secret path");
8750
+ return path.split(".").map((raw) => {
8751
+ const each = raw.endsWith("[]");
8752
+ const key = each ? raw.slice(0, -2) : raw;
8753
+ if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
8754
+ return {
8755
+ key,
8756
+ each
8757
+ };
8758
+ });
8759
+ }
8760
+ /** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
8761
+ function secretArrayPrefixes(path) {
8762
+ const segments = parseSecretPath(path);
8763
+ const prefixes = [];
8764
+ const walked = [];
8765
+ for (const segment of segments) {
8766
+ walked.push(segment.key);
8767
+ if (segment.each) prefixes.push(walked.join("."));
8768
+ }
8769
+ return prefixes;
8770
+ }
8771
+ /**
8772
+ * The share-view scope kinds a share-link token can carry. The hub mints
8773
+ * and validates them (`ShareViewGrantSchema` builds its
8774
+ * enum from this list); a data plane names the kinds it ACCEPTS in `share`.
8775
+ */
8776
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
8777
+ /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
8778
+ var PeerBytesTicketSchema = object({
8779
+ /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
8780
+ url: string().min(1),
8781
+ /**
8782
+ * The HOST node this URL means something on — the hub or a named agent,
8783
+ * never a runner. {@link AddonPeerBytes.open} compares it to its own and
8784
+ * refuses `cross-node` by name when they differ, without dialling.
8785
+ */
8786
+ hostNodeId: string().min(1),
8787
+ expiresAtMs: number().int().nonnegative(),
8788
+ /**
8789
+ * What the producer DECLARED the body to be, when it knows — `null` when it
8790
+ * does not. Never `0` for unknown (D393): a consumer sizing a bound off this
8791
+ * must be able to tell "the producer did not say" from "the body is empty".
8792
+ */
8793
+ declaredBytes: number().int().nonnegative().nullable()
8794
+ });
8674
8795
  function defOf(schema) {
8675
8796
  const zodBag = Reflect.get(schema, "_zod");
8676
8797
  if (typeof zodBag !== "object" || zodBag === null) return {};
@@ -8739,42 +8860,6 @@ function collectZodSecretPaths(schema, prefix = "") {
8739
8860
  return [...new Set(paths)];
8740
8861
  }
8741
8862
  /**
8742
- * Secret fields of a record, addressed the way a Zod schema declares them
8743
- * (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
8744
- * (`auth.accessToken`, `headers[].value`).
8745
- *
8746
- * Array elements are matched by a DECLARED id key (`secretArrayKeys`:
8747
- * `{ headers: 'name' }`), never by index, so reordering a list keeps every
8748
- * keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
8749
- * leaf names its element: `headers[X-Api].value`.
8750
- *
8751
- * Pure and browser-safe. Nothing here mutates: every transform returns a new value.
8752
- */
8753
- /** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
8754
- function parseSecretPath(path) {
8755
- if (path.length === 0) throw new InvalidRecordError("an empty secret path");
8756
- return path.split(".").map((raw) => {
8757
- const each = raw.endsWith("[]");
8758
- const key = each ? raw.slice(0, -2) : raw;
8759
- if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
8760
- return {
8761
- key,
8762
- each
8763
- };
8764
- });
8765
- }
8766
- /** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
8767
- function secretArrayPrefixes(path) {
8768
- const segments = parseSecretPath(path);
8769
- const prefixes = [];
8770
- const walked = [];
8771
- for (const segment of segments) {
8772
- walked.push(segment.key);
8773
- if (segment.each) prefixes.push(walked.join("."));
8774
- }
8775
- return prefixes;
8776
- }
8777
- /**
8778
8863
  * Record kinds (D881): the declaration an addon makes of one kind of fact, the
8779
8864
  * Zod row schema that describes it, and the helper that turns the two into the
8780
8865
  * JSON the engine stores.
@@ -9739,6 +9824,7 @@ var CAP_NAMES = Object.freeze([
9739
9824
  "humidity-sensor",
9740
9825
  "image",
9741
9826
  "image-settings",
9827
+ "integration-instances",
9742
9828
  "integrations",
9743
9829
  "intercom",
9744
9830
  "ir-transmitter",
@@ -11672,7 +11758,7 @@ object({
11672
11758
  params: record(string(), union([string(), number()])).optional()
11673
11759
  });
11674
11760
  //#endregion
11675
- //#region ../types/dist/composition-kKKOdFIG.mjs
11761
+ //#region ../types/dist/composition-BDUoQsw9.mjs
11676
11762
  /**
11677
11763
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11678
11764
  *
@@ -14124,6 +14210,16 @@ var MigrationPlanSchema = object({ edits: array(discriminatedUnion("op", [
14124
14210
  precondition: PreconditionSchema
14125
14211
  })
14126
14212
  ])) });
14213
+ /** `JSON.stringify` with object keys sorted, so equal values spell the same. */
14214
+ function canonicalJson(value) {
14215
+ if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
14216
+ if (typeof value === "object" && value !== null) return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
14217
+ return JSON.stringify(value) ?? "null";
14218
+ }
14219
+ /** The precondition string for a value a plan read (`null` when it read nothing). */
14220
+ function preconditionOf(value) {
14221
+ return canonicalJson(value ?? null);
14222
+ }
14127
14223
  /**
14128
14224
  * What a `procedure` body reports. `already-done` means the rerun observed the
14129
14225
  * work finished (a crash between the work and the ledger row, or a manual fix)
@@ -16072,8 +16168,18 @@ var LogChannelDescriptorSchema = object({
16072
16168
  * the operator narrows to one camera, sees nothing, and concludes the code
16073
16169
  * path was never taken.
16074
16170
  */
16075
- perDevice: boolean()
16076
- });
16171
+ perDevice: boolean(),
16172
+ /**
16173
+ * What the channel is FOR, so the UI can list the channels an addon offers.
16174
+ *
16175
+ * - `diagnostic` (the default): a per-component diagnostic armed from the
16176
+ * Levels page, emitted at its declared level.
16177
+ * - `debug`: a `debug.<channel>` dump watched live for a short window
16178
+ * (`ctx.debug`). Always per-device, and its lines are denied by the log
16179
+ * ring unless somebody is watching that device.
16180
+ */
16181
+ kind: _enum(["diagnostic", "debug"]).default("diagnostic")
16182
+ }).refine((d) => d.kind !== "debug" || d.name.startsWith("debug.") && d.perDevice, { message: "a debug channel is named debug.<channel> and is per-device" });
16077
16183
  /**
16078
16184
  * An armed window over one channel, as the document hands it to a mirror.
16079
16185
  *
@@ -21289,6 +21395,7 @@ var SYSTEM_CAP_VOCABULARY = {
21289
21395
  "load-contribution": system(INTERNAL),
21290
21396
  "local-network": system(NETWORK),
21291
21397
  "log-channels": system(INTERNAL),
21398
+ "integration-instances": system(INTERNAL),
21292
21399
  "log-destination": system(INTERNAL),
21293
21400
  "login-method": system(IDENTITY),
21294
21401
  "mesh-network": system(NETWORK),
@@ -43589,7 +43696,7 @@ var TargetKindCapsSchema = object({
43589
43696
  * the union is large and not meant for runtime validation here; the exported
43590
43697
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
43591
43698
  */
43592
- var ConfigSchemaPassthrough$3 = unknown();
43699
+ var ConfigSchemaPassthrough$4 = unknown();
43593
43700
  var TargetKindSchema = object({
43594
43701
  kind: string(),
43595
43702
  label: string(),
@@ -43626,7 +43733,7 @@ var TargetKindSchema = object({
43626
43733
  * its raster path, which is the safe default for an unknown image.
43627
43734
  */
43628
43735
  iconMediaType: string().optional(),
43629
- configSchema: ConfigSchemaPassthrough$3,
43736
+ configSchema: ConfigSchemaPassthrough$4,
43630
43737
  /**
43631
43738
  * The config keys that decide WHERE this kind's secrets are sent (server,
43632
43739
  * base URL, instance, chat, recipient). A save that changes any of them
@@ -51615,6 +51722,45 @@ var deviceProviderCapability = {
51615
51722
  }
51616
51723
  };
51617
51724
  /**
51725
+ * What the passive listener of `local-network` saw on the wire, before any
51726
+ * provider classified it. Device-manager turns an observation into the hits
51727
+ * of the providers whose declared discovery modes it matches.
51728
+ */
51729
+ var DiscoveryObservationSchema = discriminatedUnion("kind", [object({
51730
+ kind: literal("mdns"),
51731
+ /** Normalised DNS-SD service type, e.g. `_esphomelib._tcp`. */
51732
+ serviceType: string(),
51733
+ instance: string(),
51734
+ host: string(),
51735
+ /** The A record address when the announcement carried one. */
51736
+ address: string().nullable(),
51737
+ port: number().int(),
51738
+ txt: record(string(), string())
51739
+ }), object({
51740
+ kind: literal("ssdp"),
51741
+ /** Sender address of the datagram. */
51742
+ host: string(),
51743
+ port: number().int(),
51744
+ /** `NT` of a NOTIFY, `ST` of a search response. */
51745
+ target: string(),
51746
+ headers: record(string(), string())
51747
+ })]);
51748
+ object({
51749
+ addonId: string(),
51750
+ candidate: DiscoveryCandidateSchema,
51751
+ firstSeenAt: number(),
51752
+ lastSeenAt: number(),
51753
+ dismissed: boolean()
51754
+ });
51755
+ var DiscoveryProposalSchema = object({
51756
+ id: string(),
51757
+ addonId: string(),
51758
+ candidate: DiscoveryCandidateSchema,
51759
+ firstSeenAt: number(),
51760
+ lastSeenAt: number(),
51761
+ dismissed: boolean()
51762
+ });
51763
+ /**
51618
51764
  * Device Manager capability — hub-side singleton that unifies device persistence,
51619
51765
  * live registry access, and all management operations into a single tRPC surface.
51620
51766
  *
@@ -52343,6 +52489,19 @@ method(object({
52343
52489
  }), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
52344
52490
  kind: "mutation",
52345
52491
  auth: "admin"
52492
+ }), method(object({}), object({ proposals: array(DiscoveryProposalSchema).readonly() }), { auth: "admin" }), method(object({ id: string() }), object({ dismissed: boolean() }), {
52493
+ kind: "mutation",
52494
+ auth: "admin"
52495
+ }), method(object({ observations: array(DiscoveryObservationSchema).max(512) }), object({ proposed: number().int() }), {
52496
+ kind: "mutation",
52497
+ auth: "admin"
52498
+ }), method(object({}), object({
52499
+ providers: number().int(),
52500
+ proposed: number().int()
52501
+ }), {
52502
+ kind: "mutation",
52503
+ auth: "admin",
52504
+ timeoutMs: 12e4
52346
52505
  }), method(object({ addonId: string() }), object({ deviceType: _enum(DeviceType).nullable() }), { auth: "admin" }), method(object({ addonId: string() }), unknown(), { auth: "admin" }), method(object({
52347
52506
  deviceId: number(),
52348
52507
  key: string(),
@@ -55582,9 +55741,15 @@ var AvailableIntegrationTypeSchema = object({
55582
55741
  * branch on CAP — never on addon name. `device-adoption` integrations
55583
55742
  * (Home Assistant, …) route through the broker step (Approach A);
55584
55743
  * `device-provider` integrations (Reolink/ONVIF) keep the
55585
- * legacy config → discovery flow.
55744
+ * legacy config → discovery flow. `connection` is an addon that only holds a
55745
+ * connection as typed records (`integration-instances`, D985): no devices,
55746
+ * no discovery.
55586
55747
  */
55587
- kind: _enum(["device-adoption", "device-provider"]),
55748
+ kind: _enum([
55749
+ "device-adoption",
55750
+ "device-provider",
55751
+ "connection"
55752
+ ]),
55588
55753
  /**
55589
55754
  * For `device-adoption` addons: the broker kind to create/link
55590
55755
  * (e.g. `home-assistant`), declared in the addon manifest. `null` for
@@ -55697,6 +55862,67 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
55697
55862
  auth: "admin"
55698
55863
  });
55699
55864
  /**
55865
+ * `integration-instances` - the connection of an addon that holds ONE or SEVERAL
55866
+ * (an account, a relay, a destination) as typed records, shown on the
55867
+ * Integrations page like every other integration (D985, D986).
55868
+ *
55869
+ * A collection of providers: each addon that holds connections registers one
55870
+ * and the hub aggregator (`integrations.*`) reaches a provider with the pinned
55871
+ * `addonId`. It is internal and has no route: the operator-facing surface is
55872
+ * the `integrations` cap, which keeps ONE list.
55873
+ *
55874
+ * Identity: the instance id IS the `integrations` registry id (`int_NNNN`,
55875
+ * immutable, already in the `/integrations/<id>` URL). A second operator-chosen
55876
+ * slug would be a second authority over one identity (D224). `name` is the
55877
+ * editable label.
55878
+ *
55879
+ * Secrets: every field the instance kind marks `.meta({ secret: true })` reaches
55880
+ * the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
55881
+ * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
55882
+ * clears it.
55883
+ */
55884
+ var ConfigSchemaPassthrough$3 = custom();
55885
+ var IntegrationInstanceIdSchema = string().min(1);
55886
+ var IntegrationInstanceDescriptionSchema = object({
55887
+ /** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
55888
+ instanceMode: _enum(["single", "multiple"]),
55889
+ /** What the instance is, for the UI (`destination`, `relay`, `account`). */
55890
+ kind: string(),
55891
+ /** True when `testInstance` really probes the remote. */
55892
+ canTest: boolean()
55893
+ });
55894
+ var IntegrationInstanceStatusSchema = object({
55895
+ state: _enum([
55896
+ "ok",
55897
+ "off",
55898
+ "error",
55899
+ "unknown"
55900
+ ]),
55901
+ detail: string().optional()
55902
+ });
55903
+ var IntegrationInstanceSchema = object({
55904
+ id: IntegrationInstanceIdSchema,
55905
+ name: string(),
55906
+ /** The addon's own rule (an URL is present, a client id is present...). */
55907
+ configured: boolean(),
55908
+ /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
55909
+ formValues: record(string(), unknown()),
55910
+ status: IntegrationInstanceStatusSchema.optional()
55911
+ });
55912
+ var IntegrationInstancePatchSchema = object({
55913
+ id: IntegrationInstanceIdSchema,
55914
+ name: string().min(1).optional(),
55915
+ /** Omitted / '' / redaction token keeps a secret; `{ $secretClear: true }` clears it. */
55916
+ values: record(string(), unknown())
55917
+ });
55918
+ method(_void(), IntegrationInstanceDescriptionSchema), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3), method(_void(), array(IntegrationInstanceSchema).readonly()), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable()), method(object({
55919
+ id: IntegrationInstanceIdSchema,
55920
+ name: string().min(1)
55921
+ }), object({ id: string() }), { kind: "mutation" }), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), { kind: "mutation" }), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), { kind: "mutation" }), optionalMethod(object({
55922
+ id: IntegrationInstanceIdSchema.nullable(),
55923
+ values: record(string(), unknown())
55924
+ }), TestConnectionResultSchema$1, { kind: "mutation" });
55925
+ /**
55700
55926
  * Two-way audio / talk-back cap. Clients open a WebRTC audio-only
55701
55927
  * session (server-initiated offer pattern, identical to the main
55702
55928
  * video WebRTC flow) and the browser captures the mic; the server
@@ -76690,6 +76916,40 @@ var LoggingSettingsStateSchema = object({
76690
76916
  opsLogRetentionDays: number().int().min(0),
76691
76917
  persisted: boolean()
76692
76918
  });
76919
+ /**
76920
+ * Debug WATCH — a short, per-device window over `debug.<channel>` lines.
76921
+ *
76922
+ * `start` opens (or re-arms) a 10 minute window; `rearm` extends a LIVE one
76923
+ * and never revives a lapsed one; `stop` closes it; `list` only reads. No
76924
+ * chain of re-arms outlives 60 minutes from the first start. The hub holds the
76925
+ * watches in memory and pushes the interest to every node, so a watch lapses
76926
+ * by itself and survives nothing — a restart is a stop.
76927
+ */
76928
+ var DebugWatchInputSchema = object({
76929
+ action: _enum([
76930
+ "list",
76931
+ "start",
76932
+ "rearm",
76933
+ "stop"
76934
+ ]),
76935
+ /** Full channel names, `debug.<channel>`, each declared with `kind: 'debug'`. */
76936
+ channels: array(string().min(1)).readonly().default([]),
76937
+ /** Numeric device ids. Required (non-empty) for `start`. */
76938
+ deviceIds: array(number().int()).readonly().default([])
76939
+ });
76940
+ var DebugWatchStateSchema = object({
76941
+ channel: string(),
76942
+ deviceIds: array(number().int()).readonly(),
76943
+ startedAtMs: number(),
76944
+ untilMs: number(),
76945
+ capAtMs: number()
76946
+ });
76947
+ var DebugWatchResultSchema = object({
76948
+ nowMs: number(),
76949
+ watches: array(DebugWatchStateSchema).readonly(),
76950
+ /** Channels a `rearm` found already lapsed: nothing was revived. */
76951
+ lapsed: array(string()).readonly()
76952
+ });
76693
76953
  method(_void(), SystemInfoSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
76694
76954
  kind: "mutation",
76695
76955
  auth: "admin"
@@ -76705,6 +76965,9 @@ method(_void(), SystemInfoSchema), method(_void(), HealthStatusSchema), method(_
76705
76965
  }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(_void(), array(ReportedFailureContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
76706
76966
  kind: "mutation",
76707
76967
  auth: "admin"
76968
+ }), method(DebugWatchInputSchema, DebugWatchResultSchema, {
76969
+ kind: "mutation",
76970
+ auth: "admin"
76708
76971
  });
76709
76972
  /**
76710
76973
  * Tamper / case-open detection sensor. Drives Home Assistant
@@ -83814,6 +84077,13 @@ Object.freeze({
83814
84077
  access: "view",
83815
84078
  role: "admin"
83816
84079
  },
84080
+ "deviceManager.dismissDiscoveryProposal": {
84081
+ capName: "device-manager",
84082
+ capScope: "system",
84083
+ addonId: null,
84084
+ access: "create",
84085
+ role: "admin"
84086
+ },
83817
84087
  "deviceManager.enable": {
83818
84088
  capName: "device-manager",
83819
84089
  capScope: "system",
@@ -83996,6 +84266,13 @@ Object.freeze({
83996
84266
  access: "view",
83997
84267
  role: "viewer"
83998
84268
  },
84269
+ "deviceManager.listDiscoveryProposals": {
84270
+ capName: "device-manager",
84271
+ capScope: "system",
84272
+ addonId: null,
84273
+ access: "view",
84274
+ role: "admin"
84275
+ },
83999
84276
  "deviceManager.listLocationDescriptions": {
84000
84277
  capName: "device-manager",
84001
84278
  capScope: "system",
@@ -84150,6 +84427,13 @@ Object.freeze({
84150
84427
  access: "create",
84151
84428
  role: "admin"
84152
84429
  },
84430
+ "deviceManager.reportDiscoveryObservations": {
84431
+ capName: "device-manager",
84432
+ capScope: "system",
84433
+ addonId: null,
84434
+ access: "create",
84435
+ role: "admin"
84436
+ },
84153
84437
  "deviceManager.runDeviceAction": {
84154
84438
  capName: "device-manager",
84155
84439
  capScope: "system",
@@ -84157,6 +84441,13 @@ Object.freeze({
84157
84441
  access: "create",
84158
84442
  role: "admin"
84159
84443
  },
84444
+ "deviceManager.runDiscoverySweep": {
84445
+ capName: "device-manager",
84446
+ capScope: "system",
84447
+ addonId: null,
84448
+ access: "create",
84449
+ role: "admin"
84450
+ },
84160
84451
  "deviceManager.setChildLayout": {
84161
84452
  capName: "device-manager",
84162
84453
  capScope: "system",
@@ -85088,6 +85379,62 @@ Object.freeze({
85088
85379
  access: "create",
85089
85380
  role: "admin"
85090
85381
  },
85382
+ "integrationInstances.createInstance": {
85383
+ capName: "integration-instances",
85384
+ capScope: "system",
85385
+ addonId: null,
85386
+ access: "create",
85387
+ role: "admin"
85388
+ },
85389
+ "integrationInstances.deleteInstance": {
85390
+ capName: "integration-instances",
85391
+ capScope: "system",
85392
+ addonId: null,
85393
+ access: "delete",
85394
+ role: "admin"
85395
+ },
85396
+ "integrationInstances.describe": {
85397
+ capName: "integration-instances",
85398
+ capScope: "system",
85399
+ addonId: null,
85400
+ access: "view",
85401
+ role: "admin"
85402
+ },
85403
+ "integrationInstances.getInstance": {
85404
+ capName: "integration-instances",
85405
+ capScope: "system",
85406
+ addonId: null,
85407
+ access: "view",
85408
+ role: "admin"
85409
+ },
85410
+ "integrationInstances.instanceFormSchema": {
85411
+ capName: "integration-instances",
85412
+ capScope: "system",
85413
+ addonId: null,
85414
+ access: "view",
85415
+ role: "admin"
85416
+ },
85417
+ "integrationInstances.listInstances": {
85418
+ capName: "integration-instances",
85419
+ capScope: "system",
85420
+ addonId: null,
85421
+ access: "view",
85422
+ role: "admin"
85423
+ },
85424
+ "integrationInstances.testInstance": {
85425
+ capName: "integration-instances",
85426
+ capScope: "system",
85427
+ addonId: null,
85428
+ access: "create",
85429
+ role: "admin"
85430
+ },
85431
+ "integrationInstances.updateInstance": {
85432
+ capName: "integration-instances",
85433
+ capScope: "system",
85434
+ addonId: null,
85435
+ access: "create",
85436
+ role: "admin"
85437
+ },
85091
85438
  "integrations.create": {
85092
85439
  capName: "integrations",
85093
85440
  capScope: "system",
@@ -90744,6 +91091,13 @@ Object.freeze({
90744
91091
  access: "create",
90745
91092
  role: "viewer"
90746
91093
  },
91094
+ "system.debugWatch": {
91095
+ capName: "system",
91096
+ capScope: "system",
91097
+ addonId: null,
91098
+ access: "create",
91099
+ role: "admin"
91100
+ },
90747
91101
  "system.detectSiteLocation": {
90748
91102
  capName: "system",
90749
91103
  capScope: "system",
@@ -94673,6 +95027,11 @@ Object.freeze({
94673
95027
  form: "single",
94674
95028
  optional: false
94675
95029
  }],
95030
+ "system.debugWatch": [{
95031
+ name: "deviceIds",
95032
+ form: "array",
95033
+ optional: true
95034
+ }],
94676
95035
  "tamper.getStatus": [{
94677
95036
  name: "deviceId",
94678
95037
  form: "single",
@@ -97157,6 +97516,12 @@ Object.defineProperty(exports, "createEvent", {
97157
97516
  return createEvent;
97158
97517
  }
97159
97518
  });
97519
+ Object.defineProperty(exports, "defineAddonMigrations", {
97520
+ enumerable: true,
97521
+ get: function() {
97522
+ return defineAddonMigrations;
97523
+ }
97524
+ });
97160
97525
  Object.defineProperty(exports, "defineEntryKind", {
97161
97526
  enumerable: true,
97162
97527
  get: function() {
@@ -97361,6 +97726,12 @@ Object.defineProperty(exports, "powerMeterCapability", {
97361
97726
  return powerMeterCapability;
97362
97727
  }
97363
97728
  });
97729
+ Object.defineProperty(exports, "preconditionOf", {
97730
+ enumerable: true,
97731
+ get: function() {
97732
+ return preconditionOf;
97733
+ }
97734
+ });
97364
97735
  Object.defineProperty(exports, "prepareNotification", {
97365
97736
  enumerable: true,
97366
97737
  get: function() {