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