@camstack/addon-matter-broker 0.2.158 → 0.2.160

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.
package/dist/addon.mjs CHANGED
@@ -5993,7 +5993,7 @@ var ZodIssueCode = {
5993
5993
  var ZodFirstPartyTypeKind;
5994
5994
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5995
5995
  //#endregion
5996
- //#region ../types/dist/sleep-DEZaLbiu.mjs
5996
+ //#region ../types/dist/sleep-BX_MqzUe.mjs
5997
5997
  /**
5998
5998
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5999
5999
  * window to float samples (D455).
@@ -7622,6 +7622,28 @@ var SECRET_RETRY_MAX_MS = 5 * 6e4;
7622
7622
  /** Coalesces a burst of `keychain.changed` events into one re-resolve. */
7623
7623
  var SECRET_CHANGE_DEBOUNCE_MS = 250;
7624
7624
  /**
7625
+ * Pair a catalogue with the function that builds its ports FROM THE MIGRATION API. The one
7626
+ * place a catalogue is bound: a migration reaches nothing a ports binder cannot reach
7627
+ * through `api` (guard `check-migration-ports-surface`).
7628
+ */
7629
+ function migrationSet(catalogue, bindPorts) {
7630
+ return { bind: (api) => defineAddonMigrations(catalogue, bindPorts(api)) };
7631
+ }
7632
+ /** A mixed array is a mistake: the other kind would be dropped without a word. */
7633
+ function refuseMixed(form, kept, dropped) {
7634
+ if (dropped > 0) throw new Error(`runMigrations(${form}): ${dropped} of ${kept + dropped} entries are of the other kind and would be dropped; pass one kind per call`);
7635
+ }
7636
+ function catalogueOf(list) {
7637
+ const kept = list.filter((entry) => !("execute" in entry));
7638
+ refuseMixed("catalogue, (api) => ports", kept.length, list.length - kept.length);
7639
+ return kept;
7640
+ }
7641
+ function boundOf(list) {
7642
+ const kept = list.filter((entry) => "execute" in entry);
7643
+ refuseMixed("bound migrations", kept.length, list.length - kept.length);
7644
+ return kept;
7645
+ }
7646
+ /**
7625
7647
  * Base class for CamStack addons. Eliminates settings boilerplate:
7626
7648
  *
7627
7649
  * - Typed `config` property with automatic resolution from store + defaults
@@ -7788,23 +7810,21 @@ var BaseAddon = class {
7788
7810
  await this.loadScopedSchemas();
7789
7811
  return normalized;
7790
7812
  }
7813
+ async runMigrations(first, second, third) {
7814
+ if (typeof second === "function") return this.runMigrationSets([migrationSet(catalogueOf(first), second)], third);
7815
+ return this.runBound(boundOf(first), second);
7816
+ }
7791
7817
  /**
7792
- * Run this addon's migration catalogue (D987 M3) through the hub's `migrations`
7793
- * engine. Call it at the point of the boot where the data must already have the new
7794
- * shape — `onInitialize`, or `onRestoreDevices` for a migration that needs the
7795
- * restored devices — with the catalogue bound to the addon's ports
7796
- * (`defineAddonMigrations(ADDON_MIGRATIONS, ports)`).
7797
- *
7798
- * An engine that cannot be reached or refuses the catalogue is logged and runs
7799
- * nothing; the boot continues. A migration that FAILED (or was blocked by one)
7800
- * and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
7801
- * serve data whose shape it cannot trust; `tolerate` migrations never do.
7802
- *
7803
- * ALWAYS pass the whole catalogue; a phase that must run only some of it passes
7804
- * `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
7805
- * omitted applied ids "newer").
7818
+ * Several catalogues of ONE owner in one declaration (the engine replaces an owner's
7819
+ * catalogue on every declare, so two calls would each judge the other's ids "newer").
7820
+ * Build each entry with `migrationSet(catalogue, (api) => ports)`.
7806
7821
  */
7807
- async runMigrations(migrations, options) {
7822
+ async runMigrationSets(sets, options) {
7823
+ const api = this.ctx.migrationApi;
7824
+ if (api === void 0) throw new Error(`${this.ctx.id}: the migration API is not available on this context`);
7825
+ return this.runBound(sets.flatMap((set) => set.bind(api)), options);
7826
+ }
7827
+ async runBound(migrations, options) {
7808
7828
  const api = this.ctx.api.migrations;
7809
7829
  const report = await runAddonMigrations({
7810
7830
  declareOwner: (input) => api.declareOwner.mutate(input),
@@ -13515,7 +13535,7 @@ var RecordIntegrationInstances = class {
13515
13535
  return record === null ? null : this.viewOf(record);
13516
13536
  };
13517
13537
  /** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
13518
- createInstance = async ({ id, name, kind }) => {
13538
+ createInstance = async ({ id, name, kind, values }) => {
13519
13539
  const minted = id ?? this.hooks.mintId?.({
13520
13540
  name,
13521
13541
  ...kind !== void 0 ? { kind } : {}
@@ -13525,10 +13545,20 @@ var RecordIntegrationInstances = class {
13525
13545
  id: minted,
13526
13546
  data: this.hooks.initialBody(name, kind)
13527
13547
  }, { ifRev: 0 });
13548
+ const patch = this.patchOf(values ?? {});
13549
+ if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
13528
13550
  await this.hooks.onChanged?.(minted, "created");
13529
13551
  return { id: minted };
13530
13552
  };
13531
13553
  updateInstance = async ({ id, name, values }) => {
13554
+ const patch = this.patchOf(values);
13555
+ if (name !== void 0) patch[this.nameField] = name;
13556
+ await this.handle.patch(id, patch);
13557
+ await this.hooks.onChanged?.(id, "updated");
13558
+ return { ok: true };
13559
+ };
13560
+ /** The record patch a form `values` object means: secrets kept, cleared or replaced. */
13561
+ patchOf(values) {
13532
13562
  const patch = {};
13533
13563
  for (const [key, value] of Object.entries(values)) {
13534
13564
  if (value === void 0) continue;
@@ -13539,11 +13569,8 @@ var RecordIntegrationInstances = class {
13539
13569
  else if (isSecretClear(value)) patch[key] = null;
13540
13570
  else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
13541
13571
  }
13542
- if (name !== void 0) patch[this.nameField] = name;
13543
- await this.handle.patch(id, patch);
13544
- await this.hooks.onChanged?.(id, "updated");
13545
- return { ok: true };
13546
- };
13572
+ return patch;
13573
+ }
13547
13574
  /** The switch lives in the record body; the addon is told so it applies it (D996). */
13548
13575
  setInstanceEnabled = async ({ id, enabled }) => {
13549
13576
  if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
@@ -13580,6 +13607,76 @@ var RecordIntegrationInstances = class {
13580
13607
  };
13581
13608
  }
13582
13609
  };
13610
+ /** No part holds the id a call named. */
13611
+ var UnknownInstanceError = class extends Error {
13612
+ instanceId;
13613
+ constructor(instanceId) {
13614
+ super(`Unknown instance '${instanceId}'`);
13615
+ this.instanceId = instanceId;
13616
+ this.name = "UnknownInstanceError";
13617
+ }
13618
+ };
13619
+ /** A create or a form named no kind, or a kind no part serves. */
13620
+ var UnknownInstanceKindError = class extends Error {
13621
+ kindId;
13622
+ constructor(kindId) {
13623
+ super(kindId === void 0 ? "This provider has several kinds: name one" : `Unknown instance kind '${kindId}'`);
13624
+ this.kindId = kindId;
13625
+ this.name = "UnknownInstanceKindError";
13626
+ }
13627
+ };
13628
+ function composeInstanceKinds(parts, base) {
13629
+ const byKind = (kindId) => {
13630
+ if (kindId === void 0) {
13631
+ const only = parts.length === 1 ? parts[0] : void 0;
13632
+ if (only === void 0) throw new UnknownInstanceKindError(void 0);
13633
+ return only;
13634
+ }
13635
+ const part = parts.find((candidate) => candidate.kind.id === kindId);
13636
+ if (part === void 0) throw new UnknownInstanceKindError(kindId);
13637
+ return part;
13638
+ };
13639
+ const holderOf = async (id) => {
13640
+ for (const part of parts) if (await part.provider.getInstance({ id }) !== null) return part;
13641
+ throw new UnknownInstanceError(id);
13642
+ };
13643
+ const tagged = (part, instance) => ({
13644
+ ...instance,
13645
+ kind: part.kind.id
13646
+ });
13647
+ return {
13648
+ describe: async () => ({
13649
+ ...base,
13650
+ kinds: parts.map((part) => part.kind)
13651
+ }),
13652
+ instanceFormSchema: async ({ id, kind }) => {
13653
+ return (id === null ? byKind(kind) : await holderOf(id)).provider.instanceFormSchema({ id });
13654
+ },
13655
+ listInstances: async () => {
13656
+ return (await Promise.all(parts.map(async (part) => (await part.provider.listInstances()).map((instance) => tagged(part, instance))))).flat();
13657
+ },
13658
+ getInstance: async ({ id }) => {
13659
+ for (const part of parts) {
13660
+ const found = await part.provider.getInstance({ id });
13661
+ if (found !== null) return tagged(part, found);
13662
+ }
13663
+ return null;
13664
+ },
13665
+ createInstance: async (input) => byKind(input.kind).provider.createInstance(input),
13666
+ updateInstance: async (input) => (await holderOf(input.id)).provider.updateInstance(input),
13667
+ setInstanceEnabled: async (input) => (await holderOf(input.id)).provider.setInstanceEnabled(input),
13668
+ deleteInstance: async (input) => (await holderOf(input.id)).provider.deleteInstance(input),
13669
+ testInstance: async (input) => {
13670
+ const test = (input.id === null ? byKind(input.kind) : await holderOf(input.id)).provider.testInstance;
13671
+ if (test === void 0) return {
13672
+ success: false,
13673
+ status: "inconclusive",
13674
+ error: "This kind cannot be tested"
13675
+ };
13676
+ return test(input);
13677
+ }
13678
+ };
13679
+ }
13583
13680
  var EncodeProfileSchema = object({
13584
13681
  video: object({
13585
13682
  codec: _enum([
@@ -15794,6 +15891,39 @@ var UnstampedEventMediaCountSchema = object({
15794
15891
  /** Sum across both, or `null` when either lane could not be counted. */
15795
15892
  total: number().int().nonnegative().nullable()
15796
15893
  }).nullable();
15894
+ /**
15895
+ * Where the stored event media sits, per storage location: how many media rows
15896
+ * and how many bytes carry each location id, how many carry none (legacy,
15897
+ * resolved to the default `eventMedia` location), and how many name a location
15898
+ * that was not asked about (a deleted or never-listed one).
15899
+ *
15900
+ * One covering-index aggregate per asked location plus the NULL one, and one
15901
+ * unfiltered total that scans the covering index: O(rows), not a walk of the
15902
+ * rows themselves. `unlisted` is total minus the ASKED locations minus NULL, so
15903
+ * it is a deleted location only when every location id was asked.
15904
+ * `null` is "could not be measured", never zero.
15905
+ */
15906
+ var MediaLocationCensusInputSchema = object({ locationIds: array(string$2().min(1)).max(50).readonly() });
15907
+ var MediaLocationCensusSchema = object({
15908
+ locations: array(object({
15909
+ locationId: string$2(),
15910
+ rows: number().int().nonnegative(),
15911
+ bytes: number().nonnegative()
15912
+ })).readonly(),
15913
+ unstamped: object({
15914
+ rows: number().int().nonnegative(),
15915
+ bytes: number().nonnegative()
15916
+ }),
15917
+ /** Rows stamped with a location id that was not in the asked list. */
15918
+ unlisted: object({
15919
+ rows: number().int().nonnegative(),
15920
+ bytes: number().nonnegative()
15921
+ }),
15922
+ total: object({
15923
+ rows: number().int().nonnegative(),
15924
+ bytes: number().nonnegative()
15925
+ })
15926
+ });
15797
15927
  var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string$2().min(1) });
15798
15928
  /** The independently selectable logical storage classes — every class
15799
15929
  * `storage.listLocationDeclarations` reports, so an operator never meets a
@@ -46830,52 +46960,7 @@ onBrightnessChanged: { data: object({
46830
46960
  minIntervalMs: 0
46831
46961
  } } }
46832
46962
  };
46833
- /**
46834
- * `broker` — unified pub/sub broker registry, system-scoped collection.
46835
- *
46836
- * The cap models any kind-tagged message broker the user wants to
46837
- * register and that other addons might consume. The first two kinds
46838
- * are MQTT (mosquitto / aedes embedded / cloud bridge) and
46839
- * Home Assistant (HA WebSocket — subscribe_entities + call_service).
46840
- * Future kinds (Zigbee2MQTT bridge, ZHA, KNX, Telegram, …) plug in
46841
- * the same surface.
46842
- *
46843
- * Why one cap, not one cap per kind:
46844
- * - The integrations page wants a single table of "every broker"
46845
- * across kinds; a unified cap drives it without join logic.
46846
- * - Generic admin operations (add / remove / test / status) are
46847
- * identical across kinds — duplicating them per cap was busywork.
46848
- * - The `network-access` cap precedent: same interface, many
46849
- * `providerKind: 'ingress'` implementations (Tailscale, ngrok, …).
46850
- *
46851
- * What stays kind-specific:
46852
- * - `getBrokerConfig` payload (kind decides whether it returns
46853
- * mqtt URL+creds, HA baseUrl+token, …) — typed as
46854
- * `Record<string, unknown>` at the wire; consumers narrow.
46855
- * - `publish` / `subscribe` / `getState` arguments (target / filter /
46856
- * key) — also typed as `Record<string, unknown>` so each kind can
46857
- * evolve its surface (MQTT topic+qos vs HA entity_id+domain)
46858
- * without rebreaking the cap signature.
46859
- * - `add` `settings` payload (different fields per kind, validated by
46860
- * the kind-specific provider).
46861
- *
46862
- * Bidirectionality:
46863
- * - `publish` is RPC — synchronous result (provider-defined).
46864
- * - `subscribe` returns a subscription handle; the actual message
46865
- * stream flows over the typed event-bus as `broker.message` events
46866
- * keyed by `(brokerId, subscriptionId)`. Consumers filter in their
46867
- * event handler. Subscriptions persist across reconnects — the
46868
- * provider re-subscribes upstream after a transport drop.
46869
- *
46870
- * Why event-bus push (not a subscription RPC stream):
46871
- * - Matches the rest of CamStack's D8 contract — events for telemetry,
46872
- * RPC for loss-is-a-bug. Broker messages are telemetry (a single
46873
- * drop is recoverable via `getState({key})`).
46874
- * - Frees `broker.subscribe` from holding a long-lived RPC channel.
46875
- * - Cross-process: events already route through `$event-bus`; no
46876
- * per-broker wire-shaping needed.
46877
- */
46878
- var BrokerStatusEnum = _enum([
46963
+ _enum([
46879
46964
  "connected",
46880
46965
  "disconnected",
46881
46966
  "connecting",
@@ -46883,141 +46968,12 @@ var BrokerStatusEnum = _enum([
46883
46968
  "unreachable",
46884
46969
  "error"
46885
46970
  ]);
46886
- var BrokerInfoSchema = object({
46887
- /** Stable broker id. Persisted; survives addon restarts. */
46888
- id: string$2(),
46889
- /** Addon id of the provider that OWNS this broker.
46890
- *
46891
- * The `broker` cap is a system-scoped collection: several addons
46892
- * register a `broker` provider, each owning a DISJOINT set of
46893
- * brokers (mqtt-broker owns `mqtt_*`, provider-homeassistant owns
46894
- * `ha_*`). A broker therefore belongs to exactly one addon — like a
46895
- * device belongs to one integration. The admin UI threads this id
46896
- * back as the `{ addonId }` system-collection selector on every
46897
- * id-keyed call (`get` / `getSettings` / `setSettings` / `remove` /
46898
- * `testConnection`), so the call routes to the OWNING provider
46899
- * instead of defaulting to the first-registered one. */
46900
- addonId: string$2(),
46901
- /** Human-readable name (operator-chosen at add-time). */
46902
- name: string$2(),
46903
- /** Provider-defined kind tag — `mqtt` / `home-assistant` / future. */
46904
- kind: string$2(),
46905
- status: BrokerStatusEnum,
46906
- /** Free-form provider-specific info (HA version, MQTT broker
46907
- * flavour, latency, mTLS active, …). */
46908
- info: record(string$2(), unknown()),
46909
- /** Ms epoch of the last connection probe / status update. */
46910
- lastCheckedAt: number().nullable(),
46911
- /** Last error message, when `status` indicates failure. */
46912
- error: string$2().nullable()
46913
- });
46914
46971
  var RegistryStatusSchema = object({
46915
46972
  brokerCount: number().int().nonnegative(),
46916
46973
  connectedCount: number().int().nonnegative()
46917
46974
  });
46918
- /**
46919
- * One entry per `broker` provider — which addon provides which broker
46920
- * kind(s). The unified create picker reads this to know, for each
46921
- * (addonId, kind) pair the operator can pick, where to route the
46922
- * follow-up `getSettingsSchema` / `testSettings` / `add` calls (via the
46923
- * `{ addonId }` selector). A provider only advertises a kind here when
46924
- * it implements `getSettingsSchema` for that kind (so the picker never
46925
- * offers a kind whose creation form can't be rendered).
46926
- */
46927
- var BrokerProviderInfoSchema = object({
46928
- /** Addon id of the `broker` provider this entry describes. */
46929
- addonId: string$2(),
46930
- /** Broker kinds this provider can create, with a display label. */
46931
- kinds: array(object({
46932
- kind: string$2(),
46933
- label: string$2(),
46934
- /**
46935
- * Relative hub path to start an interactive account link (OAuth popup).
46936
- * May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
46937
- */
46938
- interactiveConnectPath: string$2().min(1).optional(),
46939
- /** Relative hub path to disconnect the linked account (same popup pattern). */
46940
- interactiveDisconnectPath: string$2().min(1).optional(),
46941
- /**
46942
- * The kind signs in through a server-held interactive session instead of
46943
- * (or before) typed credentials. `basePath` is a hub-relative route set
46944
- * served by the owning addon (`/addon/<id>/...`) speaking the sign-in
46945
- * contract: `POST {basePath}/start` (body: the form's settings) answers
46946
- * `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
46947
- * `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
46948
- * 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
46949
- * (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
46950
- * existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
46951
- * (`{ sessionId }`) drops the session. The dialog that edits a broker
46952
- * offers it as the default path; the fields the session fills are
46953
- * marked `filledBySignIn` in the kind's settings schema.
46954
- *
46955
- * `methods` lists the ways the session can sign in, recommended first:
46956
- * `'device-code'` adds `POST {basePath}/code/start` (body: the form's
46957
- * settings) answering `{ sessionId, userCode, verificationUrl,
46958
- * expiresAt }` — the operator types `userCode` at `verificationUrl` on
46959
- * any device, and the same `status` / `commit` / `cancel` routes follow
46960
- * (status then carries `method`, `userCode`, `verificationUrl`,
46961
- * `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
46962
- * `'proxy'` is the `{basePath}/start` flow above. Absent means
46963
- * `['proxy']`.
46964
- */
46965
- signIn: object({
46966
- basePath: string$2().min(1),
46967
- methods: array(_enum(["device-code", "proxy"])).optional()
46968
- }).optional()
46969
- }))
46970
- });
46971
- var ListInputSchema = object({
46972
- /** Optional kind filter — `list({kind:'home-assistant'})` returns
46973
- * only HA brokers. Omit to list every broker. */
46974
- kind: string$2().optional() });
46975
46975
  var GetInputSchema = object({ id: string$2() });
46976
- var AddInputSchema = object({
46977
- kind: string$2().min(1),
46978
- name: string$2().min(1),
46979
- /**
46980
- * ADOPT an existing id instead of minting a new one.
46981
- *
46982
- * Written the day this cost an outage. A broker's id is not a detail: HA
46983
- * devices carry it inside their `stableId` (`ha:ha_004:dev:…`), so a broker
46984
- * lost from config and re-added as `ha_001` leaves every one of its devices
46985
- * bound to a broker that no longer exists. Re-entering the password under the
46986
- * ORIGINAL id turns a multi-step device migration back into re-entering a
46987
- * password.
46988
- *
46989
- * A provider MUST refuse an id that is already in use — adopting a live
46990
- * broker's id would silently take it over.
46991
- */
46992
- id: string$2().min(1).optional(),
46993
- /** Kind-specific settings (e.g. MQTT `{url,username,password}` or HA
46994
- * `{baseUrl,accessToken}`). Validated by the kind-specific provider
46995
- * branch on receipt — invalid shape rejects the add. */
46996
- settings: record(string$2(), unknown())
46997
- });
46998
- var AddResultSchema = object({ id: string$2() });
46999
- var RemoveInputSchema = object({ id: string$2() });
47000
- var TestConnectionResultSchema = discriminatedUnion("ok", [object({
47001
- ok: literal(true),
47002
- latencyMs: number().nonnegative()
47003
- }), object({
47004
- ok: literal(false),
47005
- error: string$2()
47006
- })]);
47007
46976
  var SettingsRecordSchema$1 = record(string$2(), unknown());
47008
- var SettingsSchemaInputSchema = object({ kind: string$2() });
47009
- var TestSettingsInputSchema = object({
47010
- kind: string$2(),
47011
- settings: SettingsRecordSchema$1
47012
- });
47013
- var TestSettingsResultSchema = discriminatedUnion("ok", [object({
47014
- ok: literal(true),
47015
- latencyMs: number().nonnegative().optional()
47016
- }).strict(), object({
47017
- ok: literal(false),
47018
- error: string$2()
47019
- })]);
47020
- var SettingsSchemaResultSchema = unknown().nullable();
47021
46977
  var PublishInputSchema = object({
47022
46978
  brokerId: string$2(),
47023
46979
  /** Kind-specific routing target.
@@ -47061,51 +47017,12 @@ var brokerCapability = {
47061
47017
  kind: "poll"
47062
47018
  },
47063
47019
  methods: {
47064
- list: method(ListInputSchema, array(BrokerInfoSchema)),
47065
- get: method(GetInputSchema, BrokerInfoSchema.nullable()),
47066
- /** Enumerate which addon provides which broker kind(s) for the
47067
- * unified create picker. The auto-mount fans this array across
47068
- * every registered `broker` provider (array-output method), so the
47069
- * picker sees every kind from every provider in one call. */
47070
- listProviders: method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }),
47071
- add: method(AddInputSchema, AddResultSchema, {
47072
- kind: "mutation",
47073
- auth: "admin"
47074
- }),
47075
- remove: method(RemoveInputSchema, _void(), {
47076
- kind: "mutation",
47077
- auth: "admin"
47078
- }),
47079
- testConnection: method(GetInputSchema, TestConnectionResultSchema, {
47080
- kind: "mutation",
47081
- auth: "admin"
47082
- }),
47083
- /** Read the persisted settings record for a broker (kind-specific
47084
- * shape). Admin-only — settings may contain secrets. Returns `null`
47085
- * when the broker id is unknown to the provider (the collection
47086
- * fallback may route a foreign id to the first provider). */
47087
- getSettings: method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }),
47088
- /** Overwrite the persisted settings record. The kind-specific
47089
- * provider validates the shape and applies the change (reconnects
47090
- * if credentials changed). */
47091
- setSettings: method(object({
47092
- id: string$2(),
47093
- settings: SettingsRecordSchema$1
47094
- }), _void(), {
47095
- kind: "mutation",
47096
- auth: "admin"
47097
- }),
47098
47020
  /** Returns the kind-specific connection config the consumer needs
47099
47021
  * to open its own client (MQTT pattern: `{url, username, password,
47100
47022
  * clientIdPrefix}`). HA providers MAY return the auth envelope
47101
47023
  * but typical HA consumers use `publish` / `subscribe` instead.
47102
47024
  * Returns `null` when the broker id is unknown to the provider. */
47103
47025
  getBrokerConfig: method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }),
47104
- getSettingsSchema: method(SettingsSchemaInputSchema, SettingsSchemaResultSchema, { auth: "admin" }),
47105
- testSettings: method(TestSettingsInputSchema, TestSettingsResultSchema, {
47106
- kind: "mutation",
47107
- auth: "admin"
47108
- }),
47109
47026
  publish: method(PublishInputSchema, unknown(), {
47110
47027
  kind: "mutation",
47111
47028
  auth: "admin"
@@ -51952,7 +51869,14 @@ var DiscoveryCandidateSchema = object({
51952
51869
  /** `stableId` of the gateway candidate this device was found BEHIND. */
51953
51870
  parentStableId: string$2().optional(),
51954
51871
  /** `false` = shown for context but not pickable. Default true. */
51955
- selectable: boolean().optional()
51872
+ selectable: boolean().optional(),
51873
+ /**
51874
+ * Keys of the provider's creation form (`getChildCreationSchema`) this candidate
51875
+ * cannot be adopted without — a key for an encrypted board, a pairing code. The
51876
+ * admin asks for exactly these, on the creation form prefilled with
51877
+ * `prefilledConfig`, before it adopts. Absent or empty = adopt as it is.
51878
+ */
51879
+ needsParams: array(string$2()).optional()
51956
51880
  });
51957
51881
  /**
51958
51882
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -52079,8 +52003,14 @@ var deviceProviderCapability = {
52079
52003
  * provider does not support manual creation. Lets the Add-Device dialog
52080
52004
  * pick the right type instead of assuming Camera.
52081
52005
  */
52082
- getManualCreationType: method(object({}), object({ deviceType: _enum(DeviceType).nullable() })),
52083
- adoptDiscoveredDevice: method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
52006
+ getManualCreationType: method(object({
52007
+ /** The integration asked about; a provider whose integrations differ answers per one. */
52008
+ integrationId: string$2().optional() }), object({ deviceType: _enum(DeviceType).nullable() })),
52009
+ adoptDiscoveredDevice: method(object({
52010
+ candidate: DiscoveryCandidateSchema,
52011
+ /** The integration the operator adopts into; a provider with several instances needs it. */
52012
+ integrationId: string$2().optional()
52013
+ }), DeviceSummarySchema, {
52084
52014
  kind: "mutation",
52085
52015
  auth: "admin"
52086
52016
  }),
@@ -52091,10 +52021,16 @@ var deviceProviderCapability = {
52091
52021
  * devices of that type. The output is a `ConfigUISchema` — the
52092
52022
  * router type-asserts it at the boundary.
52093
52023
  */
52094
- getChildCreationSchema: method(object({ type: _enum(DeviceType) }), CreationSchemaOutputSchema),
52024
+ getChildCreationSchema: method(object({
52025
+ type: _enum(DeviceType),
52026
+ /** The integration asked about; a provider whose integrations differ answers per one. */
52027
+ integrationId: string$2().optional()
52028
+ }), CreationSchemaOutputSchema),
52095
52029
  createDevice: method(object({
52096
52030
  type: _enum(DeviceType),
52097
- config: record(string$2(), unknown())
52031
+ config: record(string$2(), unknown()),
52032
+ /** The integration the operator adds the device to; a provider with several instances needs it. */
52033
+ integrationId: string$2().optional()
52098
52034
  }), DeviceSummarySchema, {
52099
52035
  kind: "mutation",
52100
52036
  auth: "admin"
@@ -52819,7 +52755,10 @@ method(object({
52819
52755
  auth: "admin"
52820
52756
  }), method(object({
52821
52757
  addonId: string$2(),
52822
- type: _enum(DeviceType)
52758
+ type: _enum(DeviceType),
52759
+ /** The integration the form is for; a provider whose integrations differ (a hub
52760
+ * integration takes no manual add) answers per integration. Absent = the addon's own. */
52761
+ integrationId: string$2().optional()
52823
52762
  }), unknown().nullable()), method(object({
52824
52763
  addonId: string$2(),
52825
52764
  type: _enum(DeviceType),
@@ -52903,7 +52842,11 @@ method(object({
52903
52842
  kind: "mutation",
52904
52843
  auth: "admin",
52905
52844
  timeoutMs: 12e4
52906
- }), method(object({ addonId: string$2() }), object({ deviceType: _enum(DeviceType).nullable() }), { auth: "admin" }), method(object({ addonId: string$2() }), unknown(), { auth: "admin" }), method(object({
52845
+ }), method(object({
52846
+ addonId: string$2(),
52847
+ /** The integration the Add-Device dialog is open on; absent = the addon's own. */
52848
+ integrationId: string$2().optional()
52849
+ }), object({ deviceType: _enum(DeviceType).nullable() }), { auth: "admin" }), method(object({ addonId: string$2() }), unknown(), { auth: "admin" }), method(object({
52907
52850
  deviceId: number(),
52908
52851
  key: string$2(),
52909
52852
  value: unknown()
@@ -56183,7 +56126,7 @@ var TestConnectionStatusEnum = _enum([
56183
56126
  "inconclusive",
56184
56127
  "unsupported"
56185
56128
  ]);
56186
- var TestConnectionResultSchema$1 = object({
56129
+ var TestConnectionResultSchema = object({
56187
56130
  /** True ONLY for `validated`. Never true for a test that did not run. */
56188
56131
  success: boolean(),
56189
56132
  error: string$2().optional(),
@@ -56228,7 +56171,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string$2
56228
56171
  }), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
56229
56172
  addonId: string$2(),
56230
56173
  settings: record(string$2(), unknown())
56231
- }), TestConnectionResultSchema$1, {
56174
+ }), TestConnectionResultSchema, {
56232
56175
  kind: "mutation",
56233
56176
  auth: "admin"
56234
56177
  });
@@ -56255,6 +56198,14 @@ var ConfigSchemaPassthrough$3 = custom();
56255
56198
  var IntegrationInstanceIdSchema = string$2().min(1);
56256
56199
  var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
56257
56200
  /**
56201
+ * What the addon's instances ARE for the Integrations page (planning#416). `devices`: each
56202
+ * instance is an integration that brings devices (the registry owns its on/off switch);
56203
+ * `services`: a connection configured from the addon's own function page, never listed on
56204
+ * Integrations.
56205
+ * It is the same vocabulary as the manifest's `integrationRole`, which stays as an override.
56206
+ */
56207
+ var IntegrationInstanceRoleSchema = _enum(["devices", "services"]);
56208
+ /**
56258
56209
  * One kind of instance a provider can create (an adapter, an account type). The sign-in and
56259
56210
  * connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
56260
56211
  * hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
@@ -56265,6 +56216,22 @@ var IntegrationInstanceKindSchema = object({
56265
56216
  /** Stable id of the kind, sent back as `createInstance.kind`. */
56266
56217
  id: string$2().min(1),
56267
56218
  label: string$2().min(1),
56219
+ /**
56220
+ * The function capability this kind is configured for (`broker`, `smtp-provider`,
56221
+ * `device-provider`, ...). A page section is the kinds whose `functionCap` it names (D1013); a
56222
+ * provider with more than one section must name it on every kind.
56223
+ */
56224
+ functionCap: string$2().min(1),
56225
+ /**
56226
+ * For `functionCap: 'broker'`: the registry kind of the broker an instance of this kind is
56227
+ * (`matter`, `home-assistant`, ...). A caller that needs a broker of one kind (a link field)
56228
+ * finds the instances whose kind names it; absent takes the kind `id`.
56229
+ */
56230
+ brokerKind: string$2().min(1).optional(),
56231
+ /** What instances of this kind are for the Integrations page; absent takes the provider's `role`. */
56232
+ role: IntegrationInstanceRoleSchema.optional(),
56233
+ /** `testInstance` really probes the remote for this kind; absent takes the provider's `canTest`. */
56234
+ canTest: boolean().optional(),
56268
56235
  /** The kind signs in through a server-held interactive session. */
56269
56236
  signIn: object({
56270
56237
  basePath: string$2().min(1),
@@ -56277,11 +56244,7 @@ var IntegrationInstanceKindSchema = object({
56277
56244
  });
56278
56245
  var IntegrationInstanceDescriptionSchema = object({
56279
56246
  /** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
56280
- role: _enum([
56281
- "devices",
56282
- "services",
56283
- "both"
56284
- ]),
56247
+ role: IntegrationInstanceRoleSchema,
56285
56248
  /** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
56286
56249
  discovery: object({
56287
56250
  mode: _enum([
@@ -56356,7 +56319,11 @@ var integrationInstancesCapability = {
56356
56319
  methods: {
56357
56320
  describe: method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }),
56358
56321
  /** The form; `id: null` is the create form. */
56359
- instanceFormSchema: method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }),
56322
+ instanceFormSchema: method(object({
56323
+ id: IntegrationInstanceIdSchema.nullable(),
56324
+ /** The kind of the create form (`id: null`) of a provider with several. */
56325
+ kind: string$2().min(1).optional()
56326
+ }), ConfigSchemaPassthrough$3, { auth: "admin" }),
56360
56327
  listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
56361
56328
  getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
56362
56329
  /**
@@ -56366,7 +56333,9 @@ var integrationInstancesCapability = {
56366
56333
  createInstance: method(object({
56367
56334
  id: IntegrationInstanceIdSchema.optional(),
56368
56335
  name: string$2().min(1),
56369
- kind: string$2().min(1).optional()
56336
+ kind: string$2().min(1).optional(),
56337
+ /** Form values written with the record (a link picked in the create dialog). */
56338
+ values: record(string$2(), unknown()).optional()
56370
56339
  }), object({ id: string$2() }), {
56371
56340
  kind: "mutation",
56372
56341
  auth: "admin"
@@ -56393,8 +56362,10 @@ var integrationInstancesCapability = {
56393
56362
  }),
56394
56363
  testInstance: optionalMethod(object({
56395
56364
  id: IntegrationInstanceIdSchema.nullable(),
56396
- values: record(string$2(), unknown())
56397
- }), TestConnectionResultSchema$1, {
56365
+ values: record(string$2(), unknown()),
56366
+ /** The kind to test when `id` is null and the provider has several. */
56367
+ kind: string$2().min(1).optional()
56368
+ }), TestConnectionResultSchema, {
56398
56369
  kind: "mutation",
56399
56370
  auth: "admin"
56400
56371
  })
@@ -59151,6 +59122,380 @@ var SystemMetricsSchema = object({
59151
59122
  gpuMemoryPercent: number().optional()
59152
59123
  });
59153
59124
  method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string$2() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string$2() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema);
59125
+ /**
59126
+ * records - an addon's own facts and documents (D881): ledgers, audit rows,
59127
+ * config documents, mirrors. The third storage API next to `ctx.settings`
59128
+ * (operator intent, D870) and device-history (numbers over time, D850).
59129
+ *
59130
+ * Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
59131
+ * crossed a process and stated by a hub in-process caller (`ctx.records` does
59132
+ * that with the addon id closed over). It is deliberately NOT in any input
59133
+ * schema, so a session can never supply it, and a call with no stamp is refused
59134
+ * `records/no-caller`. A kind is readable and writable only by its owner; other
59135
+ * addons go through the owner's capability.
59136
+ *
59137
+ * Every read is three-way: a row, `null`, or the call THROWS. A failed read is
59138
+ * never `[]`. Secret fields are never on the wire: a read shows the
59139
+ * redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
59140
+ * resolves itself, and a write may carry only a keychain marker bound to the
59141
+ * row's own ref.
59142
+ *
59143
+ * Internal: addons reach it through `ctx.records`, never directly.
59144
+ */
59145
+ var RecordIdSchema = string$2().min(1).max(256);
59146
+ /** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
59147
+ var RecordBodySchema = record(string$2(), unknown());
59148
+ var RecordInputSchema = object({
59149
+ id: RecordIdSchema,
59150
+ /** The device this row belongs to (D865 removal, per-device reads). */
59151
+ deviceId: number().int().optional(),
59152
+ /** The domain clock, ms: observed / created / opened. */
59153
+ at: number().int().optional(),
59154
+ /** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
59155
+ expiresAt: number().int().optional(),
59156
+ data: RecordBodySchema
59157
+ });
59158
+ var RecordViewSchema = object({
59159
+ id: string$2(),
59160
+ deviceId: number().int().nullable(),
59161
+ at: number().nullable(),
59162
+ expiresAt: number().nullable(),
59163
+ rev: number().int(),
59164
+ updatedAt: number(),
59165
+ data: RecordBodySchema
59166
+ });
59167
+ /** Declared index fields only. Anything else does not compile, by design. */
59168
+ var RecordWhereSchema = record(RecordFieldPathSchema, union([
59169
+ string$2(),
59170
+ number(),
59171
+ boolean(),
59172
+ object({ in: array(union([
59173
+ string$2(),
59174
+ number(),
59175
+ boolean()
59176
+ ])).min(1).max(RECORD_BATCH_MAX) }),
59177
+ object({
59178
+ gt: union([string$2(), number()]).optional(),
59179
+ gte: union([string$2(), number()]).optional(),
59180
+ lt: union([string$2(), number()]).optional(),
59181
+ lte: union([string$2(), number()]).optional()
59182
+ }).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
59183
+ ]));
59184
+ var KindShape = { kind: RecordKindNameSchema };
59185
+ var TimeWindowShape = {
59186
+ deviceId: number().int().optional(),
59187
+ atFrom: number().optional(),
59188
+ atTo: number().optional()
59189
+ };
59190
+ var RecordQueryInputSchema = object({
59191
+ ...KindShape,
59192
+ ...TimeWindowShape,
59193
+ where: RecordWhereSchema.optional(),
59194
+ /** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
59195
+ order: _enum(["at", "id"]).optional(),
59196
+ desc: boolean().optional(),
59197
+ limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
59198
+ /** Opaque keyset cursor from a previous answer. There is no offset. */
59199
+ cursor: string$2().min(1).optional()
59200
+ });
59201
+ var RecordQueryResultSchema = object({
59202
+ records: array(RecordViewSchema),
59203
+ nextCursor: string$2().nullable()
59204
+ });
59205
+ var RecordKindStateSchema = _enum([
59206
+ "created",
59207
+ "unchanged",
59208
+ "updated"
59209
+ ]);
59210
+ var DeclareKindResultSchema = object({
59211
+ kindId: number().int(),
59212
+ state: RecordKindStateSchema,
59213
+ createdIndexes: array(string$2())
59214
+ });
59215
+ /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
59216
+ var SecretRefsList = array(string$2());
59217
+ /**
59218
+ * One op of a `transact`: the single-row writes, each on a kind of the caller.
59219
+ * All ops of one call share ONE database file and ONE `transactFamily`.
59220
+ */
59221
+ var RecordTransactOpSchema = discriminatedUnion("op", [
59222
+ object({
59223
+ op: literal("put"),
59224
+ kind: RecordKindNameSchema,
59225
+ record: RecordInputSchema,
59226
+ ifRev: number().int().min(0).optional()
59227
+ }),
59228
+ object({
59229
+ op: literal("patch"),
59230
+ kind: RecordKindNameSchema,
59231
+ id: RecordIdSchema,
59232
+ data: RecordBodySchema,
59233
+ at: number().int().nullable().optional(),
59234
+ expiresAt: number().int().nullable().optional(),
59235
+ ifRev: number().int().min(1).optional()
59236
+ }),
59237
+ object({
59238
+ op: literal("delete"),
59239
+ kind: RecordKindNameSchema,
59240
+ id: RecordIdSchema
59241
+ })
59242
+ ]);
59243
+ var RecordSecretRefsSchema = record(string$2(), record(string$2(), string$2()));
59244
+ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string$2(), DeclareKindResultSchema) }), {
59245
+ kind: "mutation",
59246
+ auth: "admin"
59247
+ }), method(object({
59248
+ ...KindShape,
59249
+ id: RecordIdSchema
59250
+ }), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
59251
+ ...KindShape,
59252
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
59253
+ }), array(RecordViewSchema), { auth: "admin" }), method(object({
59254
+ ...KindShape,
59255
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
59256
+ }), RecordSecretRefsSchema, { auth: "admin" }), method(object({
59257
+ ...KindShape,
59258
+ record: RecordInputSchema,
59259
+ /** Compare-and-set. `0` means "the row must not exist". */
59260
+ ifRev: number().int().min(0).optional()
59261
+ }), object({
59262
+ rev: number().int(),
59263
+ releasedRefs: SecretRefsList
59264
+ }), {
59265
+ kind: "mutation",
59266
+ auth: "admin"
59267
+ }), method(object({
59268
+ ...KindShape,
59269
+ records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
59270
+ }), object({
59271
+ count: number().int(),
59272
+ releasedRefs: SecretRefsList
59273
+ }), {
59274
+ kind: "mutation",
59275
+ auth: "admin"
59276
+ }), method(object({
59277
+ ...KindShape,
59278
+ id: RecordIdSchema,
59279
+ data: RecordBodySchema,
59280
+ at: number().int().nullable().optional(),
59281
+ expiresAt: number().int().nullable().optional(),
59282
+ ifRev: number().int().min(1).optional()
59283
+ }), object({
59284
+ rev: number().int(),
59285
+ releasedRefs: SecretRefsList
59286
+ }), {
59287
+ kind: "mutation",
59288
+ auth: "admin"
59289
+ }), method(object({
59290
+ ...KindShape,
59291
+ id: RecordIdSchema
59292
+ }), object({
59293
+ deleted: boolean(),
59294
+ secretRefs: SecretRefsList
59295
+ }), {
59296
+ kind: "mutation",
59297
+ auth: "admin"
59298
+ }), method(object({
59299
+ ...KindShape,
59300
+ ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
59301
+ }), object({
59302
+ deleted: number().int(),
59303
+ secretRefs: SecretRefsList
59304
+ }), {
59305
+ kind: "mutation",
59306
+ auth: "admin"
59307
+ }), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
59308
+ ...KindShape,
59309
+ ...TimeWindowShape,
59310
+ where: RecordWhereSchema.optional()
59311
+ }), object({ count: number().int() }), { auth: "admin" }), method(object({
59312
+ ...KindShape,
59313
+ deviceId: number().int().optional(),
59314
+ from: number(),
59315
+ to: number(),
59316
+ bucketMs: number().int().positive()
59317
+ }), object({ buckets: array(object({
59318
+ bucketStart: number(),
59319
+ count: number().int()
59320
+ })) }), { auth: "admin" }), method(object({
59321
+ ...KindShape,
59322
+ op: _enum([
59323
+ "sum",
59324
+ "min",
59325
+ "max",
59326
+ "count"
59327
+ ]),
59328
+ field: RecordFieldPathSchema.optional(),
59329
+ groupBy: RecordFieldPathSchema.optional(),
59330
+ where: RecordWhereSchema.optional()
59331
+ }), object({ rows: array(object({
59332
+ group: union([
59333
+ string$2(),
59334
+ number(),
59335
+ _null()
59336
+ ]),
59337
+ value: number().nullable()
59338
+ })) }), { auth: "admin" }), method(object({
59339
+ ...KindShape,
59340
+ deviceId: number().int().optional(),
59341
+ atBefore: number().optional(),
59342
+ where: RecordWhereSchema.optional()
59343
+ }), object({
59344
+ deleted: number().int(),
59345
+ secretRefs: SecretRefsList
59346
+ }), {
59347
+ kind: "mutation",
59348
+ auth: "admin"
59349
+ }), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
59350
+ results: array(object({
59351
+ rev: number().int().nullable(),
59352
+ deleted: boolean()
59353
+ })),
59354
+ releasedRefs: SecretRefsList,
59355
+ secretRefs: SecretRefsList
59356
+ }), {
59357
+ kind: "mutation",
59358
+ auth: "admin"
59359
+ }), method(object({
59360
+ deviceId: number().int(),
59361
+ kinds: array(RecordKindNameSchema).optional()
59362
+ }), object({
59363
+ deleted: record(string$2(), number().int()),
59364
+ secretRefs: SecretRefsList
59365
+ }), {
59366
+ kind: "mutation",
59367
+ auth: "admin"
59368
+ }), method(object(KindShape), object({ id: number().int() }), {
59369
+ kind: "mutation",
59370
+ auth: "admin"
59371
+ });
59372
+ /**
59373
+ * The wire shapes of the migration API, version 1 (planning#447).
59374
+ *
59375
+ * A migration is code that outlives the server it was written against: an addon at
59376
+ * version 1.4 that is brought to 1.9 in one jump still runs its 1.4 catalogue, against
59377
+ * the 1.9 kernel. The only thing that code may call is {@link MigrationApiV1}; this file
59378
+ * is the contract of every method of it, one Zod pair per method.
59379
+ *
59380
+ * APPEND-ONLY. A released method is never removed and never changes meaning: an input
59381
+ * field is never removed or made required, an enum never narrows, an output field is
59382
+ * never removed. A change that cannot be expressed that way is `migration-api.v2`, next
59383
+ * to this file. `scripts/check-migration-api-append-only.ts` holds the line against
59384
+ * `scripts/migration-api-contract.json`, and every `behaviours` id below must have an
59385
+ * `it('<id>'` in `migration-api.contract.spec.ts`.
59386
+ */
59387
+ var Rows = record(string$2(), unknown());
59388
+ var IntegrationId = object({ integrationId: string$2().min(1) });
59389
+ var RecordId = object({ id: string$2().min(1) });
59390
+ var RecordRowSchema = object({
59391
+ id: string$2(),
59392
+ deviceId: number().int().nullable(),
59393
+ at: number().nullable(),
59394
+ expiresAt: number().nullable(),
59395
+ rev: number().int(),
59396
+ updatedAt: number(),
59397
+ data: Rows
59398
+ });
59399
+ var RecordPutShape = {
59400
+ id: string$2().min(1),
59401
+ deviceId: number().int().optional(),
59402
+ at: number().int().optional(),
59403
+ expiresAt: number().int().optional(),
59404
+ data: Rows
59405
+ };
59406
+ var MigrationIntegrationSchema = object({
59407
+ id: string$2(),
59408
+ addonId: string$2(),
59409
+ name: string$2(),
59410
+ info: Rows,
59411
+ originStableId: string$2().optional()
59412
+ });
59413
+ var MigrationDeviceSchema = object({
59414
+ id: number().int(),
59415
+ integrationId: string$2().nullable().optional(),
59416
+ /** The owning device; null or absent for a root. */
59417
+ parentDeviceId: number().int().nullable().optional(),
59418
+ type: string$2().optional(),
59419
+ name: string$2().optional(),
59420
+ stableId: string$2().optional()
59421
+ });
59422
+ /** What the engine says about the migration API it serves: lets an addon notice an older server. */
59423
+ var MigrationApiInfoSchema = object({
59424
+ version: number().int(),
59425
+ methods: array(string$2())
59426
+ });
59427
+ object({
59428
+ message: string$2(),
59429
+ meta: Rows.optional(),
59430
+ /** Grouping tags of the line; a line about a device carries `{ deviceId }`. */
59431
+ tags: object({ deviceId: number().int().optional() }).optional()
59432
+ });
59433
+ object({
59434
+ owner: string$2().min(1),
59435
+ metas: array(MigrationMetaSchema)
59436
+ }), object({
59437
+ bootId: string$2(),
59438
+ snapshot: _enum([
59439
+ "none",
59440
+ "written",
59441
+ "refused"
59442
+ ]),
59443
+ steps: array(MigrationOwnerStepSchema),
59444
+ /** The migration API the engine serves; absent from a server that predates it. */
59445
+ migrationApi: MigrationApiInfoSchema.optional()
59446
+ }), object({ id: string$2().min(1) }), object({
59447
+ proceed: boolean(),
59448
+ state: union([literal("pending"), MigrationStatusSchema]),
59449
+ reason: string$2().nullable()
59450
+ }), object({
59451
+ id: string$2().min(1),
59452
+ result: MigrationOwnerResultSchema
59453
+ }), object({
59454
+ status: MigrationStatusSchema,
59455
+ error: string$2().nullable(),
59456
+ rowsChanged: number().int().nonnegative()
59457
+ }), object({
59458
+ rows: number().int().nonnegative(),
59459
+ secrets: number().int().nonnegative()
59460
+ }), IntegrationId.extend({ patch: Rows }), object({ written: boolean() }), IntegrationId.extend({ keys: array(string$2().min(1)) }), object({
59461
+ present: boolean(),
59462
+ removed: Rows
59463
+ }), RecordRowSchema.nullable(), RecordRowSchema.nullable(), object({
59464
+ ...RecordPutShape,
59465
+ ifRev: number().int().optional()
59466
+ }), object({ rev: number().int() }), object({ records: array(object(RecordPutShape)) }), object({ count: number().int().nonnegative() }), RecordId.extend({
59467
+ partial: Rows,
59468
+ ifRev: number().int().optional()
59469
+ }), object({ rev: number().int() }), object({ deleted: boolean() }), object({
59470
+ deviceId: number().int().optional(),
59471
+ atFrom: number().optional(),
59472
+ atTo: number().optional(),
59473
+ where: RecordWhereSchema.optional(),
59474
+ limit: number().int().min(1).optional(),
59475
+ cursor: string$2().min(1).optional(),
59476
+ resolveSecrets: boolean().optional()
59477
+ }), object({
59478
+ records: array(RecordRowSchema),
59479
+ nextCursor: string$2().nullable()
59480
+ }), object({
59481
+ deviceId: number().int().optional(),
59482
+ atBefore: number().optional(),
59483
+ where: RecordWhereSchema.optional()
59484
+ }), object({ count: number().int().nonnegative() }), object({}), object({ store: Rows }), object({ patch: Rows }), object({}), object({ keys: array(string$2().min(1)) }), object({ removed: array(string$2()) }), object({ refs: array(string$2()) }), object({ values: record(string$2(), string$2().nullable()) }), object({
59485
+ ref: string$2().min(1),
59486
+ value: string$2()
59487
+ }), object({}), object({ refs: array(string$2()) }), object({ deleted: number().int().nonnegative() }), object({}), object({ integrations: array(MigrationIntegrationSchema) }), object({ id: string$2().min(1) }), MigrationIntegrationSchema.nullable(), object({
59488
+ addonId: string$2().min(1),
59489
+ integrationId: string$2().min(1).optional()
59490
+ }), object({ devices: array(MigrationDeviceSchema) }), object({ deviceId: number().int() }), object({
59491
+ id: number().int(),
59492
+ integrationId: string$2().nullable(),
59493
+ /** Whether the device carries any stored config; its content is not offered. */
59494
+ hasConfig: boolean()
59495
+ }).nullable(), object({
59496
+ deviceId: number().int(),
59497
+ integrationId: string$2().min(1)
59498
+ }), object({}), object({}), object({ nodeId: string$2() }), object({}), object({});
59154
59499
  method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { auth: "admin" }), method(object({ owner: string$2().optional() }), object({ pending: array(MigrationPendingEntrySchema) }), { auth: "admin" }), method(object({ id: string$2().min(1) }), MigrationDryRunResultSchema, { auth: "admin" }), method(object({
59155
59500
  id: string$2().min(1),
59156
59501
  error: string$2().min(1)
@@ -59180,7 +59525,9 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
59180
59525
  "written",
59181
59526
  "refused"
59182
59527
  ]),
59183
- steps: array(MigrationOwnerStepSchema)
59528
+ steps: array(MigrationOwnerStepSchema),
59529
+ /** The migration API this engine serves (additive: a server that predates it omits it). */
59530
+ migrationApi: MigrationApiInfoSchema.optional()
59184
59531
  }), {
59185
59532
  kind: "mutation",
59186
59533
  auth: "admin"
@@ -61847,7 +62194,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
61847
62194
  }), method(RelocateMediaInputSchema, object({ jobId: string$2() }), {
61848
62195
  kind: "mutation",
61849
62196
  auth: "admin"
61850
- }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
62197
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(MediaLocationCensusInputSchema, MediaLocationCensusSchema, {
62198
+ kind: "query",
62199
+ auth: "admin"
62200
+ }), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
61851
62201
  kind: "query",
61852
62202
  auth: "admin"
61853
62203
  }), method(object({}), array(RelocateJobSchema).readonly(), {
@@ -64502,7 +64852,7 @@ var BrokerStatusSchema$1 = _enum([
64502
64852
  "unreachable",
64503
64853
  "tls-error"
64504
64854
  ]);
64505
- var BrokerInfoSchema$1 = object({
64855
+ var BrokerInfoSchema = object({
64506
64856
  id: string$2(),
64507
64857
  name: string$2(),
64508
64858
  url: string$2(),
@@ -64565,7 +64915,7 @@ var StatusSchema = object({
64565
64915
  brokerCount: number(),
64566
64916
  embeddedRunning: boolean()
64567
64917
  });
64568
- method(_void(), array(BrokerInfoSchema$1)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
64918
+ method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
64569
64919
  kind: "mutation",
64570
64920
  auth: "admin"
64571
64921
  }), method(IdInputSchema, _void(), {
@@ -71747,253 +72097,6 @@ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, met
71747
72097
  auth: "admin"
71748
72098
  });
71749
72099
  /**
71750
- * records - an addon's own facts and documents (D881): ledgers, audit rows,
71751
- * config documents, mirrors. The third storage API next to `ctx.settings`
71752
- * (operator intent, D870) and device-history (numbers over time, D850).
71753
- *
71754
- * Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
71755
- * crossed a process and stated by a hub in-process caller (`ctx.records` does
71756
- * that with the addon id closed over). It is deliberately NOT in any input
71757
- * schema, so a session can never supply it, and a call with no stamp is refused
71758
- * `records/no-caller`. A kind is readable and writable only by its owner; other
71759
- * addons go through the owner's capability.
71760
- *
71761
- * Every read is three-way: a row, `null`, or the call THROWS. A failed read is
71762
- * never `[]`. Secret fields are never on the wire: a read shows the
71763
- * redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
71764
- * resolves itself, and a write may carry only a keychain marker bound to the
71765
- * row's own ref.
71766
- *
71767
- * Internal: addons reach it through `ctx.records`, never directly.
71768
- */
71769
- var RecordIdSchema = string$2().min(1).max(256);
71770
- /** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
71771
- var RecordBodySchema = record(string$2(), unknown());
71772
- var RecordInputSchema = object({
71773
- id: RecordIdSchema,
71774
- /** The device this row belongs to (D865 removal, per-device reads). */
71775
- deviceId: number().int().optional(),
71776
- /** The domain clock, ms: observed / created / opened. */
71777
- at: number().int().optional(),
71778
- /** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
71779
- expiresAt: number().int().optional(),
71780
- data: RecordBodySchema
71781
- });
71782
- var RecordViewSchema = object({
71783
- id: string$2(),
71784
- deviceId: number().int().nullable(),
71785
- at: number().nullable(),
71786
- expiresAt: number().nullable(),
71787
- rev: number().int(),
71788
- updatedAt: number(),
71789
- data: RecordBodySchema
71790
- });
71791
- /** Declared index fields only. Anything else does not compile, by design. */
71792
- var RecordWhereSchema = record(RecordFieldPathSchema, union([
71793
- string$2(),
71794
- number(),
71795
- boolean(),
71796
- object({ in: array(union([
71797
- string$2(),
71798
- number(),
71799
- boolean()
71800
- ])).min(1).max(RECORD_BATCH_MAX) }),
71801
- object({
71802
- gt: union([string$2(), number()]).optional(),
71803
- gte: union([string$2(), number()]).optional(),
71804
- lt: union([string$2(), number()]).optional(),
71805
- lte: union([string$2(), number()]).optional()
71806
- }).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
71807
- ]));
71808
- var KindShape = { kind: RecordKindNameSchema };
71809
- var TimeWindowShape = {
71810
- deviceId: number().int().optional(),
71811
- atFrom: number().optional(),
71812
- atTo: number().optional()
71813
- };
71814
- var RecordQueryInputSchema = object({
71815
- ...KindShape,
71816
- ...TimeWindowShape,
71817
- where: RecordWhereSchema.optional(),
71818
- /** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
71819
- order: _enum(["at", "id"]).optional(),
71820
- desc: boolean().optional(),
71821
- limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
71822
- /** Opaque keyset cursor from a previous answer. There is no offset. */
71823
- cursor: string$2().min(1).optional()
71824
- });
71825
- var RecordQueryResultSchema = object({
71826
- records: array(RecordViewSchema),
71827
- nextCursor: string$2().nullable()
71828
- });
71829
- var RecordKindStateSchema = _enum([
71830
- "created",
71831
- "unchanged",
71832
- "updated"
71833
- ]);
71834
- var DeclareKindResultSchema = object({
71835
- kindId: number().int(),
71836
- state: RecordKindStateSchema,
71837
- createdIndexes: array(string$2())
71838
- });
71839
- /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
71840
- var SecretRefsList = array(string$2());
71841
- /**
71842
- * One op of a `transact`: the single-row writes, each on a kind of the caller.
71843
- * All ops of one call share ONE database file and ONE `transactFamily`.
71844
- */
71845
- var RecordTransactOpSchema = discriminatedUnion("op", [
71846
- object({
71847
- op: literal("put"),
71848
- kind: RecordKindNameSchema,
71849
- record: RecordInputSchema,
71850
- ifRev: number().int().min(0).optional()
71851
- }),
71852
- object({
71853
- op: literal("patch"),
71854
- kind: RecordKindNameSchema,
71855
- id: RecordIdSchema,
71856
- data: RecordBodySchema,
71857
- at: number().int().nullable().optional(),
71858
- expiresAt: number().int().nullable().optional(),
71859
- ifRev: number().int().min(1).optional()
71860
- }),
71861
- object({
71862
- op: literal("delete"),
71863
- kind: RecordKindNameSchema,
71864
- id: RecordIdSchema
71865
- })
71866
- ]);
71867
- var RecordSecretRefsSchema = record(string$2(), record(string$2(), string$2()));
71868
- method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string$2(), DeclareKindResultSchema) }), {
71869
- kind: "mutation",
71870
- auth: "admin"
71871
- }), method(object({
71872
- ...KindShape,
71873
- id: RecordIdSchema
71874
- }), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
71875
- ...KindShape,
71876
- ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
71877
- }), array(RecordViewSchema), { auth: "admin" }), method(object({
71878
- ...KindShape,
71879
- ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
71880
- }), RecordSecretRefsSchema, { auth: "admin" }), method(object({
71881
- ...KindShape,
71882
- record: RecordInputSchema,
71883
- /** Compare-and-set. `0` means "the row must not exist". */
71884
- ifRev: number().int().min(0).optional()
71885
- }), object({
71886
- rev: number().int(),
71887
- releasedRefs: SecretRefsList
71888
- }), {
71889
- kind: "mutation",
71890
- auth: "admin"
71891
- }), method(object({
71892
- ...KindShape,
71893
- records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
71894
- }), object({
71895
- count: number().int(),
71896
- releasedRefs: SecretRefsList
71897
- }), {
71898
- kind: "mutation",
71899
- auth: "admin"
71900
- }), method(object({
71901
- ...KindShape,
71902
- id: RecordIdSchema,
71903
- data: RecordBodySchema,
71904
- at: number().int().nullable().optional(),
71905
- expiresAt: number().int().nullable().optional(),
71906
- ifRev: number().int().min(1).optional()
71907
- }), object({
71908
- rev: number().int(),
71909
- releasedRefs: SecretRefsList
71910
- }), {
71911
- kind: "mutation",
71912
- auth: "admin"
71913
- }), method(object({
71914
- ...KindShape,
71915
- id: RecordIdSchema
71916
- }), object({
71917
- deleted: boolean(),
71918
- secretRefs: SecretRefsList
71919
- }), {
71920
- kind: "mutation",
71921
- auth: "admin"
71922
- }), method(object({
71923
- ...KindShape,
71924
- ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
71925
- }), object({
71926
- deleted: number().int(),
71927
- secretRefs: SecretRefsList
71928
- }), {
71929
- kind: "mutation",
71930
- auth: "admin"
71931
- }), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
71932
- ...KindShape,
71933
- ...TimeWindowShape,
71934
- where: RecordWhereSchema.optional()
71935
- }), object({ count: number().int() }), { auth: "admin" }), method(object({
71936
- ...KindShape,
71937
- deviceId: number().int().optional(),
71938
- from: number(),
71939
- to: number(),
71940
- bucketMs: number().int().positive()
71941
- }), object({ buckets: array(object({
71942
- bucketStart: number(),
71943
- count: number().int()
71944
- })) }), { auth: "admin" }), method(object({
71945
- ...KindShape,
71946
- op: _enum([
71947
- "sum",
71948
- "min",
71949
- "max",
71950
- "count"
71951
- ]),
71952
- field: RecordFieldPathSchema.optional(),
71953
- groupBy: RecordFieldPathSchema.optional(),
71954
- where: RecordWhereSchema.optional()
71955
- }), object({ rows: array(object({
71956
- group: union([
71957
- string$2(),
71958
- number(),
71959
- _null()
71960
- ]),
71961
- value: number().nullable()
71962
- })) }), { auth: "admin" }), method(object({
71963
- ...KindShape,
71964
- deviceId: number().int().optional(),
71965
- atBefore: number().optional(),
71966
- where: RecordWhereSchema.optional()
71967
- }), object({
71968
- deleted: number().int(),
71969
- secretRefs: SecretRefsList
71970
- }), {
71971
- kind: "mutation",
71972
- auth: "admin"
71973
- }), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
71974
- results: array(object({
71975
- rev: number().int().nullable(),
71976
- deleted: boolean()
71977
- })),
71978
- releasedRefs: SecretRefsList,
71979
- secretRefs: SecretRefsList
71980
- }), {
71981
- kind: "mutation",
71982
- auth: "admin"
71983
- }), method(object({
71984
- deviceId: number().int(),
71985
- kinds: array(RecordKindNameSchema).optional()
71986
- }), object({
71987
- deleted: record(string$2(), number().int()),
71988
- secretRefs: SecretRefsList
71989
- }), {
71990
- kind: "mutation",
71991
- auth: "admin"
71992
- }), method(object(KindShape), object({ id: number().int() }), {
71993
- kind: "mutation",
71994
- auth: "admin"
71995
- });
71996
- /**
71997
72100
  * record-entries - the ONE admin surface for every list of user-created entries
71998
72101
  * (D881): notification targets, brokers, LLM profiles, anything an addon
71999
72102
  * declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
@@ -81721,7 +81824,7 @@ var BaseDeviceProvider = class extends BaseAddon {
81721
81824
  * `deviceClasses` map (first registered type). `null` when manual creation is
81722
81825
  * unsupported. Lets the Add-Device dialog pick the right type per provider.
81723
81826
  */
