@camstack/addon-smtp-nodemailer 1.2.152 → 1.2.154

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.
@@ -841,7 +841,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
841
841
  return EventCategory;
842
842
  }({});
843
843
  //#endregion
844
- //#region ../types/dist/device-type-D1fH5kaC.mjs
844
+ //#region ../types/dist/device-type-DxCT0krO.mjs
845
845
  /**
846
846
  import { errMsg } from '@camstack/types'
847
847
  * Extract a human-readable message from an unknown error value.
@@ -1003,6 +1003,13 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
1003
1003
  * filters that want "all devices with on-motion behaviour".
1004
1004
  */
1005
1005
  DeviceFeature["MotionTrigger"] = "motion-trigger";
1006
+ /**
1007
+ * Device can be run for a chosen time and then stopped. Mirrors the
1008
+ * `timed-run` wrapper cap — the cap is bound by the core-blocks addon, not by
1009
+ * a driver, so the flag is derived from the device's bindings
1010
+ * (`featuresOfBoundCaps`). Gates the quick-run control in the viewer.
1011
+ */
1012
+ DeviceFeature["TimedRun"] = "timed-run";
1006
1013
  /** Light supports rgb-triplet color via `color` cap. */
1007
1014
  DeviceFeature["LightColorRgb"] = "light-color-rgb";
1008
1015
  /** Light supports HSV color via `color` cap. */