81724
- async getManualCreationType() {
81827
+ async getManualCreationType(_input) {
81725
81828
  if (!await this.supportsManualCreation()) return { deviceType: null };
81726
81829
  return { deviceType: Object.values(DeviceType).find((t) => this.deviceClasses[t] !== void 0) ?? null };
81727
81830
  }
@@ -81732,7 +81835,7 @@ var BaseDeviceProvider = class extends BaseAddon {
81732
81835
  return true;
81733
81836
  }
81734
81837
  async getChildCreationSchema(input) {
81735
- return this.onGetCreationSchema(input.type);
81838
+ return this.onGetCreationSchema(input.type, { integrationId: input.integrationId });
81736
81839
  }
81737
81840
  /**
81738
81841
  * Default kernel-orchestrated `createDevice` implementation. The
@@ -81744,7 +81847,7 @@ var BaseDeviceProvider = class extends BaseAddon {
81744
81847
  * `deviceClasses` instead.
81745
81848
  */
81746
81849
  async createDevice(input) {
81747
- const spec = await this.onCreateDevice(input.type, input.config);
81850
+ const spec = await this.onCreateDevice(input.type, input.config, { ...input.integrationId !== void 0 ? { integrationId: input.integrationId } : {} });
81748
81851
  const Class = this.deviceClasses[spec.meta.type];
81749
81852
  if (!Class) throw new Error(`${this.providerName} provider: no device class registered for type "${spec.meta.type}" — add it to the deviceClasses map`);
81750
81853
  const stableId = this.generateStableId(spec.meta.type, spec.config);
@@ -83153,20 +83256,6 @@ Object.freeze({
83153
83256
  access: "create",
83154
83257
  role: "viewer"
83155
83258
  },
83156
- "broker.add": {
83157
- capName: "broker",
83158
- capScope: "system",
83159
- addonId: null,
83160
- access: "create",
83161
- role: "admin"
83162
- },
83163
- "broker.get": {
83164
- capName: "broker",
83165
- capScope: "system",
83166
- addonId: null,
83167
- access: "view",
83168
- role: "admin"
83169
- },
83170
83259
  "broker.getBrokerConfig": {
83171
83260
  capName: "broker",
83172
83261
  capScope: "system",
@@ -83174,20 +83263,6 @@ Object.freeze({
83174
83263
  access: "view",
83175
83264
  role: "admin"
83176
83265
  },
83177
- "broker.getSettings": {
83178
- capName: "broker",
83179
- capScope: "system",
83180
- addonId: null,
83181
- access: "view",
83182
- role: "admin"
83183
- },
83184
- "broker.getSettingsSchema": {
83185
- capName: "broker",
83186
- capScope: "system",
83187
- addonId: null,
83188
- access: "view",
83189
- role: "admin"
83190
- },
83191
83266
  "broker.getState": {
83192
83267
  capName: "broker",
83193
83268
  capScope: "system",
@@ -83202,20 +83277,6 @@ Object.freeze({
83202
83277
  access: "view",
83203
83278
  role: "admin"
83204
83279
  },
83205
- "broker.list": {
83206
- capName: "broker",
83207
- capScope: "system",
83208
- addonId: null,
83209
- access: "view",
83210
- role: "admin"
83211
- },
83212
- "broker.listProviders": {
83213
- capName: "broker",
83214
- capScope: "system",
83215
- addonId: null,
83216
- access: "view",
83217
- role: "admin"
83218
- },
83219
83280
  "broker.publish": {
83220
83281
  capName: "broker",
83221
83282
  capScope: "system",
@@ -83223,20 +83284,6 @@ Object.freeze({
83223
83284
  access: "create",
83224
83285
  role: "admin"
83225
83286
  },
83226
- "broker.remove": {
83227
- capName: "broker",
83228
- capScope: "system",
83229
- addonId: null,
83230
- access: "delete",
83231
- role: "admin"
83232
- },
83233
- "broker.setSettings": {
83234
- capName: "broker",
83235
- capScope: "system",
83236
- addonId: null,
83237
- access: "create",
83238
- role: "admin"
83239
- },
83240
83287
  "broker.subscribe": {
83241
83288
  capName: "broker",
83242
83289
  capScope: "system",
@@ -83244,20 +83291,6 @@ Object.freeze({
83244
83291
  access: "create",
83245
83292
  role: "admin"
83246
83293
  },
83247
- "broker.testConnection": {
83248
- capName: "broker",
83249
- capScope: "system",
83250
- addonId: null,
83251
- access: "create",
83252
- role: "admin"
83253
- },
83254
- "broker.testSettings": {
83255
- capName: "broker",
83256
- capScope: "system",
83257
- addonId: null,
83258
- access: "create",
83259
- role: "admin"
83260
- },
83261
83294
  "broker.unsubscribe": {
83262
83295
  capName: "broker",
83263
83296
  capScope: "system",
@@ -88221,6 +88254,13 @@ Object.freeze({
88221
88254
  access: "view",
88222
88255
  role: "viewer"
88223
88256
  },
88257
+ "pipelineAnalytics.getMediaLocationCensus": {
88258
+ capName: "pipeline-analytics",
88259
+ capScope: "device",
88260
+ addonId: null,
88261
+ access: "view",
88262
+ role: "admin"
88263
+ },
88224
88264
  "pipelineAnalytics.getMediaReclaimStatus": {
88225
88265
  capName: "pipeline-analytics",
88226
88266
  capScope: "device",
@@ -97162,6 +97202,275 @@ object({
97162
97202
  delay: number().nonnegative().optional(),
97163
97203
  commands: record(string$2(), unknown())
97164
97204
  });
97205
+ var BrokerInUseError = class extends Error {
97206
+ brokerId;
97207
+ users;
97208
+ name = "BrokerInUseError";
97209
+ constructor(brokerId, users) {
97210
+ super(`broker '${brokerId}' is still in use by ${users.map((u) => `${u.kind} '${u.name ?? u.id}' (${u.id})`).join(", ")}; unlink them first`);
97211
+ this.brokerId = brokerId;
97212
+ this.users = users;
97213
+ }
97214
+ };
97215
+ /** Throw {@link BrokerInUseError} when any finder names a user of `brokerId`. */
97216
+ async function assertBrokerUnused(brokerId, finders) {
97217
+ const users = [];
97218
+ for (const finder of finders) users.push(...await finder.find(brokerId));
97219
+ if (users.length > 0) throw new BrokerInUseError(brokerId, users);
97220
+ }
97221
+ /**
97222
+ * An account provider whose `deleteInstance` is refused while anything still uses the account
97223
+ * (D1012). The account is an `integration-instances` kind (D1013), so the refusal lives on the
97224
+ * delete the instances page calls, not on a separate broker cap.
97225
+ */
97226
+ function guardBrokerDelete(base, users) {
97227
+ return {
97228
+ ...base,
97229
+ deleteInstance: async (input) => {
97230
+ await assertBrokerUnused(input.id, users());
97231
+ return base.deleteInstance(input);
97232
+ }
97233
+ };
97234
+ }
97235
+ /**
97236
+ * The generic binding of a service to a broker (D1012). A service (an integration today; a
97237
+ * notifier target, a calendar source or an exporter later) names the broker it runs through by id
97238
+ * and keeps only its OWN link settings: never a copy of the broker's host, sign-in or secrets,
97239
+ * which stay with the broker and are edited on the Brokers page.
97240
+ *
97241
+ * One primitive serves every consumer:
97242
+ * - {@link declareBrokerLinkKind}: the record kind, body `{ name, brokerId, ...linkFields }`;
97243
+ * - {@link buildBrokerLinkForm}: the form, a `broker-select` picker plus the service's link
97244
+ * sections, which may be DYNAMIC (a list read from the chosen broker, like HA calendars);
97245
+ * - {@link buildBrokerLinkInstances}: the `integration-instances` provider of an integration;
97246
+ * - {@link resolveBrokerId} / {@link brokerLinkUserFinder}: which broker a record names and which
97247
+ * records name a broker, so a delete of a broker still in use can name its users generically.
97248
+ */
97249
+ /** The record kind an integration is stored under. */
97250
+ var BROKER_LINK_KIND_NAME = "connections";
97251
+ /** What every broker-linked record carries; `brokerId` is always present, `''` is a row whose broker is not picked yet; link fields of the service come on top. */
97252
+ var BrokerLinkBodySchema = entryBody({ brokerId: string$2() });
97253
+ function declareBrokerLinkKind(spec) {
97254
+ return defineEntryKind(BROKER_LINK_KIND_NAME, {
97255
+ title: spec.title,
97256
+ schema: BrokerLinkBodySchema.extend(spec.linkFields ?? {}),
97257
+ writeTrigger: spec.writeTrigger
97258
+ });
97259
+ }
97260
+ /** The form: a picker over the brokers of `brokerKind`, then the service's link sections. */
97261
+ async function buildBrokerLinkForm(spec, brokerId) {
97262
+ const dynamic = brokerId !== void 0 && spec.dynamicSections !== void 0 ? await spec.dynamicSections(brokerId) : [];
97263
+ return { sections: [
97264
+ {
97265
+ id: "broker-link",
97266
+ title: spec.brokerLabel,
97267
+ description: `Each integration links to a ${spec.brokerLabel} connection (Connectivity → Brokers). The connection and its sign-in are edited there.`,
97268
+ columns: 1,
97269
+ fields: [{
97270
+ type: "broker-select",
97271
+ key: "brokerId",
97272
+ label: spec.brokerLabel,
97273
+ brokerKind: spec.brokerKind
97274
+ }]
97275
+ },
97276
+ ...spec.linkSections ?? [],
97277
+ ...dynamic
97278
+ ] };
97279
+ }
97280
+ /** The broker a record names; undefined while it names none. */
97281
+ function resolveBrokerId(record) {
97282
+ return record.brokerId === void 0 || record.brokerId === "" ? void 0 : record.brokerId;
97283
+ }
97284
+ /** The broker an integration links to, from its record. */
97285
+ async function brokerIdOf(instances, integrationId) {
97286
+ const body = await instances.getResolved(integrationId);
97287
+ return body === null ? void 0 : resolveBrokerId(body);
97288
+ }
97289
+ /** Every record of the kind that names `brokerId`, as users of that broker. */
97290
+ function brokerLinkUserFinder(instances, userKind = "integration") {
97291
+ return { find: async (brokerId) => (await instances.listResolved()).filter((entry) => resolveBrokerId(entry.body) === brokerId).map((entry) => ({
97292
+ kind: userKind,
97293
+ id: entry.id,
97294
+ name: entry.body.name
97295
+ })) };
97296
+ }
97297
+ /** The `integration-instances` provider of an addon whose integrations link to a broker. */
97298
+ function buildBrokerLinkInstances(records, spec) {
97299
+ const instances = new RecordIntegrationInstances(records, spec.kind, {
97300
+ description: {
97301
+ role: "devices",
97302
+ instanceMode: "multiple",
97303
+ kind: "connection",
97304
+ canTest: false
97305
+ },
97306
+ formSchema: async (id) => buildBrokerLinkForm(spec, id === null ? void 0 : await brokerIdOf(instances, id)),
97307
+ initialBody: (name) => spec.kind.schema.parse({
97308
+ name,
97309
+ brokerId: ""
97310
+ }),
97311
+ configured: (body) => resolveBrokerId(body) !== void 0,
97312
+ ...spec.onChanged !== void 0 ? { onChanged: spec.onChanged } : {}
97313
+ });
97314
+ return instances;
97315
+ }
97316
+ var BROKER_ROWS_MAX = 1e3;
97317
+ var STRAY_REGISTRY_ID = /^int_\d+$/;
97318
+ async function moveBrokerLinksToConnections(ports) {
97319
+ const brokers = await ports.listBrokers();
97320
+ const strays = brokers.filter((b) => STRAY_REGISTRY_ID.test(b.id));
97321
+ if (strays.length > 0) ports.warn(`${strays.length} broker row(s) are keyed like a registry integration and are left as they are: ${strays.map((s) => s.id).join(", ")}`);
97322
+ let moved = 0;
97323
+ for (const broker of brokers) {
97324
+ const integrationId = broker.integrationId;
97325
+ if (integrationId === void 0 || integrationId === "") continue;
97326
+ const existing = await ports.readConnection(integrationId);
97327
+ if (existing === null) await ports.putConnection(integrationId, {
97328
+ name: broker.name,
97329
+ brokerId: broker.id
97330
+ });
97331
+ else if (existing.brokerId !== broker.id) {
97332
+ ports.warn(`integration '${integrationId}' already names broker '${existing.brokerId ?? "none"}', not '${broker.id}'; the link stays on the broker`);
97333
+ continue;
97334
+ }
97335
+ if ((await ports.readConnection(integrationId))?.brokerId !== broker.id) throw new Error(`integration '${integrationId}': the link did not read back - broker '${broker.id}' keeps it`);
97336
+ await ports.clearBrokerLink(broker.id);
97337
+ moved += 1;
97338
+ }
97339
+ return {
97340
+ outcome: moved === 0 ? "already-done" : "done",
97341
+ summary: {
97342
+ brokers: brokers.length,
97343
+ moved,
97344
+ strayIntegrationRows: strays.length
97345
+ }
97346
+ };
97347
+ }
97348
+ /** The ports over the two record kinds of the addon. */
97349
+ function brokerLinkPortsOver(deps) {
97350
+ return {
97351
+ warn: deps.warn,
97352
+ listBrokers: async () => {
97353
+ return (await deps.brokers.query({ limit: BROKER_ROWS_MAX })).records.map((row) => {
97354
+ const name = row.data["name"];
97355
+ const link = row.data["integrationId"];
97356
+ return {
97357
+ id: row.id,
97358
+ name: typeof name === "string" && name.length > 0 ? name : row.id,
97359
+ integrationId: typeof link === "string" ? link : void 0
97360
+ };
97361
+ });
97362
+ },
97363
+ readConnection: async (id) => {
97364
+ const row = await deps.connections.get(id);
97365
+ return row === null ? null : { brokerId: row.data.brokerId };
97366
+ },
97367
+ putConnection: async (id, body) => {
97368
+ await deps.connections.put({
97369
+ id,
97370
+ data: body
97371
+ }, { ifRev: 0 });
97372
+ },
97373
+ clearBrokerLink: async (brokerId) => {
97374
+ await deps.brokers.patch(brokerId, { integrationId: null });
97375
+ }
97376
+ };
97377
+ }
97378
+ //#endregion
97379
+ //#region src/config.ts
97380
+ /**
97381
+ * Matter broker connection settings for ONE fabric/controller (one broker entry).
97382
+ * v1 is intentionally minimal: optional Thread / Wi-Fi credentials supplied up
97383
+ * front for wireless commissioning. The pairing code itself is NOT stored here —
97384
+ * it is passed transiently to the commissioning action.
97385
+ *
97386
+ * The fabric credentials (root cert / NOC / node registry) live in the addon
97387
+ * `dataDir` via the controller's storage, NEVER in this typed KV.
97388
+ */
97389
+ var matterConnectionSchema = object({
97390
+ /** Optional Thread operational dataset (hex) for Thread device onboarding. */
97391
+ threadDataset: string$2().default("").describe("Thread operational dataset (hex) — required only for Thread devices").meta({ secret: true }),
97392
+ /** Optional Wi-Fi SSID for Wi-Fi device onboarding over BLE. */
97393
+ wifiSsid: string$2().default("").describe("Wi-Fi SSID — required only for BLE Wi-Fi onboarding"),
97394
+ /** Optional Wi-Fi password for Wi-Fi device onboarding over BLE. */
97395
+ wifiPassword: string$2().default("").describe("Wi-Fi password (optional)").meta({ secret: true }),
97396
+ /** PULL-reconcile interval for the commissioned-node inventory. */
97397
+ reconcileIntervalMs: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1e4).max(864e5).default(3e5)).describe("Node-inventory reconcile interval (ms)")
97398
+ });
97399
+ /** A single registered Matter fabric ("broker"). */
97400
+ var MatterBrokerEntrySchema = object({
97401
+ id: string$2().min(1),
97402
+ name: string$2().min(1),
97403
+ connection: matterConnectionSchema,
97404
+ /** Absent means on; the instances page switches a fabric off without deleting it. */
97405
+ enabled: boolean().optional()
97406
+ });
97407
+ /** Top-level addon config — the fabrics are records, so nothing is left here. */
97408
+ var matterAddonConfigSchema = object({});
97409
+ /** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
97410
+ function toRecord(value) {
97411
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.fromEntries(Object.entries(value));
97412
+ return {};
97413
+ }
97414
+ /** Coerce a loose settings blob through the connection schema, applying defaults. */
97415
+ function settingsToMatterConnection(settings) {
97416
+ return matterConnectionSchema.parse(toRecord(settings));
97417
+ }
97418
+ /**
97419
+ * Settings readers see an empty string for both secrets. The stored value
97420
+ * stays in the keychain (records `secret` meta). An empty round-trip must
97421
+ * not wipe it — see {@link mergeMatterSecrets}.
97422
+ */
97423
+ function publicMatterConnection(connection) {
97424
+ return {
97425
+ ...connection,
97426
+ wifiPassword: "",
97427
+ threadDataset: ""
97428
+ };
97429
+ }
97430
+ /** Hand-written connection form for the broker/integration creation UI. */
97431
+ function buildConnectionFormSchema() {
97432
+ return { sections: [{
97433
+ id: "matter-fabric",
97434
+ title: "Matter fabric",
97435
+ titleKey: "matter-broker:connection.s_matter-fabric.title",
97436
+ description: "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
97437
+ descriptionKey: "matter-broker:connection.s_matter-fabric.description",
97438
+ columns: 1,
97439
+ fields: [
97440
+ {
97441
+ type: "password",
97442
+ key: "threadDataset",
97443
+ secret: true,
97444
+ showToggle: true,
97445
+ label: "Thread operational dataset (hex, optional)",
97446
+ labelKey: "matter-broker:connection.s_matter-fabric.threadDataset.label",
97447
+ placeholder: "0e08000000000001…"
97448
+ },
97449
+ {
97450
+ type: "text",
97451
+ key: "wifiSsid",
97452
+ label: "Wi-Fi SSID (optional)",
97453
+ labelKey: "matter-broker:connection.s_matter-fabric.wifiSsid.label"
97454
+ },
97455
+ {
97456
+ type: "password",
97457
+ key: "wifiPassword",
97458
+ label: "Wi-Fi password (optional)",
97459
+ labelKey: "matter-broker:connection.s_matter-fabric.wifiPassword.label",
97460
+ showToggle: true
97461
+ },
97462
+ {
97463
+ type: "number",
97464
+ key: "reconcileIntervalMs",
97465
+ label: "Node reconcile interval (ms)",
97466
+ labelKey: "matter-broker:connection.s_matter-fabric.reconcileIntervalMs.label",
97467
+ min: 1e4,
97468
+ max: 864e5,
97469
+ default: 3e5
97470
+ }
97471
+ ]
97472
+ }] };
97473
+ }
97165
97474
  //#endregion
97166
97475
  //#region ../../node_modules/@matter/general/dist/esm/net/tcp/TcpConnection.js
97167
97476
  /**
@@ -207501,7 +207810,6 @@ var MatterBrokerRegistry = class {
207501
207810
  #onAttributeReport;
207502
207811
  #makeManager;
207503
207812
  #managers = /* @__PURE__ */ new Map();
207504
- #integrationToBroker = /* @__PURE__ */ new Map();
207505
207813
  #nextId = 1;
207506
207814
  constructor(logger, deps) {
207507
207815
  this.#logger = logger;
@@ -207550,7 +207858,6 @@ var MatterBrokerRegistry = class {
207550
207858
  }
207551
207859
  }));
207552
207860
  this.#managers.clear();
207553
- this.#integrationToBroker.clear();
207554
207861
  }
207555
207862
  setOnBrokerConnected(cb) {
207556
207863
  this.#onBrokerConnected = cb;
@@ -207570,17 +207877,14 @@ var MatterBrokerRegistry = class {
207570
207877
  const entry = {
207571
207878
  id,
207572
207879
  name,
207573
- connection,
207574
- ...opts.integrationId !== void 0 ? { integrationId: opts.integrationId } : {}
207880
+ connection
207575
207881
  };
207576
207882
  await this.#startManager(entry);
207577
- if (opts.integrationId !== void 0) this.#integrationToBroker.set(opts.integrationId, id);
207578
207883
  return entry;
207579
207884
  }
207580
207885
  async removeEntry(id) {
207581
207886
  const mgr = this.#managers.get(id);
207582
207887
  if (!mgr) return;
207583
- for (const [integrationId, brokerId] of this.#integrationToBroker.entries()) if (brokerId === id) this.#integrationToBroker.delete(integrationId);
207584
207888
  this.#managers.delete(id);
207585
207889
  try {
207586
207890
  await mgr.stop();
@@ -207621,9 +207925,6 @@ var MatterBrokerRegistry = class {
207621
207925
  if (!mgr) throw new Error(`MatterBrokerRegistry: unknown broker id "${brokerId}"`);
207622
207926
  await mgr.invokeCommand(invocation);
207623
207927
  }
207624
- linkIntegration(integrationId, brokerId) {
207625
- this.#integrationToBroker.set(integrationId, brokerId);
207626
- }
207627
207928
  list() {
207628
207929
  return Array.from(this.#managers.values()).map((m) => m.getInfo());
207629
207930
  }
@@ -207642,10 +207943,7 @@ var MatterBrokerRegistry = class {
207642
207943
  return count;
207643
207944
  }
207644
207945
  async #startManager(entry) {
207645
- if (this.#managers.has(entry.id)) {
207646
- if (entry.integrationId !== void 0) this.#integrationToBroker.set(entry.integrationId, entry.id);
207647
- return;
207648
- }
207946
+ if (this.#managers.has(entry.id)) return;
207649
207947
  const mgr = this.#makeManager({
207650
207948
  id: entry.id,
207651
207949
  name: entry.name,
@@ -207658,7 +207956,6 @@ var MatterBrokerRegistry = class {
207658
207956
  onAttributeReport: (id, report) => this.#onAttributeReport(id, report)
207659
207957
  });
207660
207958
  this.#managers.set(entry.id, mgr);
207661
- if (entry.integrationId !== void 0) this.#integrationToBroker.set(entry.integrationId, entry.id);
207662
207959
  await mgr.start();
207663
207960
  }
207664
207961
  /** The next free broker id, for a record minted before its manager exists. */
@@ -207678,111 +207975,6 @@ var MatterBrokerRegistry = class {
207678
207975
  }
207679
207976
  };
207680
207977
  //#endregion
207681
- //#region src/config.ts
207682
- /**
207683
- * Matter broker connection settings for ONE fabric/controller (one broker entry).
207684
- * v1 is intentionally minimal: optional Thread / Wi-Fi credentials supplied up
207685
- * front for wireless commissioning. The pairing code itself is NOT stored here —
207686
- * it is passed transiently to the commissioning action.
207687
- *
207688
- * The fabric credentials (root cert / NOC / node registry) live in the addon
207689
- * `dataDir` via the controller's storage, NEVER in this typed KV.
207690
- */
207691
- var matterConnectionSchema = object({
207692
- /** Optional Thread operational dataset (hex) for Thread device onboarding. */
207693
- threadDataset: string$2().default("").describe("Thread operational dataset (hex) — required only for Thread devices").meta({ secret: true }),
207694
- /** Optional Wi-Fi SSID for Wi-Fi device onboarding over BLE. */
207695
- wifiSsid: string$2().default("").describe("Wi-Fi SSID — required only for BLE Wi-Fi onboarding"),
207696
- /** Optional Wi-Fi password for Wi-Fi device onboarding over BLE. */
207697
- wifiPassword: string$2().default("").describe("Wi-Fi password (optional)").meta({ secret: true }),
207698
- /** PULL-reconcile interval for the commissioned-node inventory. */
207699
- reconcileIntervalMs: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1e4).max(864e5).default(3e5)).describe("Node-inventory reconcile interval (ms)")
207700
- });
207701
- /** A single registered Matter fabric ("broker"). */
207702
- var MatterBrokerEntrySchema = object({
207703
- id: string$2().min(1),
207704
- name: string$2().min(1),
207705
- connection: matterConnectionSchema,
207706
- integrationId: string$2().optional(),
207707
- /** Absent means on; the instances page switches a fabric off without deleting it. */
207708
- enabled: boolean().optional()
207709
- });
207710
- /** Top-level addon config — the fabrics are records, so nothing is left here. */
207711
- var matterAddonConfigSchema = object({});
207712
- /** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
207713
- function toRecord(value) {
207714
- if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.fromEntries(Object.entries(value));
207715
- return {};
207716
- }
207717
- /** Coerce a loose settings blob through the connection schema, applying defaults. */
207718
- function settingsToMatterConnection(settings) {
207719
- return matterConnectionSchema.parse(toRecord(settings));
207720
- }
207721
- /**
207722
- * Settings readers see an empty string for both secrets. The stored value
207723
- * stays in the keychain (records `secret` meta). An empty round-trip must
207724
- * not wipe it — see {@link mergeMatterSecrets}.
207725
- */
207726
- function publicMatterConnection(connection) {
207727
- return {
207728
- ...connection,
207729
- wifiPassword: "",
207730
- threadDataset: ""
207731
- };
207732
- }
207733
- /** Keep a stored secret when the form sent back the redacted empty string. */
207734
- function mergeMatterSecrets(existing, incoming) {
207735
- return {
207736
- ...incoming,
207737
- wifiPassword: incoming.wifiPassword.length > 0 ? incoming.wifiPassword : existing.wifiPassword,
207738
- threadDataset: incoming.threadDataset.length > 0 ? incoming.threadDataset : existing.threadDataset
207739
- };
207740
- }
207741
- /** Hand-written connection form for the broker/integration creation UI. */
207742
- function buildConnectionFormSchema() {
207743
- return { sections: [{
207744
- id: "matter-fabric",
207745
- title: "Matter fabric",
207746
- titleKey: "matter-broker:connection.s_matter-fabric.title",
207747
- description: "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
207748
- descriptionKey: "matter-broker:connection.s_matter-fabric.description",
207749
- columns: 1,
207750
- fields: [
207751
- {
207752
- type: "password",
207753
- key: "threadDataset",
207754
- secret: true,
207755
- showToggle: true,
207756
- label: "Thread operational dataset (hex, optional)",
207757
- labelKey: "matter-broker:connection.s_matter-fabric.threadDataset.label",
207758
- placeholder: "0e08000000000001…"
207759
- },
207760
- {
207761
- type: "text",
207762
- key: "wifiSsid",
207763
- label: "Wi-Fi SSID (optional)",
207764
- labelKey: "matter-broker:connection.s_matter-fabric.wifiSsid.label"
207765
- },
207766
- {
207767
- type: "password",
207768
- key: "wifiPassword",
207769
- label: "Wi-Fi password (optional)",
207770
- labelKey: "matter-broker:connection.s_matter-fabric.wifiPassword.label",
207771
- showToggle: true
207772
- },
207773
- {
207774
- type: "number",
207775
- key: "reconcileIntervalMs",
207776
- label: "Node reconcile interval (ms)",
207777
- labelKey: "matter-broker:connection.s_matter-fabric.reconcileIntervalMs.label",
207778
- min: 1e4,
207779
- max: 864e5,
207780
- default: 3e5
207781
- }
207782
- ]
207783
- }] };
207784
- }
207785
- //#endregion
207786
207978
  //#region src/matter-commissioning.ts
207787
207979
  /** A clear validation error for a malformed pairing code. */
207788
207980
  var InvalidPairingCodeError = class extends Error {
@@ -207813,7 +208005,7 @@ function parsePairingCode(raw) {
207813
208005
  normalized: digits
207814
208006
  };
207815
208007
  }
207816
- /** True when a code parses cleanly (no throw). Convenience for `testSettings`. */
208008
+ /** True when a code parses cleanly (no throw). Convenience for validating the pasted code. */
207817
208009
  function isValidPairingCode(raw) {
207818
208010
  try {
207819
208011
  parsePairingCode(raw);
@@ -207824,113 +208016,22 @@ function isValidPairingCode(raw) {
207824
208016
  }
207825
208017
  //#endregion
207826
208018
  //#region src/matter-broker-provider.ts
207827
- var MATTER_KIND = "matter";
207828
208019
  /**
207829
- * Construct the `broker` cap provider for the Matter addon. Pure builder.
207830
- * Mirrors `buildEcowittBrokerProvider`. The defining Matter operation —
207831
- * commissioning — is surfaced through `publish` with
207832
- * `target: { action: 'commissionNode' }` and `payload: { pairingCode }`.
207833
- * `threadDataset` and `wifiPassword` are redacted on settings reads and
207834
- * preserved on an empty round-trip. Fabric keys stay in matter.js storage.
208020
+ * Construct the `broker` cap provider for the Matter addon: the bus only (D1013). The fabrics
208021
+ * themselves are the `account` kind of the addon's `integration-instances` provider. The defining
208022
+ * Matter operation, commissioning, is surfaced through `publish` with
208023
+ * `target: { action: 'commissionNode' }` and `payload: { pairingCode }`. `threadDataset` and
208024
+ * `wifiPassword` are redacted on config reads. Fabric keys stay in matter.js storage.
207835
208025
  */
207836
208026
  function buildMatterBrokerProvider(deps) {
207837
- const { ownerAddonId, registry, getBrokers, persistBrokers, cascadeRemoveDevices, logger } = deps;
208027
+ const { registry, getBrokers } = deps;
207838
208028
  const owns = (id) => getBrokers().some((b) => b.id === id);
207839
208029
  const redact = (entry) => publicMatterConnection(entry.connection);
207840
208030
  return {
207841
- list: async ({ kind }) => {
207842
- if (kind && kind !== MATTER_KIND) return [];
207843
- return registry.list().map((info) => ({
207844
- ...info,
207845
- addonId: ownerAddonId
207846
- }));
207847
- },
207848
- get: async ({ id }) => {
207849
- const info = registry.get(id);
207850
- return info ? {
207851
- ...info,
207852
- addonId: ownerAddonId
207853
- } : null;
207854
- },
207855
- listProviders: async () => [{
207856
- addonId: ownerAddonId,
207857
- kinds: [{
207858
- kind: MATTER_KIND,
207859
- label: "Matter"
207860
- }]
207861
- }],
207862
- add: async ({ kind, name, settings }) => {
207863
- if (kind !== MATTER_KIND) throw new Error(`matter-broker: only kind '${MATTER_KIND}' is handled here (got '${kind}')`);
207864
- const entry = await registry.createEntry(name, settingsToMatterConnection(settings));
207865
- await persistBrokers([...getBrokers(), entry]);
207866
- return { id: entry.id };
207867
- },
207868
- remove: async ({ id }) => {
207869
- if (!owns(id)) return;
207870
- await registry.removeEntry(id);
207871
- await cascadeRemoveDevices(id).catch((err) => {
207872
- logger.warn("matter: broker cascade-remove threw", {
207873
- tags: { brokerId: id },
207874
- meta: { error: errMsg(err) }
207875
- });
207876
- });
207877
- await persistBrokers(getBrokers().filter((b) => b.id !== id));
207878
- },
207879
- testConnection: async ({ id }) => {
207880
- if (!owns(id)) return {
207881
- ok: false,
207882
- error: "unknown broker"
207883
- };
207884
- const info = registry.get(id);
207885
- if (!info) return {
207886
- ok: false,
207887
- error: "unknown broker"
207888
- };
207889
- if (info.status === "connected") return {
207890
- ok: true,
207891
- latencyMs: info.lastCheckedAt ? Math.max(0, Date.now() - info.lastCheckedAt) : 0
207892
- };
207893
- return {
207894
- ok: false,
207895
- error: info.error ?? `status: ${info.status}`
207896
- };
207897
- },
207898
- getSettings: async ({ id }) => {
207899
- const entry = getBrokers().find((b) => b.id === id);
207900
- return entry ? redact(entry) : null;
207901
- },
207902
- setSettings: async ({ id, settings }) => {
207903
- if (!owns(id)) return;
207904
- const existing = getBrokers().find((b) => b.id === id);
207905
- if (!existing) return;
207906
- const parsed = settingsToMatterConnection(settings);
207907
- const merged = mergeMatterSecrets(existing.connection, parsed);
207908
- const updated = await registry.updateEntry(id, merged);
207909
- await persistBrokers(getBrokers().map((b) => b.id === id ? updated : b));
207910
- },
207911
208031
  getBrokerConfig: async ({ id }) => {
207912
208032
  const entry = getBrokers().find((b) => b.id === id);
207913
208033
  return entry ? redact(entry) : null;
207914
208034
  },
207915
- getSettingsSchema: async ({ kind }) => {
207916
- if (kind !== MATTER_KIND) return null;
207917
- return buildConnectionFormSchema();
207918
- },
207919
- testSettings: async ({ kind, settings }) => {
207920
- if (kind !== MATTER_KIND) return {
207921
- ok: false,
207922
- error: `unsupported kind: ${kind}`
207923
- };
207924
- try {
207925
- settingsToMatterConnection(settings);
207926
- return { ok: true };
207927
- } catch (err) {
207928
- return {
207929
- ok: false,
207930
- error: errMsg(err)
207931
- };
207932
- }
207933
- },
207934
208035
  /**
207935
208036
  * `publish` is the commissioning + control action channel.
207936
208037
  * `target: { action: 'commissionNode' }`, `payload: { pairingCode }` →
@@ -208150,8 +208251,8 @@ function buildMatterAdoptionProvider(deps) {
208150
208251
  //#region src/matter-broker-device-cascade.ts
208151
208252
  /**
208152
208253
  * Remove every adopted Matter PARENT device (children cascade via the kernel)
208153
- * whose persisted config carries `{ system: 'matter', brokerId }`. Used on
208154
- * broker.remove and integration delete. Best-effort per device; returns the
208254
+ * whose persisted config carries `{ system: 'matter', brokerId }`. Used when a
208255
+ * fabric instance is deleted and when an integration is deleted. Best-effort per device; returns the
208155
208256
  * count removed. Mirrors the Ecowitt cascade helper.
208156
208257
  */
208157
208258
  async function cascadeRemoveDevicesForBroker(input) {
@@ -208791,20 +208892,26 @@ var matterBrokersEntryKind = defineEntryKind("brokers", {
208791
208892
  title: "Matter brokers",
208792
208893
  schema: entryBody({
208793
208894
  ...matterConnectionSchema.shape,
208895
+ /** Retired (D1012): the link lives on the integration's `connections` record. Only the migration reads it. */
208794
208896
  integrationId: string$2().optional(),
208795
208897
  enabled: boolean().optional()
208796
208898
  }),
208797
208899
  writeTrigger: "an operator adds, edits or removes a Matter fabric broker",
208798
208900
  retiredSecretPaths: ["connection.threadDataset", "connection.wifiPassword"]
208799
208901
  });
208902
+ /** A Matter integration: the link to the fabric (broker) it imports devices from. */
208903
+ var matterConnectionsEntryKind = declareBrokerLinkKind({
208904
+ title: "Matter integrations",
208905
+ writeTrigger: "an operator links a Matter integration to a Matter fabric"
208906
+ });
208800
208907
  //#endregion
208801
- //#region src/matter-instances.ts
208908
+ //#region src/matter-account-instances.ts
208802
208909
  /**
208803
- * The Matter fabrics as `integration-instances` (planning#408 P3): one instance per `brokers`
208910
+ * The Matter fabrics as `integration-instances` (planning#408 P3, D1013; the `account` kind of the composed provider, the integrations are `matter-instances.ts`): one instance per `brokers`
208804
208911
  * record, over the SAME records and ids (`matter_NNN`) the `broker` cap serves. The body is flat.
208805
208912
  * A fabric needs no sign-in; commissioning a device stays on the adoption flow, behind the fabric.
208806
208913
  */
208807
- var MATTER_INSTANCE_KIND = "matter";
208914
+ var MATTER_ACCOUNT_KIND = "account";
208808
208915
  function statusOf(info) {
208809
208916
  if (info === null) return {
208810
208917
  state: "unknown",
@@ -208824,17 +208931,13 @@ function statusOf(info) {
208824
208931
  ...info.error !== null ? { detail: info.error } : {}
208825
208932
  };
208826
208933
  }
208827
- function buildMatterInstances(records, deps) {
208934
+ function buildMatterAccountInstances(records, deps) {
208828
208935
  return new RecordIntegrationInstances(records, matterBrokersEntryKind, {
208829
208936
  description: {
208830
- role: "both",
208937
+ role: "services",
208831
208938
  instanceMode: "multiple",
208832
208939
  kind: "connection",
208833
208940
  canTest: true,
208834
- kinds: [{
208835
- id: MATTER_INSTANCE_KIND,
208836
- label: "Matter"
208837
- }],
208838
208941
  allowRestoreId: true
208839
208942
  },
208840
208943
  formSchema: () => buildConnectionFormSchema(),
@@ -208846,7 +208949,7 @@ function buildMatterInstances(records, deps) {
208846
208949
  reconcileIntervalMs: 3e5
208847
208950
  }),
208848
208951
  mintId: () => deps.mintId(),
208849
- kindOf: () => MATTER_INSTANCE_KIND,
208952
+ kindOf: () => MATTER_ACCOUNT_KIND,
208850
208953
  health: (_body, id) => {
208851
208954
  const info = deps.infoOf(id);
208852
208955
  return {
@@ -208875,6 +208978,22 @@ function buildMatterInstances(records, deps) {
208875
208978
  });
208876
208979
  }
208877
208980
  //#endregion
208981
+ //#region src/matter-instances.ts
208982
+ /**
208983
+ * The Matter integrations as `integration-instances` (D1012): one `connections` record per
208984
+ * integration, keyed by the registry id, whose body is the link to a Matter fabric (a `brokers`
208985
+ * record, edited on the Brokers page).
208986
+ */
208987
+ var MATTER_BROKER_KIND = "matter";
208988
+ function buildMatterInstances(records) {
208989
+ return buildBrokerLinkInstances(records, {
208990
+ kind: matterConnectionsEntryKind,
208991
+ brokerKind: MATTER_BROKER_KIND,
208992
+ brokerLabel: "Matter fabric",
208993
+ onChanged: () => void 0
208994
+ });
208995
+ }
208996
+ //#endregion
208878
208997
  //#region src/migrations/flatten-broker-records.ts
208879
208998
  /**
208880
208999
  * The Matter broker records keep their fabric settings flat (planning#408 P3): the nested
@@ -208898,6 +209017,21 @@ async function flattenBrokerRecords(ports) {
208898
209017
  });
208899
209018
  }
208900
209019
  //#endregion
209020
+ //#region src/migrations/broker-links-to-connections.ts
209021
+ var BROKER_LINKS_TO_CONNECTIONS_ID = "matter-broker/2026-11-05-broker-links-to-connections";
209022
+ /** The broker records and the integration (`connections`) records of this addon. */
209023
+ function matterBrokerLinkPorts(records, warn) {
209024
+ return brokerLinkPortsOver({
209025
+ brokers: records.kind({
209026
+ name: matterBrokersEntryKind.name,
209027
+ declaration: matterBrokersEntryKind.declaration,
209028
+ schema: record(string$2(), unknown())
209029
+ }),
209030
+ connections: records.kind(matterConnectionsEntryKind),
209031
+ warn
209032
+ });
209033
+ }
209034
+ //#endregion
208901
209035
  //#region src/migrations/index.ts
208902
209036
  /** The `matter-broker` catalogue (D987): offered to the engine when the addon initialises. */
208903
209037
  var ADDON_MIGRATIONS = [{
@@ -208915,6 +209049,21 @@ var ADDON_MIGRATIONS = [{
208915
209049
  fixtureMark: "the integration link and the Wi-Fi SSID of each fabric"
208916
209050
  },
208917
209051
  procedure: flattenBrokerRecords
209052
+ }, {
209053
+ meta: {
209054
+ id: BROKER_LINKS_TO_CONNECTIONS_ID,
209055
+ owner: "matter-broker",
209056
+ level: "addon-state",
209057
+ seq: 2,
209058
+ dependsOn: [FLATTEN_BROKER_RECORDS_ID],
209059
+ store: "settings",
209060
+ scope: "cluster",
209061
+ onFailure: "block-owner",
209062
+ bodyKind: "procedure",
209063
+ description: "Write each integration a connections record naming its Matter fabric (keyed by the integration id), then clear the integration link on the fabric; stray registry-keyed fabric rows are counted and logged, never deleted",
209064
+ fixtureMark: "the name and the Wi-Fi SSID of the fabric that keeps its record"
209065
+ },
209066
+ procedure: moveBrokerLinksToConnections
208918
209067
  }];
208919
209068
  //#endregion
208920
209069
  //#region src/matter-broker-store.ts
@@ -208928,7 +209077,6 @@ function rowToEntry(id, data) {
208928
209077
  wifiPassword: data.wifiPassword,
208929
209078
  reconcileIntervalMs: data.reconcileIntervalMs
208930
209079
  },
208931
- integrationId: data.integrationId,
208932
209080
  enabled: data.enabled
208933
209081
  });
208934
209082
  return parsed.success ? parsed.data : null;
@@ -208965,11 +209113,10 @@ var MatterBrokerStore = class MatterBrokerStore {
208965
209113
  const nextIds = new Set(entries.map((e) => e.id));
208966
209114
  for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
208967
209115
  for (const entry of entries) {
208968
- const { id, name, connection, integrationId, enabled } = entry;
209116
+ const { id, name, connection, enabled } = entry;
208969
209117
  const body = {
208970
209118
  name,
208971
209119
  ...connection,
208972
- ...integrationId === void 0 ? {} : { integrationId },
208973
209120
  ...enabled === void 0 ? {} : { enabled }
208974
209121
  };
208975
209122
  const parsed = matterBrokersEntryKind.schema.safeParse(body);
@@ -209007,6 +209154,7 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209007
209154
  registry = null;
209008
209155
  brokerStore = null;
209009
209156
  instances = null;
209157
+ accountInstances = null;
209010
209158
  activeBrokers = [];
209011
209159
  constructor() {
209012
209160
  super({ ...DEFAULTS });
@@ -209015,15 +209163,22 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209015
209163
  const regs = this.providerRegistrationsFromSuper(await super.onInitialize());
209016
209164
  const records = this.ctx.records;
209017
209165
  if (records === void 0) throw new Error("matter-broker: ctx.records is required (hub-only addon)");
209018
- await records.declare({ brokers: matterBrokersEntryKind });
209166
+ await records.declare({
209167
+ brokers: matterBrokersEntryKind,
209168
+ connections: matterConnectionsEntryKind
209169
+ });
209019
209170
  this.brokerStore = MatterBrokerStore.of(records);
209020
- this.instances = buildMatterInstances(records, {
209171
+ this.instances = buildMatterInstances(records);
209172
+ this.accountInstances = buildMatterAccountInstances(records, {
209021
209173
  mintId: () => this.requireRegistry().allocateId(),
209022
209174
  infoOf: (id) => this.registry?.get(id) ?? null,
209023
209175
  onChanged: (id) => this.syncBrokerRuntime(id)
209024
209176
  });
209025
209177
  if (this.ctx.secrets === void 0) throw new Error("matter-broker: ctx.secrets is required");
209026
- await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, flattenPortsOver(records.kind(matterBrokersEntryKind))));
209178
+ await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, {
209179
+ ...flattenPortsOver(records.kind(matterBrokersEntryKind)),
209180
+ ...matterBrokerLinkPorts(records, (message) => this.ctx.logger.warn(message))
209181
+ }));
209027
209182
  this.activeBrokers = await this.loadPersistedBrokers();
209028
209183
  this.registry = new MatterBrokerRegistry(this.ctx.logger, {
209029
209184
  dataDir: this.ctx.dataDir,
@@ -209044,8 +209199,6 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209044
209199
  matterCommands.setDispatcher((brokerId, invocation) => this.requireRegistry().invokeCommand(brokerId, invocation));
209045
209200
  await this.registry.restore(this.activeBrokers);
209046
209201
  this.ctx.logger.info("Matter: broker addon initialised", { meta: { brokerCount: this.activeBrokers.length } });
209047
- await this.reconcileIntegrationsToBrokers();
209048
- this.subscribeIntegrationLifecycle();
209049
209202
  return [
209050
209203
  ...regs,
209051
209204
  {
@@ -209054,7 +209207,7 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209054
209207
  },
209055
209208
  {
209056
209209
  capability: integrationInstancesCapability,
209057
- provider: this.requireInstances().toProvider()
209210
+ provider: this.buildInstancesProvider()
209058
209211
  },
209059
209212
  {
209060
209213
  capability: deviceAdoptionCapability,
@@ -209073,9 +209226,37 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209073
209226
  if (this.instances === null) throw new Error("matter-broker: instances not ready");
209074
209227
  return this.instances;
209075
209228
  }
209229
+ /** One provider over the two record kinds: the fabric (broker) and the integrations using it. */
209230
+ buildInstancesProvider() {
209231
+ if (this.accountInstances === null) throw new Error("matter-broker: fabric instances not ready");
209232
+ return composeInstanceKinds([{
209233
+ kind: {
209234
+ id: "account",
209235
+ label: "Matter fabric",
209236
+ functionCap: "broker",
209237
+ brokerKind: "matter",
209238
+ role: "services"
209239
+ },
209240
+ provider: guardBrokerDelete(this.accountInstances.toProvider(), () => [brokerLinkUserFinder(this.requireInstances())])
209241
+ }, {
209242
+ kind: {
209243
+ id: "integration",
209244
+ label: "Matter",
209245
+ functionCap: "device-provider",
209246
+ role: "devices"
209247
+ },
209248
+ provider: this.requireInstances().toProvider()
209249
+ }], {
209250
+ role: "devices",
209251
+ instanceMode: "multiple",
209252
+ kind: "account",
209253
+ canTest: true,
209254
+ allowRestoreId: true
209255
+ });
209256
+ }
209076
209257
  /**
209077
- * Bring the runtime in line with one record after the instances page changed it: a removed
209078
- * record stops its controller and cascades its devices, an off record only stops the
209258
+ * Bring the runtime in line with one fabric record after the instances page changed it: a
209259
+ * removed record stops its controller and cascades its devices, an off record only stops the
209079
209260
  * controller, a live one re-applies its settings or starts under the record's own id.
209080
209261
  */
209081
209262
  async syncBrokerRuntime(id) {
@@ -209093,10 +209274,7 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209093
209274
  }
209094
209275
  try {
209095
209276
  if (reg.get(id) !== null) await reg.updateEntry(id, entry.connection);
209096
- else await reg.createEntry(entry.name, entry.connection, {
209097
- id,
209098
- ...entry.integrationId !== void 0 ? { integrationId: entry.integrationId } : {}
209099
- });
209277
+ else await reg.createEntry(entry.name, entry.connection, { id });
209100
209278
  } catch (err) {
209101
209279
  this.ctx.logger.warn("matter instance: could not start the fabric", { meta: {
209102
209280
  brokerId: id,
@@ -209223,12 +209401,8 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209223
209401
  }
209224
209402
  buildBrokerProvider() {
209225
209403
  return buildMatterBrokerProvider({
209226
- ownerAddonId: this.ctx.id,
209227
209404
  registry: this.requireRegistry(),
209228
- getBrokers: () => this.activeBrokers,
209229
- persistBrokers: (brokers) => this.persistBrokerEntries(brokers),
209230
- cascadeRemoveDevices: (brokerId) => this.cascadeRemoveDevicesForBroker(brokerId),
209231
- logger: this.ctx.logger
209405
+ getBrokers: () => this.activeBrokers
209232
209406
  });
209233
209407
  }
209234
209408
  async cascadeRemoveDevicesForBroker(brokerId) {
@@ -209243,42 +209417,6 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209243
209417
  logger: this.ctx.logger
209244
209418
  });
209245
209419
  }
209246
- async reconcileIntegrationsToBrokers() {
209247
- try {
209248
- const mine = (await this.ctx.api.integrations.list.query()).filter((i) => i.addonId === this.ctx.id);
209249
- const surviving = new Set(mine.map((i) => i.id));
209250
- for (const broker of this.activeBrokers) {
209251
- const integrationId = broker.integrationId;
209252
- if (integrationId === void 0 || !surviving.has(integrationId)) continue;
209253
- this.requireRegistry().linkIntegration(integrationId, broker.id);
209254
- }
209255
- const toRemove = this.activeBrokers.filter((b) => b.integrationId !== void 0 && !surviving.has(b.integrationId)).map((b) => b.id);
209256
- if (toRemove.length === 0) return;
209257
- for (const id of toRemove) try {
209258
- await this.requireRegistry().removeEntry(id);
209259
- await this.cascadeRemoveDevicesForBroker(id);
209260
- } catch (err) {
209261
- this.ctx.logger.warn("Matter integration→broker: broker cleanup failed", { meta: {
209262
- brokerId: id,
209263
- error: errMsg(err)
209264
- } });
209265
- }
209266
- const nextBrokers = this.activeBrokers.filter((b) => !toRemove.includes(b.id));
209267
- await this.persistBrokerEntries(nextBrokers);
209268
- } catch (err) {
209269
- this.ctx.logger.warn("Matter integration→broker reconcile failed", { meta: { error: errMsg(err) } });
209270
- }
209271
- }
209272
- subscribeIntegrationLifecycle() {
209273
- const handler = (event) => {
209274
- const addonId = event.data["addonId"];
209275
- if (typeof addonId === "string" && addonId !== this.ctx.id) return;
209276
- this.reconcileIntegrationsToBrokers();
209277
- };
209278
- this.ctx.eventBus.subscribe({ category: EventCategory.IntegrationEnabled }, handler);
209279
- this.ctx.eventBus.subscribe({ category: EventCategory.IntegrationDisabled }, handler);
209280
- this.ctx.eventBus.subscribe({ category: EventCategory.IntegrationDeleted }, handler);
209281
- }
209282
209420
  buildAdoptionProvider() {
209283
209421
  return buildMatterAdoptionProvider({
209284
209422
  registry: {
@@ -209291,9 +209429,9 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209291
209429
  },
209292
209430
  logger: this.ctx.logger,
209293
209431
  getBrokerIdForIntegration: async (id) => {
209294
- const broker = this.activeBrokers.find((entry) => entry.integrationId === id);
209295
- if (broker === void 0) throw new Error(`integration ${id} has no linked brokerId`);
209296
- return broker.id;
209432
+ const brokerId = await brokerIdOf(this.requireInstances(), id);
209433
+ if (brokerId === void 0) throw new Error(`integration ${id} has no broker`);
209434
+ return brokerId;
209297
209435
  },
209298
209436
  listAdoptedMatter: async () => {
209299
209437
  const reg = this.ctx.kernel.deviceRegistry;
@@ -209344,22 +209482,9 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
209344
209482
  }
209345
209483
  });
209346
209484
  }
209347
- globalSettingsSchema() {
209348
- return this.schema({ sections: [{
209349
- id: "matter-fabric",
209350
- title: "Matter fabric",
209351
- titleKey: "matter-broker:globalSettings.s_matter-fabric.title",
209352
- description: "Matter fabrics are managed in the External systems → Brokers tab.",
209353
- descriptionKey: "matter-broker:globalSettings.s_matter-fabric.description",
209354
- columns: 1,
209355
- fields: [{
209356
- type: "info",
209357
- key: "fabricHelp",
209358
- label: "Fabrics are managed separately",
209359
- labelKey: "matter-broker:globalSettings.s_matter-fabric.fabricHelp.label",
209360
- content: "This integration links to a Matter fabric broker. Add, edit, or remove fabrics from External systems → Brokers, then commission devices into the fabric. The integration only stores a reference to its broker."
209361
- }]
209362
- }] });
209485
+ /** The connection form a record carries; the values live on the record, never in `addon-settings`. */
209486
+ async integrationFormSchema(_instance) {
209487
+ return buildConnectionFormSchema();
209363
209488
  }
209364
209489
  async supportsManualCreation() {
209365
209490
  return false;
@@ -209408,4 +209533,4 @@ function endpointsEqual(a, b) {
209408
209533
  return true;
209409
209534
  }
209410
209535
  //#endregion
209411
- export { MatterBrokerAddon, parsePairingCode as a, matterConnectionSchema as c, endpointLabel as d, mapMatterEndpointToCamStack as f, createMatterController as h, isValidPairingCode as i, settingsToMatterConnection as l, StubMatterController as m, endpointNativeId as n, buildConnectionFormSchema as o, MatterControllerNotWiredError as p, InvalidPairingCodeError as r, matterAddonConfigSchema as s, buildMatterNodeCandidate as t, MatterClusterId as u };
209536
+ export { MatterBrokerAddon, parsePairingCode as a, mapMatterEndpointToCamStack as c, createMatterController as d, buildConnectionFormSchema as f, settingsToMatterConnection as h, isValidPairingCode as i, MatterControllerNotWiredError as l, matterConnectionSchema as m, endpointNativeId as n, MatterClusterId as o, matterAddonConfigSchema as p, InvalidPairingCodeError as r, endpointLabel as s, buildMatterNodeCandidate as t, StubMatterController as u };