@@ -6037,7 +6044,7 @@ var ZodIssueCode = {
6037
6044
  var ZodFirstPartyTypeKind;
6038
6045
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6039
6046
  //#endregion
6040
- //#region ../types/dist/sleep-CQP-REJp.mjs
6047
+ //#region ../types/dist/sleep-CGPZyzB5.mjs
6041
6048
  /**
6042
6049
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6043
6050
  * window to float samples (D455).
@@ -7253,6 +7260,7 @@ var RecordIntegrationInstances = class {
7253
7260
  this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
7254
7261
  this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
7255
7262
  this.nameField = kind.declaration.entry?.nameField ?? "name";
7263
+ if (hooks.kindOf === void 0 && hooks.description.kinds.length !== 1) throw new Error(`record kind '${kind.name}': describe().kinds has ${hooks.description.kinds.length} entries, so kindOf is required to say which one an instance is`);
7256
7264
  }
7257
7265
  /**
7258
7266
  * One instance read for the addon's own runtime; null when absent. When the keychain is
@@ -7294,10 +7302,21 @@ var RecordIntegrationInstances = class {
7294
7302
  }
7295
7303
  return out;
7296
7304
  }
7305
+ /** The kind an instance body is of: the provider's `kindOf`, else its one declared kind. */
7306
+ kindOfBody(body) {
7307
+ if (this.hooks.kindOf !== void 0) {
7308
+ const id = this.hooks.kindOf(body);
7309
+ if (!this.hooks.description.kinds.some((declared) => declared.id === id)) throw new Error(`record kind '${this.kind.name}': kindOf returned '${id}', which describe().kinds does not declare (D1013)`);
7310
+ return id;
7311
+ }
7312
+ const only = this.hooks.description.kinds[0];
7313
+ if (only === void 0) throw new Error(`record kind '${this.kind.name}' declares no kinds`);
7314
+ return only.id;
7315
+ }
7297
7316
  viewOf(record) {
7298
7317
  const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
7299
7318
  const rawName = record.data[this.nameField];
7300
- const kind = this.hooks.kindOf?.(record.data);
7319
+ const kind = this.kindOfBody(record.data);
7301
7320
  const health = this.hooks.health?.(record.data, record.id);
7302
7321
  return {
7303
7322
  id: record.id,
@@ -7305,7 +7324,7 @@ var RecordIntegrationInstances = class {
7305
7324
  configured: this.hooks.configured(record.data),
7306
7325
  enabled: isInstanceEnabled(record.data),
7307
7326
  formValues: shown,
7308
- ...kind !== void 0 ? { kind } : {},
7327
+ kind,
7309
7328
  ...health?.status !== void 0 ? { status: health.status } : {},
7310
7329
  ...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
7311
7330
  };
@@ -8406,7 +8425,7 @@ function boundOf(list) {
8406
8425
  * Base class for CamStack addons. Eliminates settings boilerplate:
8407
8426
  *
8408
8427
  * - Typed `config` property with automatic resolution from store + defaults
8409
- * - `getGlobalSettings()` / `updateGlobalSettings()` auto-implemented
8428
+ * - the scoped settings API (`clusterSettings` / `nodeSettings` / `deviceDefaults`) auto-implemented
8410
8429
  * - `getAddonSettings()` / `updateAddonSettings()` auto-implemented
8411
8430
  * - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented
8412
8431
  * - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)
@@ -8416,7 +8435,7 @@ function boundOf(list) {
8416
8435
  * Subclasses override:
8417
8436
  * - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]
8418
8437
  * - `onShutdown()` — cleanup
8419
- * - `globalSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
8438
+ * - `clusterSettingsSchema()` / `nodeSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
8420
8439
  *
8421
8440
  * @example
8422
8441
  * ```ts
@@ -8430,7 +8449,7 @@ function boundOf(list) {
8430
8449
  * super({ maxRetries: 3, endpoint: 'http://localhost' })
8431
8450
  * }
8432
8451
  *
8433
- * protected globalSettingsSchema() {
8452
+ * protected clusterSettingsSchema() {
8434
8453
  * return this.schema({
8435
8454
  * sections: [{
8436
8455
  * id: 'main', title: 'Settings',
@@ -8473,7 +8492,7 @@ var BaseAddon = class {
8473
8492
  defaults;
8474
8493
  /**
8475
8494
  * Hidden-state paths that hold secrets (D787) — values the addon writes
8476
- * itself (`updateGlobalSettings`, `state()`) that no form field declares.
8495
+ * itself (`state()`, the scoped writers) that no form field declares.
8477
8496
  * Stored in the keychain exactly like a `password` field. Syntax:
8478
8497
  * `'snapshotSigningKey'`, `'identity.pincode'`, `'tokens[userId].accessToken'`
8479
8498
  * (`key[<id field>]` addresses array items by id, never by index).
@@ -8483,7 +8502,7 @@ var BaseAddon = class {
8483
8502
  /**
8484
8503
  * D986 M4: the scoped schemas this addon declared, resolved once per capability
8485
8504
  * (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
8486
- * see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
8505
+ * see them. Empty until loaded.
8487
8506
  */
8488
8507
  _scopedByCap = /* @__PURE__ */ new Map();
8489
8508
  _nodeKeysByCap = /* @__PURE__ */ new Map();
@@ -8669,7 +8688,7 @@ var BaseAddon = class {
8669
8688
  /** Addon-specific cleanup. Override if needed. */
8670
8689
  async onShutdown() {}
8671
8690
  /**
8672
- * Called after config is resolved during updateGlobalSettings/updateAddonSettings.
8691
+ * Called after config is resolved during a settings write.
8673
8692
  * Override to react to config changes (e.g. restart a sampler, reconnect a service).
8674
8693
  * Not called during initialize() — use onInitialize() for initial setup.
8675
8694
  */
@@ -8698,82 +8717,12 @@ var BaseAddon = class {
8698
8717
  scopedSettingsCapabilities() {
8699
8718
  return [];
8700
8719
  }
8701
- /** Override to provide global-level settings UI schema. */
8702
- globalSettingsSchema(_cap) {
8703
- return null;
8704
- }
8705
8720
  /** Override to provide device-level settings UI schema. */
8706
8721
  deviceSettingsSchema() {
8707
8722
  return null;
8708
8723
  }
8709
8724
  /**
8710
- * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
8711
- * ARE the configuration of its integration.
8712
- *
8713
- * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
8714
- * operator should find on the addon's integration page (System →
8715
- * Integrations → <name>) rather than only in the cluster-wide list of every
8716
- * addon. Empty (the default) means the addon has no integration-level
8717
- * settings and no such surface is offered — this is opt-in, because whether
8718
- * an addon's configuration IS its integration's configuration depends on the
8719
- * nature of the integration.
8720
- *
8721
- * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
8722
- * the ONE global schema, in the ONE addon store, written by the ONE
8723
- * `updateGlobalSettings` path. There is deliberately no
8724
- * `updateIntegrationSettings`: a second write path is how a surface acquires
8725
- * a second store key, and this repo has shipped that twice (`btmPath@hub`,
8726
- * D266). Selecting sections cannot introduce a key that selecting cannot.
8727
- *
8728
- * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
8729
- * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
8730
- * removed with the reason recorded at
8731
- * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
8732
- * determined by WHICH schema it lives in, not by a field-level marker."* A
8733
- * marker sprinkled across sections also has to borrow a field that already
8734
- * means something else; borrowing `section.tab` put the literal word
8735
- * "integration" into an operator-facing tab bar, because `tab` means "how to
8736
- * GROUP this visually" and cannot also mean "where this lives" (D269
8737
- * supersedes D268). One declaration, in one place, next to the schema whose
8738
- * ids it names.
8739
- */
8740
- integrationSettingSections() {
8741
- return [];
8742
- }
8743
- async getGlobalSettings(overlay, cap, nodeId) {
8744
- await this.loadScopedSchemas(cap);
8745
- const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
8746
- if (!schema) return { sections: [] };
8747
- const projected = await this.readRawProjectedStore(nodeId, cap);
8748
- return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
8749
- }
8750
- /**
8751
- * The integration-level view of this addon's settings: exactly the sections
8752
- * named by {@link integrationSettingSections}, hydrated from the SAME store
8753
- * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
8754
- *
8755
- * Returns `null` when the addon declared nothing — an addon that opts out has
8756
- * no integration settings surface at all, rather than an empty one that reads
8757
- * as a failed load.
8758
- *
8759
- * Three properties hold BY CONSTRUCTION, which is why they are here in core
8760
- * and not in whichever UI happens to render this:
8761
- *
8762
- * 1. **One key.** The payload is a SUBSET of the global schema, so a field
8763
- * shown here is the same field, with the same bare key, that the addon's
8764
- * own page shows. There is no integration-specific writer — callers save
8765
- * through `updateGlobalSettings` — so a second store key is unreachable,
8766
- * not merely discouraged.
8767
- * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
8768
- * is `<key>@<nodeId>` and an integration is not a node; whichever node
8769
- * such a field silently picked would be a wrong answer for the operator
8770
- * who opened the page (D266).
8771
- * 3. **No silent typo.** A declared id that names no section throws. The
8772
- * alternative — skip it — turns a rename into a surface that quietly
8773
- * empties, which looks exactly like an addon with nothing to configure.
8774
- */
8775
- /**
8776
- * The form schema of ONE integration instance, or `null` to use {@link globalSettingsSchema}.
8725
+ * The form schema of ONE integration instance, or `null` to use the scoped form.
8777
8726
  *
8778
8727
  * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
8779
8728
  * needs its connection fields, the default integration the addon's own sections. It is the
@@ -8946,6 +8895,19 @@ var BaseAddon = class {
8946
8895
  const overlay = await this.scopedSettingsOverlay(scope, cap, nodeId);
8947
8896
  return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(stored, overlay) : stored, { inlineSecretsAsViews: true });
8948
8897
  }
8898
+ /**
8899
+ * The scoped schemas this addon serves: one entry per loaded (capability, scope), capabilities
8900
+ * in declaration order, scopes in {@link SCOPED_SETTINGS_SCOPES} order. `[]` when it declares
8901
+ * nothing.
8902
+ */
8903
+ async listScopedSettings() {
8904
+ await this.loadScopedSchemas();
8905
+ return [...this._scopedByCap].flatMap(([cap, entries]) => entries.map((entry) => ({
8906
+ cap,
8907
+ scope: entry.scope,
8908
+ sectionIds: entry.schema.sections.map((section) => section.id)
8909
+ })));
8910
+ }
8949
8911
  /** The OPERATOR's write of declared keys of one scope (D787 secret semantics). */
8950
8912
  async updateScopedSettings(scope, cap, patch, nodeId) {
8951
8913
  this._operatorWriteDepth++;
@@ -8971,32 +8933,15 @@ var BaseAddon = class {
8971
8933
  */
8972
8934
  async getIntegrationFormSchema(instance, nodeId) {
8973
8935
  const scoped = await this.integrationFormSchema(instance);
8974
- if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
8936
+ if (scoped === null) return this.scopedForm(nodeId);
8975
8937
  return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
8976
8938
  }
8977
- async getIntegrationSettings(nodeId) {
8978
- const declared = this.integrationSettingSections();
8979
- if (declared.length === 0) return null;
8980
- const schema = this.globalSettingsSchema();
8981
- if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
8982
- const byId = new Map(schema.sections.map((section) => [section.id, section]));
8983
- const sections = [];
8984
- for (const id of declared) {
8985
- const section = byId.get(id);
8986
- if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
8987
- const fields = dropPerNodeFields(section.fields);
8988
- if (fields.length === 0) continue;
8989
- sections.push({
8990
- ...section,
8991
- fields
8992
- });
8993
- }
8994
- if (sections.length === 0) return null;
8995
- const projected = await this.readRawProjectedStore(nodeId);
8996
- return hydrateSchema({
8997
- ...schema,
8998
- sections
8999
- }, projected, { inlineSecretsAsViews: true });
8939
+ /** The form of the scoped schemas this addon declared, hydrated from its store. */
8940
+ async scopedForm(nodeId) {
8941
+ await this.loadScopedSchemas();
8942
+ const schema = this.scopedFormSchema();
8943
+ if (!schema) return { sections: [] };
8944
+ return hydrateSchema(schema, await this.readRawProjectedStore(nodeId), { inlineSecretsAsViews: true });
9000
8945
  }
9001
8946
  /**
9002
8947
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
@@ -9005,8 +8950,8 @@ var BaseAddon = class {
9005
8950
  * bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
9006
8951
  * A no-op passthrough when the schema declares no `perNode` field.
9007
8952
  *
9008
- * This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
9009
- * the store for custom option logic (option narrowing, value snapping) to
8953
+ * This is the sanctioned way for an addon that needs
8954
+ * the store for custom logic (option narrowing, value snapping) to
9010
8955
  * read it per-node — never `ctx.settings.readAddonStore()` directly.
9011
8956
  * `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
9012
8957
  *
@@ -9058,7 +9003,7 @@ var BaseAddon = class {
9058
9003
  }
9059
9004
  /**
9060
9005
  * Whether ONE global key of this addon's store was written, read three-way
9061
- * (D651). For a question a hydrated view cannot answer: `getGlobalSettings`
9006
+ * (D651). For a question a hydrated view cannot answer: a hydrated form
9062
9007
  * reports `value ?? default`, so "the operator chose the default" and "nobody
9063
9008
  * chose" look the same, and a failed read looks like both. A `perNode` key is
9064
9009
  * not supported here: its stored name is scoped.
@@ -9092,7 +9037,7 @@ var BaseAddon = class {
9092
9037
  }
9093
9038
  /**
9094
9039
  * The OPERATOR's write — a settings form or an admin API call, dispatched by
9095
- * the kernel. Identical to {@link updateGlobalSettings}, except that a secret
9040
+ * the kernel. The one in-process write of the addon blob that is not a scoped write; a secret
9096
9041
  * the operator sends is stored as sent even when it equals a pre-keychain
9097
9042
  * inline value: the operator re-entering a password is a change, an addon
9098
9043
  * writing back what it read is not (D787).
@@ -9105,9 +9050,6 @@ var BaseAddon = class {
9105
9050
  this._operatorWriteDepth--;
9106
9051
  }
9107
9052
  }
9108
- async updateGlobalSettings(patch, nodeId) {
9109
- await this.applyGlobalPatch(patch, nodeId);
9110
- }
9111
9053
  /**
9112
9054
  * REMOVE stored secrets — their keychain values and their markers (D787).
9113
9055
  *
@@ -9150,8 +9092,7 @@ var BaseAddon = class {
9150
9092
  await this.resolveConfig();
9151
9093
  await this.onConfigChanged();
9152
9094
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
9153
- const legacy = this.globalSettingsSchema();
9154
- this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
9095
+ this.maybeAutoRestart(Object.keys(barePatch), this.loadedScopedSchemas().map((entry) => entry.schema));
9155
9096
  return true;
9156
9097
  }
9157
9098
  /**
@@ -9189,10 +9130,7 @@ var BaseAddon = class {
9189
9130
  this._nodeKeysByCap = /* @__PURE__ */ new Map();
9190
9131
  this._addonSecretTree = null;
9191
9132
  }
9192
- /**
9193
- * The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
9194
- * sections of its scoped schemas, so dropping the old hook does not blank the form.
9195
- */
9133
+ /** The form of an addon's scoped schemas: their sections, each shown once. */
9196
9134
  scopedFormSchema(cap) {
9197
9135
  const byId = /* @__PURE__ */ new Map();
9198
9136
  for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
@@ -9204,8 +9142,7 @@ var BaseAddon = class {
9204
9142
  return [...this._scopedByCap.values()].flat();
9205
9143
  }
9206
9144
  /**
9207
- * The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
9208
- * the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
9145
+ * The field keys that are stored per node: every field of a {@link nodeSettingsSchema}. Derived once
9209
9146
  * per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
9210
9147
  * per-node code path is bypassed and the settings API behaves like the node-agnostic one.
9211
9148
  * Call {@link loadScopedSchemas} first (the async seams do).
@@ -9214,8 +9151,7 @@ var BaseAddon = class {
9214
9151
  const cacheKey = cap ?? "";
9215
9152
  const cached = this._nodeKeysByCap.get(cacheKey);
9216
9153
  if (cached) return cached;
9217
- const legacy = this.globalSettingsSchema(cap);
9218
- const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
9154
+ const keys = /* @__PURE__ */ new Set();
9219
9155
  for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
9220
9156
  if (scope !== "node") continue;
9221
9157
  for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
@@ -9433,8 +9369,6 @@ var BaseAddon = class {
9433
9369
  }
9434
9370
  declaredKeySet() {
9435
9371
  const keys = new Set(Object.keys(this.defaults));
9436
- const schema = this.globalSettingsSchema();
9437
- if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
9438
9372
  for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
9439
9373
  for (const key of this._declaredStateKeys) keys.add(key);
9440
9374
  for (const path of this.secretStatePaths) {
@@ -9532,7 +9466,7 @@ var BaseAddon = class {
9532
9466
  /** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
9533
9467
  addonSecretTree() {
9534
9468
  if (this._addonSecretTree === null) {
9535
- const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
9469
+ const tree = /* @__PURE__ */ new Map();
9536
9470
  for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
9537
9471
  this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
9538
9472
  }
@@ -9744,47 +9678,6 @@ function classifyStoreError(err) {
9744
9678
  if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
9745
9679
  return "permanent";
9746
9680
  }
9747
- /**
9748
- * Collect the keys of every field marked `perNode: true`, recursing into
9749
- * layout containers (`group` fields and `sub-tabs` tabs) the same way
9750
- * `hydrateSchema` does. Valueless structural fields (separator/info/…)
9751
- * don't declare `perNode` and are excluded by the `in` narrowing.
9752
- */
9753
- /**
9754
- * The same fields with every `perNode: true` one removed, recursing into layout
9755
- * containers exactly as {@link collectPerNodeFieldKeys} does. A container left
9756
- * with no child is dropped rather than rendered empty.
9757
- *
9758
- * Used by `getIntegrationSettings`: an integration is not a node, so a field
9759
- * whose store key is `<key>@<nodeId>` has no node to belong to there.
9760
- */
9761
- function dropPerNodeFields(fields) {
9762
- const kept = [];
9763
- for (const field of fields) {
9764
- if (field.type === "group") {
9765
- const inner = dropPerNodeFields(field.fields);
9766
- if (inner.length > 0) kept.push({
9767
- ...field,
9768
- fields: inner
9769
- });
9770
- continue;
9771
- }
9772
- if (field.type === "sub-tabs") {
9773
- const tabs = field.tabs.map((tab) => ({
9774
- ...tab,
9775
- fields: dropPerNodeFields(tab.fields)
9776
- })).filter((tab) => tab.fields.length > 0);
9777
- if (tabs.length > 0) kept.push({
9778
- ...field,
9779
- tabs
9780
- });
9781
- continue;
9782
- }
9783
- if ("perNode" in field && field.perNode === true) continue;
9784
- kept.push(field);
9785
- }
9786
- return kept;
9787
- }
9788
9681
  /** The keys of the value-holding fields, recursing into layout containers. */
9789
9682
  function collectValueFieldKeys(fields) {
9790
9683
  const collected = [];
@@ -9817,21 +9710,6 @@ function collectFieldKeys(fields) {
9817
9710
  }
9818
9711
  return collected;
9819
9712
  }
9820
- function collectPerNodeFieldKeys(fields) {
9821
- const collected = [];
9822
- for (const field of fields) {
9823
- if (field.type === "group") {
9824
- collected.push(...collectPerNodeFieldKeys(field.fields));
9825
- continue;
9826
- }
9827
- if (field.type === "sub-tabs") {
9828
- for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
9829
- continue;
9830
- }
9831
- if ("perNode" in field && field.perNode === true) collected.push(field.key);
9832
- }
9833
- return collected;
9834
- }
9835
9713
  /**
9836
9714
  * Normalize an `ICamstackAddon.initialize()` return value into the
9837
9715
  * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
@@ -12588,13 +12466,25 @@ Object.freeze({
12588
12466
  "path": "temperature",
12589
12467
  "valueType": "number"
12590
12468
  }],
12591
- "timed-run": [{
12592
- "path": "activeUntil",
12593
- "valueType": "number"
12594
- }, {
12595
- "path": "requestedBy",
12596
- "valueType": "string"
12597
- }],
12469
+ "timed-run": [
12470
+ {
12471
+ "path": "activeUntil",
12472
+ "valueType": "number"
12473
+ },
12474
+ {
12475
+ "path": "requestedBy",
12476
+ "valueType": "string"
12477
+ },
12478
+ {
12479
+ "path": "quickRun.duration",
12480
+ "valueType": "number"
12481
+ },
12482
+ {
12483
+ "path": "quickRun.unit",
12484
+ "valueType": "enum",
12485
+ "enumValues": ["s", "min"]
12486
+ }
12487
+ ],
12598
12488
  "update": [
12599
12489
  {
12600
12490
  "path": "currentVersion",
@@ -12832,7 +12722,7 @@ function userError(code, params, englishFallback) {
12832
12722
  return new CamstackError(code, params, englishFallback);
12833
12723
  }
12834
12724
  //#endregion
12835
- //#region ../types/dist/composition-BmBHuDTQ.mjs
12725
+ //#region ../types/dist/composition-Bx0JvKUU.mjs
12836
12726
  /**
12837
12727
  * The ffmpeg build every CamStack node downloads, per platform and arch.
12838
12728
  *
@@ -13994,6 +13884,387 @@ var NodeColorIdSchema = _enum([
13994
13884
  "node-h"
13995
13885
  ]);
13996
13886
  //#endregion
13887
+ //#region ../types/dist/convert-BAzH15HU.mjs
13888
+ /**
13889
+ * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
13890
+ * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
13891
+ * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
13892
+ * decimals). K derives from `°C = K − 273.15`.
13893
+ */
13894
+ var UNIT_TABLE = {
13895
+ "°C": {
13896
+ dimension: "temperature",
13897
+ factor: 1,
13898
+ offset: 0
13899
+ },
13900
+ "°F": {
13901
+ dimension: "temperature",
13902
+ factor: 5 / 9,
13903
+ offset: -160 / 9
13904
+ },
13905
+ K: {
13906
+ dimension: "temperature",
13907
+ factor: 1,
13908
+ offset: -273.15
13909
+ },
13910
+ hPa: {
13911
+ dimension: "pressure",
13912
+ factor: 1,
13913
+ offset: 0
13914
+ },
13915
+ kPa: {
13916
+ dimension: "pressure",
13917
+ factor: 10,
13918
+ offset: 0
13919
+ },
13920
+ Pa: {
13921
+ dimension: "pressure",
13922
+ factor: .01,
13923
+ offset: 0
13924
+ },
13925
+ mbar: {
13926
+ dimension: "pressure",
13927
+ factor: 1,
13928
+ offset: 0
13929
+ },
13930
+ bar: {
13931
+ dimension: "pressure",
13932
+ factor: 1e3,
13933
+ offset: 0
13934
+ },
13935
+ inHg: {
13936
+ dimension: "pressure",
13937
+ factor: 33.8639,
13938
+ offset: 0
13939
+ },
13940
+ mmHg: {
13941
+ dimension: "pressure",
13942
+ factor: 1.33322,
13943
+ offset: 0
13944
+ },
13945
+ psi: {
13946
+ dimension: "pressure",
13947
+ factor: 68.9476,
13948
+ offset: 0
13949
+ },
13950
+ "m/s": {
13951
+ dimension: "speed",
13952
+ factor: 1,
13953
+ offset: 0
13954
+ },
13955
+ "km/h": {
13956
+ dimension: "speed",
13957
+ factor: 1 / 3.6,
13958
+ offset: 0
13959
+ },
13960
+ mph: {
13961
+ dimension: "speed",
13962
+ factor: .44704,
13963
+ offset: 0
13964
+ },
13965
+ kn: {
13966
+ dimension: "speed",
13967
+ factor: .514444,
13968
+ offset: 0
13969
+ },
13970
+ "mm/h": {
13971
+ dimension: "precipitation-rate",
13972
+ factor: 1,
13973
+ offset: 0
13974
+ },
13975
+ "in/h": {
13976
+ dimension: "precipitation-rate",
13977
+ factor: 25.4,
13978
+ offset: 0
13979
+ },
13980
+ m: {
13981
+ dimension: "length",
13982
+ factor: 1,
13983
+ offset: 0
13984
+ },
13985
+ mm: {
13986
+ dimension: "length",
13987
+ factor: .001,
13988
+ offset: 0
13989
+ },
13990
+ cm: {
13991
+ dimension: "length",
13992
+ factor: .01,
13993
+ offset: 0
13994
+ },
13995
+ km: {
13996
+ dimension: "length",
13997
+ factor: 1e3,
13998
+ offset: 0
13999
+ },
14000
+ in: {
14001
+ dimension: "length",
14002
+ factor: .0254,
14003
+ offset: 0
14004
+ },
14005
+ ft: {
14006
+ dimension: "length",
14007
+ factor: .3048,
14008
+ offset: 0
14009
+ },
14010
+ mi: {
14011
+ dimension: "length",
14012
+ factor: 1609.344,
14013
+ offset: 0
14014
+ },
14015
+ "m²": {
14016
+ dimension: "area",
14017
+ factor: 1,
14018
+ offset: 0
14019
+ },
14020
+ lx: {
14021
+ dimension: "illuminance",
14022
+ factor: 1,
14023
+ offset: 0
14024
+ },
14025
+ "W/m²": {
14026
+ dimension: "irradiance",
14027
+ factor: 1,
14028
+ offset: 0
14029
+ },
14030
+ W: {
14031
+ dimension: "power",
14032
+ factor: 1,
14033
+ offset: 0
14034
+ },
14035
+ kW: {
14036
+ dimension: "power",
14037
+ factor: 1e3,
14038
+ offset: 0
14039
+ },
14040
+ VA: {
14041
+ dimension: "apparent-power",
14042
+ factor: 1,
14043
+ offset: 0
14044
+ },
14045
+ Wh: {
14046
+ dimension: "energy",
14047
+ factor: 1,
14048
+ offset: 0
14049
+ },
14050
+ kWh: {
14051
+ dimension: "energy",
14052
+ factor: 1e3,
14053
+ offset: 0
14054
+ },
14055
+ MWh: {
14056
+ dimension: "energy",
14057
+ factor: 1e6,
14058
+ offset: 0
14059
+ },
14060
+ V: {
14061
+ dimension: "voltage",
14062
+ factor: 1,
14063
+ offset: 0
14064
+ },
14065
+ mV: {
14066
+ dimension: "voltage",
14067
+ factor: .001,
14068
+ offset: 0
14069
+ },
14070
+ A: {
14071
+ dimension: "current",
14072
+ factor: 1,
14073
+ offset: 0
14074
+ },
14075
+ mA: {
14076
+ dimension: "current",
14077
+ factor: .001,
14078
+ offset: 0
14079
+ },
14080
+ ppm: {
14081
+ dimension: "concentration-volume",
14082
+ factor: 1,
14083
+ offset: 0
14084
+ },
14085
+ ppb: {
14086
+ dimension: "concentration-volume",
14087
+ factor: .001,
14088
+ offset: 0
14089
+ },
14090
+ "µg/m³": {
14091
+ dimension: "concentration-mass",
14092
+ factor: 1,
14093
+ offset: 0
14094
+ },
14095
+ "mg/m³": {
14096
+ dimension: "concentration-mass",
14097
+ factor: 1e3,
14098
+ offset: 0
14099
+ },
14100
+ kg: {
14101
+ dimension: "mass",
14102
+ factor: 1,
14103
+ offset: 0
14104
+ },
14105
+ g: {
14106
+ dimension: "mass",
14107
+ factor: .001,
14108
+ offset: 0
14109
+ },
14110
+ lb: {
14111
+ dimension: "mass",
14112
+ factor: .453592,
14113
+ offset: 0
14114
+ },
14115
+ oz: {
14116
+ dimension: "mass",
14117
+ factor: .0283495,
14118
+ offset: 0
14119
+ },
14120
+ "%": {
14121
+ dimension: "percentage",
14122
+ factor: 1,
14123
+ offset: 0
14124
+ },
14125
+ L: {
14126
+ dimension: "volume",
14127
+ factor: 1,
14128
+ offset: 0
14129
+ },
14130
+ mL: {
14131
+ dimension: "volume",
14132
+ factor: .001,
14133
+ offset: 0
14134
+ },
14135
+ gal: {
14136
+ dimension: "volume",
14137
+ factor: 3.785411784,
14138
+ offset: 0
14139
+ },
14140
+ s: {
14141
+ dimension: "duration",
14142
+ factor: 1,
14143
+ offset: 0
14144
+ },
14145
+ ms: {
14146
+ dimension: "duration",
14147
+ factor: .001,
14148
+ offset: 0
14149
+ },
14150
+ min: {
14151
+ dimension: "duration",
14152
+ factor: 60,
14153
+ offset: 0
14154
+ },
14155
+ h: {
14156
+ dimension: "duration",
14157
+ factor: 3600,
14158
+ offset: 0
14159
+ },
14160
+ d: {
14161
+ dimension: "duration",
14162
+ factor: 86400,
14163
+ offset: 0
14164
+ },
14165
+ B: {
14166
+ dimension: "information",
14167
+ factor: 1,
14168
+ offset: 0
14169
+ },
14170
+ KiB: {
14171
+ dimension: "information",
14172
+ factor: 1024,
14173
+ offset: 0
14174
+ },
14175
+ MiB: {
14176
+ dimension: "information",
14177
+ factor: 1024 ** 2,
14178
+ offset: 0
14179
+ },
14180
+ GiB: {
14181
+ dimension: "information",
14182
+ factor: 1024 ** 3,
14183
+ offset: 0
14184
+ },
14185
+ TiB: {
14186
+ dimension: "information",
14187
+ factor: 1024 ** 4,
14188
+ offset: 0
14189
+ },
14190
+ kB: {
14191
+ dimension: "information",
14192
+ factor: 1e3,
14193
+ offset: 0
14194
+ },
14195
+ MB: {
14196
+ dimension: "information",
14197
+ factor: 1e6,
14198
+ offset: 0
14199
+ },
14200
+ GB: {
14201
+ dimension: "information",
14202
+ factor: 1e9,
14203
+ offset: 0
14204
+ },
14205
+ "°": {
14206
+ dimension: "angle",
14207
+ factor: 1,
14208
+ offset: 0
14209
+ },
14210
+ dBFS: {
14211
+ dimension: "sound-level",
14212
+ factor: 1,
14213
+ offset: 0
14214
+ },
14215
+ dBm: {
14216
+ dimension: "signal-level",
14217
+ factor: 1,
14218
+ offset: 0
14219
+ },
14220
+ mired: {
14221
+ dimension: "colour-temperature",
14222
+ factor: 1,
14223
+ offset: 0
14224
+ },
14225
+ kbps: {
14226
+ dimension: "bitrate",
14227
+ factor: 1,
14228
+ offset: 0
14229
+ },
14230
+ Mbps: {
14231
+ dimension: "bitrate",
14232
+ factor: 1e3,
14233
+ offset: 0
14234
+ },
14235
+ fps: {
14236
+ dimension: "rate",
14237
+ factor: 1,
14238
+ offset: 0
14239
+ }
14240
+ };
14241
+ /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
14242
+ function specFor(unit) {
14243
+ return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
14244
+ }
14245
+ /** Value in `spec`'s unit → its dimension's canonical unit. */
14246
+ function toCanonical(value, spec) {
14247
+ return value * spec.factor + spec.offset;
14248
+ }
14249
+ /** Canonical-unit value → the unit described by `spec`. */
14250
+ function fromCanonical(canonical, spec) {
14251
+ return (canonical - spec.offset) / spec.factor;
14252
+ }
14253
+ /**
14254
+ * Graceful variant for the render path: returns the converted value, or
14255
+ * `undefined` when the conversion is refused (unknown unit / cross-dimension).
14256
+ * Never throws — a render caller falls back to the raw value + source unit.
14257
+ */
14258
+ function tryConvertUnit(value, from, to) {
14259
+ if (from === to) return value;
14260
+ const fromSpec = specFor(from);
14261
+ if (!fromSpec) return void 0;
14262
+ const toSpec = specFor(to);
14263
+ if (!toSpec) return void 0;
14264
+ if (fromSpec.dimension !== toSpec.dimension) return void 0;
14265
+ return fromCanonical(toCanonical(value, fromSpec), toSpec);
14266
+ }
14267
+ //#endregion
13997
14268
  //#region ../types/dist/index.mjs
13998
14269
  /**
13999
14270
  * The `smtp-inbound` broker kind (D999): a mailbox a device can send mail to.
@@ -42976,6 +43247,7 @@ var ExpressionBindingSourceSchema = union([
42976
43247
  object({
42977
43248
  kind: literal("global"),
42978
43249
  sourceStableId: string(),
43250
+ addonId: string().optional(),
42979
43251
  cap: string(),
42980
43252
  fieldPath: string()
42981
43253
  })
@@ -43042,385 +43314,6 @@ var SourceInfoSchema = object({
43042
43314
  raw: record(string(), unknown()).optional()
43043
43315
  });
43044
43316
  /**
43045
- * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
43046
- * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
43047
- * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
43048
- * decimals). K derives from `°C = K − 273.15`.
43049
- */
43050
- var UNIT_TABLE = {
43051
- "°C": {
43052
- dimension: "temperature",
43053
- factor: 1,
43054
- offset: 0
43055
- },
43056
- "°F": {
43057
- dimension: "temperature",
43058
- factor: 5 / 9,
43059
- offset: -160 / 9
43060
- },
43061
- K: {
43062
- dimension: "temperature",
43063
- factor: 1,
43064
- offset: -273.15
43065
- },
43066
- hPa: {
43067
- dimension: "pressure",
43068
- factor: 1,
43069
- offset: 0
43070
- },
43071
- kPa: {
43072
- dimension: "pressure",
43073
- factor: 10,
43074
- offset: 0
43075
- },
43076
- Pa: {
43077
- dimension: "pressure",
43078
- factor: .01,
43079
- offset: 0
43080
- },
43081
- mbar: {
43082
- dimension: "pressure",
43083
- factor: 1,
43084
- offset: 0
43085
- },
43086
- bar: {
43087
- dimension: "pressure",
43088
- factor: 1e3,
43089
- offset: 0
43090
- },
43091
- inHg: {
43092
- dimension: "pressure",
43093
- factor: 33.8639,
43094
- offset: 0
43095
- },
43096
- mmHg: {
43097
- dimension: "pressure",
43098
- factor: 1.33322,
43099
- offset: 0
43100
- },
43101
- psi: {
43102
- dimension: "pressure",
43103
- factor: 68.9476,
43104
- offset: 0
43105
- },
43106
- "m/s": {
43107
- dimension: "speed",
43108
- factor: 1,
43109
- offset: 0
43110
- },
43111
- "km/h": {
43112
- dimension: "speed",
43113
- factor: 1 / 3.6,
43114
- offset: 0
43115
- },
43116
- mph: {
43117
- dimension: "speed",
43118
- factor: .44704,
43119
- offset: 0
43120
- },
43121
- kn: {
43122
- dimension: "speed",
43123
- factor: .514444,
43124
- offset: 0
43125
- },
43126
- "mm/h": {
43127
- dimension: "precipitation-rate",
43128
- factor: 1,
43129
- offset: 0
43130
- },
43131
- "in/h": {
43132
- dimension: "precipitation-rate",
43133
- factor: 25.4,
43134
- offset: 0
43135
- },
43136
- m: {
43137
- dimension: "length",
43138
- factor: 1,
43139
- offset: 0
43140
- },
43141
- mm: {
43142
- dimension: "length",
43143
- factor: .001,
43144
- offset: 0
43145
- },
43146
- cm: {
43147
- dimension: "length",
43148
- factor: .01,
43149
- offset: 0
43150
- },
43151
- km: {
43152
- dimension: "length",
43153
- factor: 1e3,
43154
- offset: 0
43155
- },
43156
- in: {
43157
- dimension: "length",
43158
- factor: .0254,
43159
- offset: 0
43160
- },
43161
- ft: {
43162
- dimension: "length",
43163
- factor: .3048,
43164
- offset: 0
43165
- },
43166
- mi: {
43167
- dimension: "length",
43168
- factor: 1609.344,
43169
- offset: 0
43170
- },
43171
- "m²": {
43172
- dimension: "area",
43173
- factor: 1,
43174
- offset: 0
43175
- },
43176
- lx: {
43177
- dimension: "illuminance",
43178
- factor: 1,
43179
- offset: 0
43180
- },
43181
- "W/m²": {
43182
- dimension: "irradiance",
43183
- factor: 1,
43184
- offset: 0
43185
- },
43186
- W: {
43187
- dimension: "power",
43188
- factor: 1,
43189
- offset: 0
43190
- },
43191
- kW: {
43192
- dimension: "power",
43193
- factor: 1e3,
43194
- offset: 0
43195
- },
43196
- VA: {
43197
- dimension: "apparent-power",
43198
- factor: 1,
43199
- offset: 0
43200
- },
43201
- Wh: {
43202
- dimension: "energy",
43203
- factor: 1,
43204
- offset: 0
43205
- },
43206
- kWh: {
43207
- dimension: "energy",
43208
- factor: 1e3,
43209
- offset: 0
43210
- },
43211
- MWh: {
43212
- dimension: "energy",
43213
- factor: 1e6,
43214
- offset: 0
43215
- },
43216
- V: {
43217
- dimension: "voltage",
43218
- factor: 1,
43219
- offset: 0
43220
- },
43221
- mV: {
43222
- dimension: "voltage",
43223
- factor: .001,
43224
- offset: 0
43225
- },
43226
- A: {
43227
- dimension: "current",
43228
- factor: 1,
43229
- offset: 0
43230
- },
43231
- mA: {
43232
- dimension: "current",
43233
- factor: .001,
43234
- offset: 0
43235
- },
43236
- ppm: {
43237
- dimension: "concentration-volume",
43238
- factor: 1,
43239
- offset: 0
43240
- },
43241
- ppb: {
43242
- dimension: "concentration-volume",
43243
- factor: .001,
43244
- offset: 0
43245
- },
43246
- "µg/m³": {
43247
- dimension: "concentration-mass",
43248
- factor: 1,
43249
- offset: 0
43250
- },
43251
- "mg/m³": {
43252
- dimension: "concentration-mass",
43253
- factor: 1e3,
43254
- offset: 0
43255
- },
43256
- kg: {
43257
- dimension: "mass",
43258
- factor: 1,
43259
- offset: 0
43260
- },
43261
- g: {
43262
- dimension: "mass",
43263
- factor: .001,
43264
- offset: 0
43265
- },
43266
- lb: {
43267
- dimension: "mass",
43268
- factor: .453592,
43269
- offset: 0
43270
- },
43271
- oz: {
43272
- dimension: "mass",
43273
- factor: .0283495,
43274
- offset: 0
43275
- },
43276
- "%": {
43277
- dimension: "percentage",
43278
- factor: 1,
43279
- offset: 0
43280
- },
43281
- L: {
43282
- dimension: "volume",
43283
- factor: 1,
43284
- offset: 0
43285
- },
43286
- mL: {
43287
- dimension: "volume",
43288
- factor: .001,
43289
- offset: 0
43290
- },
43291
- gal: {
43292
- dimension: "volume",
43293
- factor: 3.785411784,
43294
- offset: 0
43295
- },
43296
- s: {
43297
- dimension: "duration",
43298
- factor: 1,
43299
- offset: 0
43300
- },
43301
- ms: {
43302
- dimension: "duration",
43303
- factor: .001,
43304
- offset: 0
43305
- },
43306
- min: {
43307
- dimension: "duration",
43308
- factor: 60,
43309
- offset: 0
43310
- },
43311
- h: {
43312
- dimension: "duration",
43313
- factor: 3600,
43314
- offset: 0
43315
- },
43316
- d: {
43317
- dimension: "duration",
43318
- factor: 86400,
43319
- offset: 0
43320
- },
43321
- B: {
43322
- dimension: "information",
43323
- factor: 1,
43324
- offset: 0
43325
- },
43326
- KiB: {
43327
- dimension: "information",
43328
- factor: 1024,
43329
- offset: 0
43330
- },
43331
- MiB: {
43332
- dimension: "information",
43333
- factor: 1024 ** 2,
43334
- offset: 0
43335
- },
43336
- GiB: {
43337
- dimension: "information",
43338
- factor: 1024 ** 3,
43339
- offset: 0
43340
- },
43341
- TiB: {
43342
- dimension: "information",
43343
- factor: 1024 ** 4,
43344
- offset: 0
43345
- },
43346
- kB: {
43347
- dimension: "information",
43348
- factor: 1e3,
43349
- offset: 0
43350
- },
43351
- MB: {
43352
- dimension: "information",
43353
- factor: 1e6,
43354
- offset: 0
43355
- },
43356
- GB: {
43357
- dimension: "information",
43358
- factor: 1e9,
43359
- offset: 0
43360
- },
43361
- "°": {
43362
- dimension: "angle",
43363
- factor: 1,
43364
- offset: 0
43365
- },
43366
- dBFS: {
43367
- dimension: "sound-level",
43368
- factor: 1,
43369
- offset: 0
43370
- },
43371
- dBm: {
43372
- dimension: "signal-level",
43373
- factor: 1,
43374
- offset: 0
43375
- },
43376
- mired: {
43377
- dimension: "colour-temperature",
43378
- factor: 1,
43379
- offset: 0
43380
- },
43381
- kbps: {
43382
- dimension: "bitrate",
43383
- factor: 1,
43384
- offset: 0
43385
- },
43386
- Mbps: {
43387
- dimension: "bitrate",
43388
- factor: 1e3,
43389
- offset: 0
43390
- },
43391
- fps: {
43392
- dimension: "rate",
43393
- factor: 1,
43394
- offset: 0
43395
- }
43396
- };
43397
- /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
43398
- function specFor(unit) {
43399
- return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
43400
- }
43401
- /** Value in `spec`'s unit → its dimension's canonical unit. */
43402
- function toCanonical(value, spec) {
43403
- return value * spec.factor + spec.offset;
43404
- }
43405
- /** Canonical-unit value → the unit described by `spec`. */
43406
- function fromCanonical(canonical, spec) {
43407
- return (canonical - spec.offset) / spec.factor;
43408
- }
43409
- /**
43410
- * Graceful variant for the render path: returns the converted value, or
43411
- * `undefined` when the conversion is refused (unknown unit / cross-dimension).
43412
- * Never throws — a render caller falls back to the raw value + source unit.
43413
- */
43414
- function tryConvertUnit(value, from, to) {
43415
- if (from === to) return value;
43416
- const fromSpec = specFor(from);
43417
- if (!fromSpec) return void 0;
43418
- const toSpec = specFor(to);
43419
- if (!toSpec) return void 0;
43420
- if (fromSpec.dimension !== toSpec.dimension) return void 0;
43421
- return fromCanonical(toCanonical(value, fromSpec), toSpec);
43422
- }
43423
- /**
43424
43317
  * A number a person types for a physical quantity — the input side of the
43425
43318
  * value + unit contract (D972).
43426
43319
  *
@@ -45099,26 +44992,23 @@ var SettingsSchemaWithValuesSchema = object({
45099
44992
  var SettingsPatchSchema = record(string(), unknown());
45100
44993
  /** The scope of a scoped-settings call (D986): where the capability declared the values. */
45101
44994
  var ScopedSettingsScopeSchema = _enum(SCOPED_SETTINGS_SCOPES);
44995
+ /** One scoped schema an addon serves: its capability, scope and section ids. */
44996
+ var ScopedSettingsDeclarationSchema = object({
44997
+ cap: string(),
44998
+ scope: ScopedSettingsScopeSchema,
44999
+ sectionIds: array(string())
45000
+ });
45102
45001
  /** Standard success response for update operations. */
45103
45002
  var SettingsUpdateResultSchema = object({ success: literal(true) });
45104
45003
  method(object({
45105
- addonId: string(),
45106
- nodeId: string().optional(),
45107
- overlay: record(string(), unknown()).optional(),
45108
- cap: string().optional()
45109
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45110
- addonId: string(),
45111
- nodeId: string().optional(),
45112
- patch: SettingsPatchSchema
45113
- }), SettingsUpdateResultSchema, {
45114
- kind: "mutation",
45115
- auth: "admin"
45116
- }), method(object({
45117
45004
  addonId: string(),
45118
45005
  cap: string(),
45119
45006
  scope: ScopedSettingsScopeSchema,
45120
45007
  nodeId: string().optional()
45121
45008
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45009
+ addonId: string(),
45010
+ nodeId: string().optional()
45011
+ }), array(ScopedSettingsDeclarationSchema).nullable()), method(object({
45122
45012
  addonId: string(),
45123
45013
  cap: string(),
45124
45014
  scope: ScopedSettingsScopeSchema,
@@ -45133,9 +45023,6 @@ method(object({
45133
45023
  scope: ScopedSettingsScopeSchema,
45134
45024
  nodeId: string().optional()
45135
45025
  }), record(string(), unknown()).nullable()), method(object({
45136
- addonId: string(),
45137
- nodeId: string().optional()
45138
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45139
45026
  addonId: string(),
45140
45027
  nodeId: string().optional(),
45141
45028
  instance: IntegrationInstanceRefSchema
@@ -46475,7 +46362,7 @@ var BackupDestinationInfoSchema = object({
46475
46362
  id: string(),
46476
46363
  /**
46477
46364
  * Manifest id of the owning addon. The Settings button on the
46478
- * destination card opens that addon's `globalSettingsSchema` panel.
46365
+ * destination card opens that addon's scoped settings panel.
46479
46366
  */
46480
46367
  addonId: string(),
46481
46368
  /** Operator-toggled enable flag from the destination-policy table. */
@@ -49074,7 +48961,7 @@ DeviceType.MediaPlayer, method(deviceOnly, _void(), {
49074
48961
  kind: "mutation",
49075
48962
  auth: "protected"
49076
48963
  });
49077
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater;
48964
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.MediaPlayer;
49078
48965
  var NUMBER = {
49079
48966
  kind: "fixed",
49080
48967
  result: ["number"]
@@ -50945,6 +50832,7 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
50945
50832
  kind: "mutation",
50946
50833
  auth: "admin"
50947
50834
  });
50835
+ var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
50948
50836
  /**
50949
50837
  * A SITE ZONE — a named part of the property that several locations belong to.
50950
50838
  *
@@ -51338,6 +51226,12 @@ var DeviceInfoSchema = object({
51338
51226
  addonId: string(),
51339
51227
  type: _enum(DeviceType),
51340
51228
  name: string(),
51229
+ /** The name a value template uses for this device (`{{ stufa_veranda.temperature }}`),
51230
+ * entity_id style. Generated once from the name at first registration, unique
51231
+ * cluster-wide, editable through `setAlias`, NOT changed by a rename (D1021).
51232
+ * Optional: a record produced by an older build omits it until the boot
51233
+ * backfill has run. */
51234
+ alias: DeviceAliasSchema.optional(),
51341
51235
  /** Operator-organisational location label. `null` when unset. */
51342
51236
  location: string().nullable(),
51343
51237
  /** Soft-disabled flag. */
@@ -51657,6 +51551,25 @@ method(object({
51657
51551
  }), _void(), {
51658
51552
  kind: "mutation",
51659
51553
  auth: "admin"
51554
+ }), method(object({
51555
+ deviceId: number(),
51556
+ alias: string()
51557
+ }), discriminatedUnion("ok", [object({
51558
+ ok: literal(true),
51559
+ alias: DeviceAliasSchema
51560
+ }), object({
51561
+ ok: literal(false),
51562
+ reason: _enum([
51563
+ "empty",
51564
+ "too-long",
51565
+ "grammar",
51566
+ "reserved",
51567
+ "taken"
51568
+ ]),
51569
+ takenBy: number().optional()
51570
+ })]), {
51571
+ kind: "mutation",
51572
+ auth: "admin"
51660
51573
  }), method(object({
51661
51574
  deviceId: number(),
51662
51575
  location: string().nullable()
@@ -54778,10 +54691,11 @@ var IntegrationInstanceDescriptionSchema = object({
54778
54691
  /** True when `testInstance` really probes the remote. */
54779
54692
  canTest: boolean(),
54780
54693
  /**
54781
- * The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
54782
- * Add flow asks which first and sends it as `createInstance.kind`.
54694
+ * The kinds an instance can be of (D1013 rule 1), each naming its `functionCap`. One entry:
54695
+ * nothing to choose; more than one: the Add flow asks which first and sends it as
54696
+ * `createInstance.kind`. Never empty: every instance belongs to exactly one kind.
54783
54697
  */
54784
- kinds: array(IntegrationInstanceKindSchema).optional(),
54698
+ kinds: array(IntegrationInstanceKindSchema).min(1),
54785
54699
  /**
54786
54700
  * The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
54787
54701
  * devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
@@ -54817,8 +54731,8 @@ var IntegrationInstanceSchema = object({
54817
54731
  * the body is the one store, `setInstanceEnabled` the one writer (epic #440, step 3).
54818
54732
  */
54819
54733
  enabled: boolean().optional(),
54820
- /** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
54821
- kind: string().optional(),
54734
+ /** The kind of this instance: exactly one of `describe().kinds[].id` (D1013 rule 2). */
54735
+ kind: string().min(1),
54822
54736
  /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
54823
54737
  formValues: record(string(), unknown()),
54824
54738
  status: IntegrationInstanceStatusSchema.optional(),
@@ -64388,6 +64302,11 @@ var NcScheduleSchema = object({
64388
64302
  * as a STATE SUMMARY at the end of each window of `schedule`.
64389
64303
  */
64390
64304
  var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
64305
+ _enum([
64306
+ "activation",
64307
+ "deactivation",
64308
+ "schedule"
64309
+ ]);
64391
64310
  /**
64392
64311
  * How a summary window is SENT (D698): `rule` (absent) = ONE notification with
64393
64312
  * every section; `section` = one notification per section with something to
@@ -64404,11 +64323,26 @@ var NcStateSummarySplitSchema = _enum(["rule", "section"]);
64404
64323
  * midnight crossing); the send time is each window's END. Refused on the
64405
64324
  * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
64406
64325
  */
64407
- var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
64408
- mode: literal("summary"),
64409
- schedule: NcScheduleSchema,
64410
- split: NcStateSummarySplitSchema.optional()
64411
- })]);
64326
+ var NcSystemEventDeliverySchema = discriminatedUnion("mode", [
64327
+ object({ mode: literal("instant") }),
64328
+ object({
64329
+ mode: literal("summary"),
64330
+ schedule: NcScheduleSchema,
64331
+ split: NcStateSummarySplitSchema.optional()
64332
+ }),
64333
+ object({
64334
+ mode: literal("modes"),
64335
+ /** The bad edge, and every kind that has a single edge. */
64336
+ onActivation: boolean(),
64337
+ /** The recovery edges of the classes of the selected kinds (derived, never stored per kind). */
64338
+ onDeactivation: boolean(),
64339
+ /** A state summary at the end of each window; absent = no summary. */
64340
+ onSchedule: object({
64341
+ schedule: NcScheduleSchema,
64342
+ grouping: NcStateSummarySplitSchema
64343
+ }).optional()
64344
+ })
64345
+ ]);
64412
64346
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
64413
64347
  var NcPlateMatcherSchema = object({
64414
64348
  /**
@@ -71735,6 +71669,7 @@ var RemoteLayoutComponentSchema = object({
71735
71669
  });
71736
71670
  });
71737
71671
  method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
71672
+ DeviceType.MediaPlayer;
71738
71673
  var RunningStateSourceSchema = _enum(["native", "assigned"]);
71739
71674
  object({
71740
71675
  /** null = unknown — never coerced to false (D393). */
@@ -74269,6 +74204,11 @@ var TimedRunUnitSchema = _enum(["s", "min"]);
74269
74204
  _enum(["switch", "pet-fountain"]);
74270
74205
  DeviceType.Heater, DeviceType.PetFountain;
74271
74206
  DeviceType.Heater, DeviceType.PetFountain;
74207
+ /** One duration, as the one-tap run asks for it. */
74208
+ var TimedRunQuickRunSchema = object({
74209
+ duration: number().positive().finite(),
74210
+ unit: TimedRunUnitSchema
74211
+ });
74272
74212
  /** Why a request was not carried out. Typed, so a caller can say it in words. */
74273
74213
  var TimedRunRefusalSchema = _enum([
74274
74214
  "unsupported-device",
@@ -74289,9 +74229,30 @@ object({
74289
74229
  activeUntil: number().nullable(),
74290
74230
  /** Who asked for the run (a user id or an addon id); `null` when nothing is armed or it did not say. */
74291
74231
  requestedBy: string().nullable(),
74232
+ /**
74233
+ * The one-tap run of THIS device: what a "Run" button on any surface starts. `null` when
74234
+ * the device is gone or its type has no timed run; a status from before this field
74235
+ * existed has none, and a surface falls back to the type's default.
74236
+ */
74237
+ quickRun: TimedRunQuickRunSchema.nullable(),
74292
74238
  lastFetchedAt: number()
74293
74239
  });
74294
- method(object({
74240
+ /** What the settings form needs: the unit, the bounds and the type's default. */
74241
+ var TimedRunOptionsSchema = object({
74242
+ unit: TimedRunUnitSchema,
74243
+ /** The longest run, in `unit`. */
74244
+ maxDuration: number(),
74245
+ defaultDuration: number()
74246
+ });
74247
+ /** The one-tap run duration, in the type's unit; `null` returns to the type's default. */
74248
+ var TimedRunSettingsPatchSchema = object({ quickRunDuration: number().positive().finite().nullable().optional() });
74249
+ method(object({ deviceId: number().int() }), TimedRunOptionsSchema), method(object({
74250
+ deviceId: number().int(),
74251
+ settings: TimedRunSettingsPatchSchema
74252
+ }), _void(), {
74253
+ kind: "mutation",
74254
+ auth: "admin"
74255
+ }), method(object({
74295
74256
  deviceId: number().int(),
74296
74257
  duration: number().positive().finite(),
74297
74258
  unit: TimedRunUnitSchema
@@ -76997,13 +76958,6 @@ Object.freeze({
76997
76958
  access: "view",
76998
76959
  role: "admin"
76999
76960
  },
77000
- "addonSettings.getGlobalSettings": {
77001
- capName: "addon-settings",
77002
- capScope: "system",
77003
- addonId: null,
77004
- access: "view",
77005
- role: "admin"
77006
- },
77007
76961
  "addonSettings.getIntegrationFormSchema": {
77008
76962
  capName: "addon-settings",
77009
76963
  capScope: "system",
@@ -77011,14 +76965,14 @@ Object.freeze({
77011
76965
  access: "view",
77012
76966
  role: "admin"
77013
76967
  },
77014
- "addonSettings.getIntegrationSettings": {
76968
+ "addonSettings.getScopedSettings": {
77015
76969
  capName: "addon-settings",
77016
76970
  capScope: "system",
77017
76971
  addonId: null,
77018
76972
  access: "view",
77019
76973
  role: "admin"
77020
76974
  },
77021
- "addonSettings.getScopedSettings": {
76975
+ "addonSettings.listScopedSettings": {
77022
76976
  capName: "addon-settings",
77023
76977
  capScope: "system",
77024
76978
  addonId: null,
@@ -77046,13 +77000,6 @@ Object.freeze({
77046
77000
  access: "create",
77047
77001
  role: "admin"
77048
77002
  },
77049
- "addonSettings.updateGlobalSettings": {
77050
- capName: "addon-settings",
77051
- capScope: "system",
77052
- addonId: null,
77053
- access: "create",
77054
- role: "admin"
77055
- },
77056
77003
  "addonSettings.updateScopedSettings": {
77057
77004
  capName: "addon-settings",
77058
77005
  capScope: "system",
@@ -79223,6 +79170,13 @@ Object.freeze({
79223
79170
  access: "create",
79224
79171
  role: "admin"
79225
79172
  },
79173
+ "deviceManager.setAlias": {
79174
+ capName: "device-manager",
79175
+ capScope: "system",
79176
+ addonId: null,
79177
+ access: "create",
79178
+ role: "admin"
79179
+ },
79226
79180
  "deviceManager.setChildLayout": {
79227
79181
  capName: "device-manager",
79228
79182
  capScope: "system",
@@ -86006,6 +85960,13 @@ Object.freeze({
86006
85960
  access: "create",
86007
85961
  role: "viewer"
86008
85962
  },
85963
+ "timedRun.getOptions": {
85964
+ capName: "timed-run",
85965
+ capScope: "device",
85966
+ addonId: null,
85967
+ access: "view",
85968
+ role: "viewer"
85969
+ },
86009
85970
  "timedRun.getStatus": {
86010
85971
  capName: "timed-run",
86011
85972
  capScope: "device",
@@ -86020,6 +85981,13 @@ Object.freeze({
86020
85981
  access: "create",
86021
85982
  role: "viewer"
86022
85983
  },
85984
+ "timedRun.setSettings": {
85985
+ capName: "timed-run",
85986
+ capScope: "device",
85987
+ addonId: null,
85988
+ access: "create",
85989
+ role: "admin"
85990
+ },
86023
85991
  "toast.onToast": {
86024
85992
  capName: "toast",
86025
85993
  capScope: "system",
@@ -86950,6 +86918,7 @@ function offered(received) {
86950
86918
  };
86951
86919
  }
86952
86920
  offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
86921
+ string().min(1).max(4096);
86953
86922
  Object.freeze({
86954
86923
  "accessories.getStatus": [{
86955
86924
  name: "deviceId",
@@ -87633,6 +87602,11 @@ Object.freeze({
87633
87602
  form: "single",
87634
87603
  optional: false
87635
87604
  }],
87605
+ "deviceManager.setAlias": [{
87606
+ name: "deviceId",
87607
+ form: "single",
87608
+ optional: false
87609
+ }],
87636
87610
  "deviceManager.setChildLayout": [{
87637
87611
  name: "deviceId",
87638
87612
  form: "single",
@@ -89697,6 +89671,11 @@ Object.freeze({
89697
89671
  form: "single",
89698
89672
  optional: false
89699
89673
  }],
89674
+ "timedRun.getOptions": [{
89675
+ name: "deviceId",
89676
+ form: "single",
89677
+ optional: false
89678
+ }],
89700
89679
  "timedRun.getStatus": [{
89701
89680
  name: "deviceId",
89702
89681
  form: "single",
@@ -89707,6 +89686,11 @@ Object.freeze({
89707
89686
  form: "single",
89708
89687
  optional: false
89709
89688
  }],
89689
+ "timedRun.setSettings": [{
89690
+ name: "deviceId",
89691
+ form: "single",
89692
+ optional: false
89693
+ }],
89710
89694
  "update.getStatus": [{
89711
89695
  name: "deviceId",
89712
89696
  form: "single",
@@ -90569,6 +90553,7 @@ var STATE_SUMMARY_VARS = [
90569
90553
  [...RULE_TEXT],
90570
90554
  [...RULE_TEXT],
90571
90555
  [...RULE_TEXT],
90556
+ [...RULE_TEXT],
90572
90557
  [...RULE_BODY_ONLY],
90573
90558
  [...RULE_TEXT],
90574
90559
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
@@ -136224,7 +136209,14 @@ function buildMailboxInstances(records, deps) {
136224
136209
  role: "services",
136225
136210
  instanceMode: "multiple",
136226
136211
  kind: "mailbox",
136227
- canTest: true
136212
+ canTest: true,
136213
+ kinds: [{
136214
+ id: MAILBOX_INSTANCE_KIND,
136215
+ label: "Inbound mailbox (SMTP)",
136216
+ functionCap: "broker",
136217
+ role: "services",
136218
+ brokerKind: SMTP_INBOUND_KIND
136219
+ }]
136228
136220
  },
136229
136221
  formSchema: () => mailboxSettingsSchema(),
136230
136222
  initialBody: (name) => newMailboxBody(name),
@@ -145960,7 +145952,13 @@ var SmtpNodemailerAddon = class extends BaseAddon {
145960
145952
  role: "services",
145961
145953
  instanceMode: "multiple",
145962
145954
  kind: "relays",
145963
- canTest: true
145955
+ canTest: true,
145956
+ kinds: [{
145957
+ id: "relays",
145958
+ label: "SMTP relay",
145959
+ functionCap: "smtp-provider",
145960
+ role: "services"
145961
+ }]
145964
145962
  },
145965
145963
  formSchema: () => this.relayForm(),
145966
145964
  initialBody: (name) => ({