@camstack/addon-smtp-nodemailer 1.2.151 → 1.2.152

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.
@@ -36,7 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
38
38
  //#endregion
39
- //#region ../types/dist/event-category-BmPm98n0.mjs
39
+ //#region ../types/dist/event-category-78tcBemo.mjs
40
40
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
41
41
  EventCategory["SystemBoot"] = "system.boot";
42
42
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -157,6 +157,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
157
157
  */
158
158
  EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
159
159
  /**
160
+ * An addon is RUNNING although its installed package is below what the
161
+ * running server requires (`camstack.requires.addons` of the server, or the
162
+ * addon's own `requires.server`). Raised once per streak by the unsupported-
163
+ * addon tracker, on the hub and on agents; AlertCenter turns it into one
164
+ * operator alert per node and package. The addon is never held back for it.
165
+ *
166
+ * Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
167
+ */
168
+ EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
169
+ /**
170
+ * The package of an {@link AddonUnsupportedVersion} alert reached a supported
171
+ * version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
172
+ *
173
+ * Payload: `{ packageName, addonId, nodeId }`.
174
+ */
175
+ EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
176
+ /**
160
177
  * Monitor scheduled the next retry for a failed addon. Transient —
161
178
  * surfaced to the UI for live-updating the "next retry in Ns"
162
179
  * countdown on the Addons page row, NOT persisted as an alert.
@@ -267,7 +284,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
267
284
  EventCategory["ProcessRestarted"] = "process.restarted";
268
285
  /**
269
286
  * The SET of storage locations changed — one was created, edited, enabled,
270
- * disabled or deleted through `storage.upsertLocation` / `deleteLocation`.
287
+ * disabled or deleted through the location instances or the forced-delete action.
271
288
  *
272
289
  * Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
273
290
  * it must also converge on its own periodic path, because a dropped event
@@ -6018,7 +6035,7 @@ var ZodIssueCode = {
6018
6035
  var ZodFirstPartyTypeKind;
6019
6036
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6020
6037
  //#endregion
6021
- //#region ../types/dist/sleep-D7ZKm4nb.mjs
6038
+ //#region ../types/dist/sleep-CQP-REJp.mjs
6022
6039
  /**
6023
6040
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6024
6041
  * window to float samples (D455).
@@ -6382,6 +6399,59 @@ Object.fromEntries([
6382
6399
  function isValuelessField(field) {
6383
6400
  return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button" || field.type === "account-sign-in" || field.type === "secret-presence";
6384
6401
  }
6402
+ /**
6403
+ * Base collector: flatten a hydrated `ConfigUISchemaWithValues` into
6404
+ * `key → { value, default }`, recursing through `group` and `sub-tabs`
6405
+ * layout fields. Keeping `value` and `default` SEPARATE (rather than
6406
+ * pre-resolving) lets callers both read the effective value AND detect
6407
+ * overrides (`value` differs from `default`). Structural over `unknown`;
6408
+ * malformed input yields an empty map, never throws.
6409
+ */
6410
+ function collectHydratedFieldEntries(schema) {
6411
+ const out = /* @__PURE__ */ new Map();
6412
+ if (typeof schema !== "object" || schema === null) return out;
6413
+ const sections = schema.sections;
6414
+ if (!Array.isArray(sections)) return out;
6415
+ for (const section of sections) if (typeof section === "object" && section !== null) collectFieldEntriesInto(section.fields, out);
6416
+ return out;
6417
+ }
6418
+ /** Recursive field walker for {@link collectHydratedFieldEntries}. */
6419
+ function collectFieldEntriesInto(fields, into) {
6420
+ if (!Array.isArray(fields)) return;
6421
+ for (const field of fields) {
6422
+ if (typeof field !== "object" || field === null) continue;
6423
+ const type = field.type;
6424
+ if (type === "group") {
6425
+ collectFieldEntriesInto(field.fields, into);
6426
+ continue;
6427
+ }
6428
+ if (type === "sub-tabs") {
6429
+ const tabs = field.tabs;
6430
+ if (Array.isArray(tabs)) {
6431
+ for (const tab of tabs) if (typeof tab === "object" && tab !== null) collectFieldEntriesInto(tab.fields, into);
6432
+ }
6433
+ continue;
6434
+ }
6435
+ const key = field.key;
6436
+ if (typeof key !== "string" || key.length === 0) continue;
6437
+ if (!("value" in field)) continue;
6438
+ into.set(key, {
6439
+ value: field.value,
6440
+ default: field.default
6441
+ });
6442
+ }
6443
+ }
6444
+ /**
6445
+ * Flatten a hydrated settings payload into `key → EFFECTIVE value`
6446
+ * (`value ?? default`). The read-side inverse of `hydrateSchema` for the
6447
+ * common "just give me the resolved values" case. Built on
6448
+ * {@link collectHydratedFieldEntries}.
6449
+ */
6450
+ function collectHydratedFieldValues(schema) {
6451
+ const out = /* @__PURE__ */ new Map();
6452
+ for (const [key, entry] of collectHydratedFieldEntries(schema)) out.set(key, entry.value ?? entry.default);
6453
+ return out;
6454
+ }
6385
6455
  function hydrateSchema(schema, values, options = {}) {
6386
6456
  return {
6387
6457
  ...schema.tabs ? { tabs: [...schema.tabs] } : {},
@@ -7266,17 +7336,35 @@ var RecordIntegrationInstances = class {
7266
7336
  ...kind !== void 0 ? { kind } : {}
7267
7337
  });
7268
7338
  if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
7339
+ const initial = this.hooks.initialBody(name, kind);
7340
+ const patch = this.patchOf(values ?? {});
7341
+ if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
7342
+ kind,
7343
+ body: {
7344
+ ...initial,
7345
+ ...patch
7346
+ }
7347
+ }));
7269
7348
  await this.handle.put({
7270
7349
  id: minted,
7271
- data: this.hooks.initialBody(name, kind)
7350
+ data: initial
7272
7351
  }, { ifRev: 0 });
7273
- const patch = this.patchOf(values ?? {});
7274
7352
  if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
7275
7353
  await this.hooks.onChanged?.(minted, "created");
7276
7354
  return { id: minted };
7277
7355
  };
7278
7356
  updateInstance = async ({ id, name, values }) => {
7279
7357
  const patch = this.patchOf(values);
7358
+ if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
7359
+ const stored = await this.handle.get(id);
7360
+ if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
7361
+ kind: void 0,
7362
+ body: {
7363
+ ...stored.data,
7364
+ ...patch
7365
+ }
7366
+ }));
7367
+ }
7280
7368
  if (name !== void 0) patch[this.nameField] = name;
7281
7369
  await this.handle.patch(id, patch);
7282
7370
  await this.hooks.onChanged?.(id, "updated");
@@ -7587,6 +7675,21 @@ var SCOPED_SETTINGS_SCOPES = [
7587
7675
  "device-defaults",
7588
7676
  "node"
7589
7677
  ];
7678
+ /** The one refusal of a key a scope did not declare. */
7679
+ function assertDeclaredKeys(keys, declared) {
7680
+ const undeclared = keys.filter((key) => !declared.has(key));
7681
+ if (undeclared.length > 0) throw new Error(`scoped settings: [${undeclared.join(", ")}] not declared for this scope (declared: [${[...declared].join(", ")}])`);
7682
+ }
7683
+ /** Build the handle over an addon's scoped read, write and unset. */
7684
+ function createScopedSettings(deps) {
7685
+ return {
7686
+ async get() {
7687
+ return deps.schema.parse(await deps.read());
7688
+ },
7689
+ patch: (patch) => deps.write(Object.fromEntries(Object.entries(patch))),
7690
+ unset: (keys) => deps.unset(keys)
7691
+ };
7692
+ }
7590
7693
  /** A write to a secret path could not be stored in the keychain, so it was refused. */
7591
7694
  var KeychainWriteRefusedError = class extends Error {
7592
7695
  fieldPath;
@@ -8162,7 +8265,16 @@ function createHandle(raw, opts) {
8162
8265
  return blob;
8163
8266
  }
8164
8267
  },
8165
- writeAddonStore: writeAddon
8268
+ writeAddonStore: writeAddon,
8269
+ removeClearedKeys: (keys) => serialize("addon", async () => {
8270
+ const stored = await readStoredAddonBlob(raw);
8271
+ index.seen("addon", stored);
8272
+ const removable = keys.filter((key) => collectSecretMarkerRefs(stored[key]).length === 0);
8273
+ if (removable.length === 0) return;
8274
+ const patch = Object.fromEntries(removable.map((key) => [key, void 0]));
8275
+ await raw.writeAddonStore(patch);
8276
+ index.wrote("addon", patch);
8277
+ })
8166
8278
  };
8167
8279
  }
8168
8280
  /** The context the addon sees, plus the private raw view and write outcome. */
@@ -8707,6 +8819,151 @@ var BaseAddon = class {
8707
8819
  }
8708
8820
  }
8709
8821
  /**
8822
+ * Hook: values layered over the stored ones when the FORM of one scope is served
8823
+ * ({@link getScopedSettings}) — a preview that derives dependent options without writing.
8824
+ * Never applied to {@link ScopedSettings.get} or to a write. `null` ⇒ no overlay.
8825
+ */
8826
+ async scopedSettingsOverlay(_scope, _cap, _nodeId) {
8827
+ return null;
8828
+ }
8829
+ /**
8830
+ * Hook: a write of one scope changed stored values (not called when the store reports no change,
8831
+ * e.g. the write-back of a stored secret). `keys` are the bare field keys of the write, node keys included.
8832
+ */
8833
+ async onScopedSettingsChanged(_scope, _cap, _keys) {}
8834
+ /** Typed handle over the CLUSTER settings `cap` declares (`clusterSettingsSchema`). */
8835
+ clusterSettings(cap, schema) {
8836
+ return this.scopedHandle("cluster", cap, schema);
8837
+ }
8838
+ /** Typed handle over the fleet defaults of the per-device settings `cap` declares. */
8839
+ deviceDefaults(cap, schema) {
8840
+ return this.scopedHandle("device-defaults", cap, schema);
8841
+ }
8842
+ /**
8843
+ * Typed handle over the settings `cap` owns PER NODE. `nodeId` omitted ⇒ the local node. A node
8844
+ * with no row yields the schema defaults — never a bare-key fallback.
8845
+ */
8846
+ nodeSettings(cap, schema, nodeId) {
8847
+ return this.scopedHandle("node", cap, schema, nodeId);
8848
+ }
8849
+ /**
8850
+ * {@link state} for a value that is kept per node: stored under `<key>@<nodeId>`, and the
8851
+ * settings store carries `nodeId` in the row data (D986 §1), so a forgotten node's rows go in one
8852
+ * central delete. `nodeId` omitted ⇒ the local node.
8853
+ */
8854
+ nodeState(key, schema, fallback, nodeId) {
8855
+ this._declaredStateKeys.add(key);
8856
+ return createDurableState({
8857
+ key: nodeScopedKey(key, nodeId ?? this._ctx?.kernel?.localNodeId ?? ""),
8858
+ schema,
8859
+ fallback,
8860
+ read: async () => this.resolveStoredSecrets(await this.readAddonStoreWithRetry()),
8861
+ write: async (patch) => {
8862
+ await this._ctx?.settings?.writeAddonStore(patch);
8863
+ },
8864
+ onParseError: (k, e) => this._ctx?.logger?.warn?.(`durable-state: stored "${k}" failed validation — using fallback`, { meta: {
8865
+ addonId: this._ctx?.id,
8866
+ key: k,
8867
+ error: String(e)
8868
+ } })
8869
+ });
8870
+ }
8871
+ /** The loaded schema `cap` declares for `scope`, or `null`. */
8872
+ async declaredScopedSchema(scope, cap) {
8873
+ await this.loadScopedSchemas(cap);
8874
+ return this.loadedScopedSchemas(cap).find((entry) => entry.scope === scope)?.schema ?? null;
8875
+ }
8876
+ async requireScopedSchema(scope, cap) {
8877
+ const schema = await this.declaredScopedSchema(scope, cap);
8878
+ if (schema === null) throw new Error(`${this.constructor.name}: cap "${cap}" declares no ${scope} settings`);
8879
+ return schema;
8880
+ }
8881
+ scopedKeys(schema) {
8882
+ return new Set(schema.sections.flatMap((section) => collectValueFieldKeys(section.fields)));
8883
+ }
8884
+ /** `store` narrowed to the keys `schema` declares. */
8885
+ narrowToScope(store, schema) {
8886
+ const keys = this.scopedKeys(schema);
8887
+ return Object.fromEntries(Object.entries(store).filter(([key]) => keys.has(key)));
8888
+ }
8889
+ scopedHandle(scope, cap, schema, nodeId) {
8890
+ return createScopedSettings({
8891
+ schema,
8892
+ read: async () => {
8893
+ const declared = await this.requireScopedSchema(scope, cap);
8894
+ const stored = this.narrowToScope(await this.resolveGlobalStore(nodeId, cap), declared);
8895
+ const effective = collectHydratedFieldValues(hydrateSchema(declared, stored));
8896
+ return {
8897
+ ...Object.fromEntries([...effective].filter(([, value]) => value !== void 0)),
8898
+ ...stored
8899
+ };
8900
+ },
8901
+ write: (patch) => this.writeScoped(scope, cap, patch, nodeId),
8902
+ unset: (keys) => this.unsetScoped(scope, cap, keys, nodeId)
8903
+ });
8904
+ }
8905
+ /** The one write path of the scoped API: declared keys only, then the global patch. */
8906
+ async writeScoped(scope, cap, patch, nodeId) {
8907
+ assertDeclaredKeys(Object.keys(patch), this.scopedKeys(await this.requireScopedSchema(scope, cap)));
8908
+ if (await this.applyGlobalPatch(patch, scope === "node" ? nodeId : void 0, cap)) await this.onScopedSettingsChanged(scope, cap, Object.keys(patch));
8909
+ }
8910
+ /**
8911
+ * Remove declared keys of one scope. A secret goes through {@link clearSecrets} — its keychain
8912
+ * value is released exactly as there — and the emptied row is then removed; every other key is
8913
+ * removed by an `undefined` patch (which a secret transform would read as "keep").
8914
+ */
8915
+ async unsetScoped(scope, cap, keys, nodeId) {
8916
+ assertDeclaredKeys(keys, this.scopedKeys(await this.requireScopedSchema(scope, cap)));
8917
+ const target = scope === "node" ? nodeId : void 0;
8918
+ const tree = this.addonSecretTree();
8919
+ const secretKeys = keys.filter((key) => tree.has(key));
8920
+ const plainKeys = keys.filter((key) => !tree.has(key));
8921
+ let changed = false;
8922
+ if (plainKeys.length > 0) {
8923
+ const patch = Object.fromEntries(plainKeys.map((key) => [key, void 0]));
8924
+ changed = await this.applyGlobalPatch(patch, target, cap) || changed;
8925
+ }
8926
+ if (secretKeys.length > 0) {
8927
+ await this.clearSecretKeys(secretKeys, target, cap);
8928
+ const node = normalizeNodeId(target ?? this._ctx?.kernel?.localNodeId);
8929
+ const rows = secretKeys.map((key) => scope === "node" ? nodeScopedKey(key, node) : key);
8930
+ if (this._secretSettings !== null) await this._secretSettings.removeClearedKeys(rows);
8931
+ else await this._ctx?.settings?.writeAddonStore(Object.fromEntries(rows.map((row) => [row, void 0])));
8932
+ changed = true;
8933
+ }
8934
+ if (changed) await this.onScopedSettingsChanged(scope, cap, [...keys]);
8935
+ }
8936
+ /**
8937
+ * The hydrated form of one scope of one capability's settings (any
8938
+ * {@link scopedSettingsOverlay} applied), or `null` when the capability declares nothing there.
8939
+ */
8940
+ async getScopedSettings(scope, cap, nodeId) {
8941
+ const schema = await this.declaredScopedSchema(scope, cap);
8942
+ if (schema === null) return null;
8943
+ const stored = this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema);
8944
+ const overlay = await this.scopedSettingsOverlay(scope, cap, nodeId);
8945
+ return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(stored, overlay) : stored, { inlineSecretsAsViews: true });
8946
+ }
8947
+ /** The OPERATOR's write of declared keys of one scope (D787 secret semantics). */
8948
+ async updateScopedSettings(scope, cap, patch, nodeId) {
8949
+ this._operatorWriteDepth++;
8950
+ try {
8951
+ await this.writeScoped(scope, cap, patch, nodeId);
8952
+ } finally {
8953
+ this._operatorWriteDepth--;
8954
+ }
8955
+ }
8956
+ /**
8957
+ * The effective values (`stored ?? default`) of one scope, a secret as its `set` view — never
8958
+ * its value. `null` when the capability declares nothing for the scope.
8959
+ */
8960
+ async readScopedValues(scope, cap, nodeId) {
8961
+ const schema = await this.declaredScopedSchema(scope, cap);
8962
+ if (schema === null) return null;
8963
+ const form = hydrateSchema(schema, this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema), { inlineSecretsAsViews: true });
8964
+ return Object.fromEntries([...collectHydratedFieldValues(form)].filter(([, value]) => value !== void 0));
8965
+ }
8966
+ /**
8710
8967
  * What the create wizard and the integration edit page render for one instance: the addon's
8711
8968
  * per-instance schema when it declares one, else its global schema (what they always showed).
8712
8969
  */
@@ -8858,6 +9115,9 @@ var BaseAddon = class {
8858
9115
  * a keychain (an older kernel) it writes `''`, as such code always did.
8859
9116
  */
8860
9117
  async clearSecrets(keys, nodeId) {
9118
+ await this.clearSecretKeys(keys, nodeId);
9119
+ }
9120
+ async clearSecretKeys(keys, nodeId, cap) {
8861
9121
  const tree = this.addonSecretTree();
8862
9122
  const keychain = this._ctx?.secrets !== void 0;
8863
9123
  const stored = keychain ? await this.readAddonStoreWithRetry() : {};
@@ -8870,25 +9130,27 @@ var BaseAddon = class {
8870
9130
  const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
8871
9131
  patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
8872
9132
  }
8873
- await this.applyGlobalPatch(patch, nodeId);
9133
+ await this.applyGlobalPatch(patch, nodeId, cap);
8874
9134
  }
8875
- async applyGlobalPatch(barePatch, nodeId) {
8876
- await this.loadScopedSchemas();
8877
- const keys = this.perNodeKeys();
9135
+ /** Resolves `true` when the write changed stored values. */
9136
+ async applyGlobalPatch(barePatch, nodeId, cap) {
9137
+ await this.loadScopedSchemas(cap);
9138
+ const keys = this.perNodeKeys(cap);
8878
9139
  const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
8879
9140
  const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
8880
9141
  const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
8881
9142
  const handle = this._secretSettings;
8882
9143
  if (handle !== null) {
8883
9144
  const { changed } = await handle.writeAddonStore(scoped);
8884
- if (!changed) return;
9145
+ if (!changed) return false;
8885
9146
  } else await this._ctx?.settings?.writeAddonStore(scoped);
8886
- if (target !== localNode) return;
9147
+ if (target !== localNode) return true;
8887
9148
  await this.resolveConfig();
8888
9149
  await this.onConfigChanged();
8889
9150
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
8890
9151
  const legacy = this.globalSettingsSchema();
8891
9152
  this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
9153
+ return true;
8892
9154
  }
8893
9155
  /**
8894
9156
  * The set of field keys the global settings schema declares `perNode: true`
@@ -10618,7 +10880,6 @@ var CAP_NAMES = Object.freeze([
10618
10880
  "notifier",
10619
10881
  "numeric-sensor",
10620
10882
  "oauth-integration",
10621
- "oidc-connections",
10622
10883
  "osd",
10623
10884
  "osd-manager",
10624
10885
  "pet-feeder",
@@ -12510,8 +12771,7 @@ Object.freeze({
12510
12771
  "device-provider": { "reloadDevice": 18e4 },
12511
12772
  "llm": {
12512
12773
  "generate": 6e5,
12513
- "generateVision": 6e5,
12514
- "testProfile": 6e5
12774
+ "generateVision": 6e5
12515
12775
  },
12516
12776
  "llm-runtime": {
12517
12777
  "complete": 6e5,
@@ -14036,17 +14296,18 @@ function composeInstanceKinds(parts, base) {
14036
14296
  * message to what the kind supports — callers never special-case a service.
14037
14297
  *
14038
14298
  * DESIGN DECISIONS (locked):
14039
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
14040
- * `setTargetEnabled`), each provider persisting via the `settings-store`
14041
- * cap. Rationale: the admin UI needs one uniform surface across the
14042
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
14043
- * alternative would fork the UI per addon and cannot host the
14044
- * discovery→adopt flow.
14045
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
14046
- * the generated cap-mount auto-`concatCollection`-fans them across every
14047
- * registered provider (notifiers addon + HA addon) so one catalog is
14048
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
14049
- * `addonId` the generated collection router extracts from the call input.
14299
+ * - A target is an INSTANCE of this function cap (epic #440, step 6): the
14300
+ * providers serve their targets as `integration-instances` records, one
14301
+ * record kind per target kind, and the admin lists, adds, edits, switches
14302
+ * and deletes them through that cap. This cap keeps what an instance cannot
14303
+ * say: the kind catalogue (`listTargetKinds`: capabilities, delivery
14304
+ * parameters, brand icon), dispatch (`send`, `testTarget`) and the form
14305
+ * helpers (`discoverTargets`, `probeTargetConfig`).
14306
+ * - `listTargetKinds` / `discoverTargets` return arrays → the generated
14307
+ * cap-mount auto-`concatCollection`-fans them across every registered
14308
+ * provider so one catalog is routable. `send` / `testTarget` /
14309
+ * `probeTargetConfig` route to ONE provider by the `addonId` the generated
14310
+ * collection router extracts from the call input.
14050
14311
  * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
14051
14312
  * transformer) + UDS MsgPack both round-trip typed arrays — already used by
14052
14313
  * `storage` / `storage-provider` / `recording` caps over the same path. No
@@ -14291,7 +14552,7 @@ var TargetKindCapsSchema = object({
14291
14552
  * the union is large and not meant for runtime validation here; the exported
14292
14553
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
14293
14554
  */
14294
- var ConfigSchemaPassthrough$4 = unknown();
14555
+ var ConfigSchemaPassthrough$3 = unknown();
14295
14556
  var TargetKindSchema = object({
14296
14557
  kind: string(),
14297
14558
  label: string(),
@@ -14328,7 +14589,7 @@ var TargetKindSchema = object({
14328
14589
  * its raster path, which is the safe default for an unknown image.
14329
14590
  */
14330
14591
  iconMediaType: string().optional(),
14331
- configSchema: ConfigSchemaPassthrough$4,
14592
+ configSchema: ConfigSchemaPassthrough$3,
14332
14593
  /**
14333
14594
  * The config keys that decide WHERE this kind's secrets are sent (server,
14334
14595
  * base URL, instance, chat, recipient). A save that changes any of them
@@ -14350,9 +14611,11 @@ var TargetKindSchema = object({
14350
14611
  caps: TargetKindCapsSchema
14351
14612
  });
14352
14613
  /**
14353
- * A persisted target. `config` holds secrets; providers REDACT secret fields
14354
- * (return a presence marker only) when serving `listTargets` — never
14355
- * round-trip a stored secret to the UI.
14614
+ * A target as a provider's own code sees it (the notification-center bridge, the
14615
+ * send path): the flat record body split into the name, the switch and the
14616
+ * kind's fields. `config` holds secrets; a reader that serves it to a client
14617
+ * REDACTS secret fields (a presence marker only) — never round-trip a stored
14618
+ * secret to the UI.
14356
14619
  */
14357
14620
  var TargetSchema = object({
14358
14621
  id: string(),
@@ -14395,13 +14658,13 @@ var ProbeTargetConfigResultSchema = object({
14395
14658
  stateInstance: string().optional(),
14396
14659
  error: string().optional()
14397
14660
  });
14398
- method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
14661
+ method(object({}), array(TargetKindSchema)), method(object({
14399
14662
  kind: string(),
14400
14663
  config: record(string(), unknown()).optional(),
14401
14664
  /**
14402
14665
  * When editing / duplicating, the blank password field never round-trips
14403
14666
  * the stored token. Pass the source target id so the provider can restore
14404
- * secrets server-side before dialling (same merge as upsert).
14667
+ * secrets server-side before dialling.
14405
14668
  */
14406
14669
  targetId: string().optional()
14407
14670
  }), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
@@ -14424,27 +14687,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
14424
14687
  }), TestResultSchema, {
14425
14688
  kind: "mutation",
14426
14689
  auth: "admin"
14427
- }), method(object({
14428
- target: TargetSchema,
14429
- /**
14430
- * Duplicate flow: create a NEW target id while copying secrets from an
14431
- * existing row. The UI never holds the stored token; blank / redacted
14432
- * password fields are restored from this source (D634 still applies if
14433
- * destinations diverge without a re-typed secret).
14434
- */
14435
- cloneSecretsFrom: string().optional()
14436
- }), TargetSchema, {
14437
- kind: "mutation",
14438
- auth: "admin"
14439
- }), method(object({ targetId: string() }), _void(), {
14440
- kind: "mutation",
14441
- auth: "admin"
14442
- }), method(object({
14443
- targetId: string(),
14444
- enabled: boolean()
14445
- }), _void(), {
14446
- kind: "mutation",
14447
- auth: "admin"
14448
14690
  });
14449
14691
  looseObject({
14450
14692
  name: string().min(1),
@@ -22712,7 +22954,6 @@ var SYSTEM_CAP_VOCABULARY = {
22712
22954
  "notification-output": system("notification delivery — reached through notifier devices and rules"),
22713
22955
  "notification-rules": system("notification rules — authored on their own page"),
22714
22956
  "oauth-integration": system(IDENTITY),
22715
- "oidc-connections": system(IDENTITY),
22716
22957
  "osd-manager": system(PIPELINE),
22717
22958
  "pipeline-executor": system(PIPELINE),
22718
22959
  "pipeline-orchestrator": system(PIPELINE),
@@ -44854,6 +45095,8 @@ var SettingsSchemaWithValuesSchema = object({
44854
45095
  });
44855
45096
  /** Patch object — keys are field names, values are the new field values. */
44856
45097
  var SettingsPatchSchema = record(string(), unknown());
45098
+ /** The scope of a scoped-settings call (D986): where the capability declared the values. */
45099
+ var ScopedSettingsScopeSchema = _enum(SCOPED_SETTINGS_SCOPES);
44857
45100
  /** Standard success response for update operations. */
44858
45101
  var SettingsUpdateResultSchema = object({ success: literal(true) });
44859
45102
  method(object({
@@ -44869,6 +45112,25 @@ method(object({
44869
45112
  kind: "mutation",
44870
45113
  auth: "admin"
44871
45114
  }), method(object({
45115
+ addonId: string(),
45116
+ cap: string(),
45117
+ scope: ScopedSettingsScopeSchema,
45118
+ nodeId: string().optional()
45119
+ }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45120
+ addonId: string(),
45121
+ cap: string(),
45122
+ scope: ScopedSettingsScopeSchema,
45123
+ nodeId: string().optional(),
45124
+ patch: SettingsPatchSchema
45125
+ }), SettingsUpdateResultSchema, {
45126
+ kind: "mutation",
45127
+ auth: "admin"
45128
+ }), method(object({
45129
+ addonId: string(),
45130
+ cap: string(),
45131
+ scope: ScopedSettingsScopeSchema,
45132
+ nodeId: string().optional()
45133
+ }), record(string(), unknown()).nullable()), method(object({
44872
45134
  addonId: string(),
44873
45135
  nodeId: string().optional()
44874
45136
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
@@ -54418,7 +54680,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
54418
54680
  * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
54419
54681
  * clears it.
54420
54682
  */
54421
- var ConfigSchemaPassthrough$3 = custom();
54683
+ var ConfigSchemaPassthrough$2 = custom();
54422
54684
  var IntegrationInstanceIdSchema = string().min(1);
54423
54685
  var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
54424
54686
  /**
@@ -54464,7 +54726,16 @@ var IntegrationInstanceKindSchema = object({
54464
54726
  /** Relative hub path to start an interactive account link of an instance of this kind. */
54465
54727
  connectPath: string().min(1).optional(),
54466
54728
  /** Relative hub path to disconnect that account. */
54467
- disconnectPath: string().min(1).optional()
54729
+ disconnectPath: string().min(1).optional(),
54730
+ /**
54731
+ * Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
54732
+ * GENERIC widget id the client maps to a component; a client that knows none of them shows none.
54733
+ * `connection` widgets write connection values, `model` widgets own the model choice.
54734
+ */
54735
+ widgets: array(object({
54736
+ id: string().min(1),
54737
+ placement: _enum(["connection", "model"])
54738
+ })).optional()
54468
54739
  });
54469
54740
  /**
54470
54741
  * How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
@@ -54569,7 +54840,7 @@ var integrationInstancesCapability = {
54569
54840
  id: IntegrationInstanceIdSchema.nullable(),
54570
54841
  /** The kind of the create form (`id: null`) of a provider with several. */
54571
54842
  kind: string().min(1).optional()
54572
- }), ConfigSchemaPassthrough$3, { auth: "admin" }),
54843
+ }), ConfigSchemaPassthrough$2, { auth: "admin" }),
54573
54844
  listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
54574
54845
  getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
54575
54846
  /**
@@ -55520,12 +55791,12 @@ var LlmModelInfoSchema = object({
55520
55791
  });
55521
55792
  /**
55522
55793
  * The provider-specific part of a profile, validated by the provider that owns
55523
- * the kind (`upsertProfile` refuses what it does not accept). A field its
55794
+ * the kind (saving an instance refuses what it does not accept). A field its
55524
55795
  * `connectionSchema` declares `type: 'password'` is a secret: it is held in the
55525
55796
  * keychain, never in the row, and reads back as the redaction marker.
55526
55797
  */
55527
55798
  var LlmProviderConfigSchema = record(string(), unknown());
55528
- var LlmProfileSchema = object({
55799
+ object({
55529
55800
  id: string(),
55530
55801
  name: string(),
55531
55802
  /** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
@@ -55619,8 +55890,8 @@ var LlmProviderWidgetSchema = object({
55619
55890
  /** ConfigUISchema tree passed through untyped on the wire (the
55620
55891
  * notification-output `ConfigSchemaPassthrough` precedent at
55621
55892
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
55622
- var ConfigSchemaPassthrough$2 = unknown();
55623
- var LlmProfileKindDescriptorSchema = object({
55893
+ var ConfigSchemaPassthrough$1 = unknown();
55894
+ object({
55624
55895
  kind: LlmProfileKindSchema,
55625
55896
  label: string(),
55626
55897
  /** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
@@ -55628,9 +55899,9 @@ var LlmProfileKindDescriptorSchema = object({
55628
55899
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
55629
55900
  addonId: string(),
55630
55901
  /** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
55631
- connectionSchema: ConfigSchemaPassthrough$2,
55902
+ connectionSchema: ConfigSchemaPassthrough$1,
55632
55903
  /** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
55633
- advancedSchema: ConfigSchemaPassthrough$2.optional(),
55904
+ advancedSchema: ConfigSchemaPassthrough$1.optional(),
55634
55905
  /** The kind's defaults; a model from `listModels` may override any of them. */
55635
55906
  features: LlmModelFeaturesSchema,
55636
55907
  /** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
@@ -55759,20 +56030,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
55759
56030
  }), method(object({
55760
56031
  addonId: string().optional(),
55761
56032
  requestId: string()
55762
- }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
55763
- kind: "mutation",
55764
- auth: "admin"
55765
- }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
55766
- kind: "mutation",
55767
- auth: "admin"
55768
- }), method(ProfileRefInputSchema, _void(), {
55769
- kind: "mutation",
55770
- auth: "admin"
55771
- }), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
55772
- kind: "mutation",
55773
- auth: "admin",
55774
- timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
55775
- }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
56033
+ }), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
55776
56034
  kind: "mutation",
55777
56035
  auth: "admin"
55778
56036
  }), method(object({}), array(LlmDefaultSchema)), method(object({
@@ -56767,20 +57025,7 @@ method(_void(), MeshStatusSchema), method(object({
56767
57025
  /** Optional hostname override the host should advertise once joined. */
56768
57026
  hostname: string().optional() }), object({
56769
57027
  /** Authentication URL the operator should open in a browser. */
56770
- loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() })), method(object({
56771
- /** Optional auth key — when provided, probes the key validity
56772
- * against the provider's API. Omit when already joined to
56773
- * just ping the daemon. */
56774
- authKey: string().optional() }), object({
56775
- ok: boolean(),
56776
- /** Provider-side identifier resolved by the probe (tailnet
56777
- * name for Tailscale, network id for ZeroTier, etc.). */
56778
- tenant: string().optional(),
56779
- /** Daemon binary version, when reachable. */
56780
- daemonVersion: string().optional(),
56781
- /** Human-readable error when `ok: false`. */
56782
- error: string().optional()
56783
- }), { kind: "mutation" });
57028
+ loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }));
56784
57029
  object({
56785
57030
  value: number().nullable().meta(readingMeta("device", { label: "Total" })),
56786
57031
  lastFetchedAt: number()
@@ -66438,61 +66683,6 @@ var OauthIntegrationDescriptorSchema = object({
66438
66683
  });
66439
66684
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
66440
66685
  /**
66441
- * `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
66442
- *
66443
- * One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
66444
- * ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
66445
- * Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
66446
- * immutable after create.
66447
- */
66448
- var ConfigSchemaPassthrough$1 = unknown();
66449
- var OidcConnectionSchema = object({
66450
- id: string().min(1),
66451
- displayName: string(),
66452
- icon: string(),
66453
- issuerUrl: string(),
66454
- clientId: string(),
66455
- /** Client id + secret present — never the secret value. */
66456
- configured: boolean(),
66457
- /**
66458
- * Values for the connection form schema. `clientSecret` is a keychain
66459
- * {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
66460
- */
66461
- formValues: record(string(), unknown()).optional()
66462
- });
66463
- /**
66464
- * Patch one connection. Omitted or empty `clientSecret` keeps the stored
66465
- * secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
66466
- */
66467
- var OidcConnectionUpdateInputSchema = object({
66468
- id: string().min(1),
66469
- displayName: string().min(1),
66470
- icon: string().optional(),
66471
- issuerUrl: string().optional(),
66472
- clientId: string().optional(),
66473
- clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
66474
- redirectUri: string().optional(),
66475
- scopes: string().optional(),
66476
- usernameClaim: _enum([
66477
- "preferred_username",
66478
- "email",
66479
- "sub"
66480
- ]).optional()
66481
- });
66482
- method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
66483
- id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
66484
- displayName: string().min(1)
66485
- }), object({ id: string() }), {
66486
- kind: "mutation",
66487
- auth: "admin"
66488
- }), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
66489
- kind: "mutation",
66490
- auth: "admin"
66491
- }), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
66492
- kind: "mutation",
66493
- auth: "admin"
66494
- });
66495
- /**
66496
66686
  * Generic on-screen-display (video overlay) cap. Each camera exposes
66497
66687
  * a firmware-defined list of overlays (channel name, timestamp,
66498
66688
  * watermark, logo, …); the cap models them as a homogeneous array of
@@ -72678,21 +72868,6 @@ method(object({
72678
72868
  */
72679
72869
  ttlSec: union([number().int().positive(), literal("never")]).optional()
72680
72870
  }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
72681
- var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
72682
- providerId: string().min(1),
72683
- displayName: string().min(1),
72684
- configSchema: unknown(),
72685
- nodeLocal: boolean(),
72686
- shouldSaveDiskSpace: literal(true),
72687
- minFreePercent: number().min(0).max(100)
72688
- }), object({
72689
- providerId: string().min(1),
72690
- displayName: string().min(1),
72691
- configSchema: unknown(),
72692
- nodeLocal: boolean(),
72693
- shouldSaveDiskSpace: literal(false),
72694
- minFreePercent: literal(null)
72695
- })]);
72696
72871
  method(object({
72697
72872
  location: StorageLocationRefSchema,
72698
72873
  relativePath: string()
@@ -72754,42 +72929,7 @@ method(object({
72754
72929
  }), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
72755
72930
  kind: "mutation",
72756
72931
  auth: "admin"
72757
- }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
72758
- createdAt: true,
72759
- updatedAt: true
72760
- }), StorageLocationSchema, {
72761
- kind: "mutation",
72762
- auth: "admin"
72763
- }), method(object({
72764
- id: string(),
72765
- force: boolean().optional()
72766
- }), _void(), {
72767
- kind: "mutation",
72768
- auth: "admin"
72769
- }), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
72770
- ok: boolean(),
72771
- error: string().optional()
72772
- }), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
72773
- providerId: string(),
72774
- /**
72775
- * The location this config is an UNSAVED edit of, when there is one.
72776
- *
72777
- * `listLocations` replaces every declared secret with the redaction
72778
- * sentinel, so the edit modal's form state holds the sentinel for any
72779
- * credential the operator did not retype — and posting that here
72780
- * without a way to resolve it makes the provider try to authenticate
72781
- * as `__camstack_redacted__` and report the operator's own working
72782
- * password as wrong. Given this id, the orchestrator restores each
72783
- * sentinel from the stored config (same rule as `upsertLocation`)
72784
- * before dispatching. Omitted by the "Add location" wizard, where
72785
- * every value was typed just now and nothing is stored yet.
72786
- */
72787
- locationId: string().optional(),
72788
- config: record(string(), unknown())
72789
- }), object({
72790
- ok: boolean(),
72791
- error: string().optional()
72792
- }), { auth: "admin" });
72932
+ }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
72793
72933
  /** One table of the settings database. */
72794
72934
  var StorageTableSchema = object({
72795
72935
  /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
@@ -74020,7 +74160,7 @@ var TerminalInstanceInfoSchema = object({
74020
74160
  /**
74021
74161
  * False while the node the instance is bound to is not in the cluster
74022
74162
  * topology (forgotten, renamed, re-paired). The instance is kept; the
74023
- * operator re-points it with `updateInstance({ targetNodeId })`.
74163
+ * operator re-points it on the Terminal page.
74024
74164
  */
74025
74165
  nodeInCluster: boolean(),
74026
74166
  profileId: string(),
@@ -74058,41 +74198,7 @@ var TerminalOutputBatchSchema = object({
74058
74198
  snapshot: string().optional(),
74059
74199
  events: array(TerminalOutputEventSchema).readonly()
74060
74200
  });
74061
- method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
74062
- targetNodeId: string().min(1),
74063
- profileId: string().min(1),
74064
- name: string().trim().min(1).max(160).optional(),
74065
- executable: string().max(1024).optional(),
74066
- args: array(string().max(2048)).max(64).optional(),
74067
- cwd: string().max(1024).optional(),
74068
- environment: array(string().max(4096)).max(64).optional(),
74069
- profileSettings: ProfileSettingsBagSchema.optional()
74070
- }), TerminalInstanceInfoSchema, {
74071
- kind: "mutation",
74072
- auth: "admin"
74073
- }), method(object({
74074
- instanceId: string().min(1),
74075
- /** Re-point the instance to another node; the profile must exist there. */
74076
- targetNodeId: string().min(1).max(256).optional(),
74077
- name: string().trim().min(1).max(160).optional(),
74078
- executable: string().max(1024).optional(),
74079
- args: array(string().max(2048)).max(64).optional(),
74080
- cwd: string().max(1024).optional(),
74081
- environment: array(string().max(4096)).max(64).optional(),
74082
- profileSettings: ProfileSettingsBagSchema.optional()
74083
- }), TerminalInstanceInfoSchema, {
74084
- kind: "mutation",
74085
- auth: "admin"
74086
- }), method(object({ instanceId: string().min(1) }), _void(), {
74087
- kind: "mutation",
74088
- auth: "admin"
74089
- }), method(object({
74090
- instanceId: string().min(1),
74091
- enabled: boolean()
74092
- }), TerminalInstanceInfoSchema, {
74093
- kind: "mutation",
74094
- auth: "admin"
74095
- }), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
74201
+ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
74096
74202
  stableId: string().min(1),
74097
74203
  name: string().trim().min(1).max(160).optional()
74098
74204
  }), TerminalInstanceInfoSchema, {
@@ -76910,6 +77016,20 @@ Object.freeze({
76910
77016
  access: "view",
76911
77017
  role: "admin"
76912
77018
  },
77019
+ "addonSettings.getScopedSettings": {
77020
+ capName: "addon-settings",
77021
+ capScope: "system",
77022
+ addonId: null,
77023
+ access: "view",
77024
+ role: "admin"
77025
+ },
77026
+ "addonSettings.readScopedValues": {
77027
+ capName: "addon-settings",
77028
+ capScope: "system",
77029
+ addonId: null,
77030
+ access: "view",
77031
+ role: "admin"
77032
+ },
76913
77033
  "addonSettings.unsetStoredSettingsKeys": {
76914
77034
  capName: "addon-settings",
76915
77035
  capScope: "system",
@@ -76931,6 +77051,13 @@ Object.freeze({
76931
77051
  access: "create",
76932
77052
  role: "admin"
76933
77053
  },
77054
+ "addonSettings.updateScopedSettings": {
77055
+ capName: "addon-settings",
77056
+ capScope: "system",
77057
+ addonId: null,
77058
+ access: "create",
77059
+ role: "admin"
77060
+ },
76934
77061
  "addonWidgets.listWidgets": {
76935
77062
  capName: "addon-widgets",
76936
77063
  capScope: "system",
@@ -80382,13 +80509,6 @@ Object.freeze({
80382
80509
  access: "delete",
80383
80510
  role: "admin"
80384
80511
  },
80385
- "llm.deleteProfile": {
80386
- capName: "llm",
80387
- capScope: "system",
80388
- addonId: null,
80389
- access: "delete",
80390
- role: "admin"
80391
- },
80392
80512
  "llm.generate": {
80393
80513
  capName: "llm",
80394
80514
  capScope: "system",
@@ -80452,20 +80572,6 @@ Object.freeze({
80452
80572
  access: "view",
80453
80573
  role: "admin"
80454
80574
  },
80455
- "llm.listProfileKinds": {
80456
- capName: "llm",
80457
- capScope: "system",
80458
- addonId: null,
80459
- access: "view",
80460
- role: "admin"
80461
- },
80462
- "llm.listProfiles": {
80463
- capName: "llm",
80464
- capScope: "system",
80465
- addonId: null,
80466
- access: "view",
80467
- role: "admin"
80468
- },
80469
80575
  "llm.listRuntimeNodes": {
80470
80576
  capName: "llm",
80471
80577
  capScope: "system",
@@ -80473,13 +80579,6 @@ Object.freeze({
80473
80579
  access: "view",
80474
80580
  role: "admin"
80475
80581
  },
80476
- "llm.migrateRawSecrets": {
80477
- capName: "llm",
80478
- capScope: "system",
80479
- addonId: null,
80480
- access: "create",
80481
- role: "admin"
80482
- },
80483
80582
  "llm.resolveModelRef": {
80484
80583
  capName: "llm",
80485
80584
  capScope: "system",
@@ -80508,20 +80607,6 @@ Object.freeze({
80508
80607
  access: "create",
80509
80608
  role: "admin"
80510
80609
  },
80511
- "llm.testProfile": {
80512
- capName: "llm",
80513
- capScope: "system",
80514
- addonId: null,
80515
- access: "create",
80516
- role: "admin"
80517
- },
80518
- "llm.upsertProfile": {
80519
- capName: "llm",
80520
- capScope: "system",
80521
- addonId: null,
80522
- access: "create",
80523
- role: "admin"
80524
- },
80525
80610
  "llmRuntime.appleAvailability": {
80526
80611
  capName: "llm-runtime",
80527
80612
  capScope: "system",
@@ -81026,13 +81111,6 @@ Object.freeze({
81026
81111
  access: "create",
81027
81112
  role: "admin"
81028
81113
  },
81029
- "meshNetwork.testConnection": {
81030
- capName: "mesh-network",
81031
- capScope: "system",
81032
- addonId: null,
81033
- access: "create",
81034
- role: "admin"
81035
- },
81036
81114
  "meter.getStatus": {
81037
81115
  capName: "meter",
81038
81116
  capScope: "device",
@@ -81691,13 +81769,6 @@ Object.freeze({
81691
81769
  access: "create",
81692
81770
  role: "admin"
81693
81771
  },
81694
- "notificationOutput.deleteTarget": {
81695
- capName: "notification-output",
81696
- capScope: "system",
81697
- addonId: null,
81698
- access: "delete",
81699
- role: "admin"
81700
- },
81701
81772
  "notificationOutput.discoverTargets": {
81702
81773
  capName: "notification-output",
81703
81774
  capScope: "system",
@@ -81705,13 +81776,6 @@ Object.freeze({
81705
81776
  access: "view",
81706
81777
  role: "admin"
81707
81778
  },
81708
- "notificationOutput.getTarget": {
81709
- capName: "notification-output",
81710
- capScope: "system",
81711
- addonId: null,
81712
- access: "view",
81713
- role: "admin"
81714
- },
81715
81779
  "notificationOutput.listTargetKinds": {
81716
81780
  capName: "notification-output",
81717
81781
  capScope: "system",
@@ -81719,13 +81783,6 @@ Object.freeze({
81719
81783
  access: "view",
81720
81784
  role: "admin"
81721
81785
  },
81722
- "notificationOutput.listTargets": {
81723
- capName: "notification-output",
81724
- capScope: "system",
81725
- addonId: null,
81726
- access: "view",
81727
- role: "admin"
81728
- },
81729
81786
  "notificationOutput.probeTargetConfig": {
81730
81787
  capName: "notification-output",
81731
81788
  capScope: "system",
@@ -81740,13 +81797,6 @@ Object.freeze({
81740
81797
  access: "create",
81741
81798
  role: "admin"
81742
81799
  },
81743
- "notificationOutput.setTargetEnabled": {
81744
- capName: "notification-output",
81745
- capScope: "system",
81746
- addonId: null,
81747
- access: "create",
81748
- role: "admin"
81749
- },
81750
81800
  "notificationOutput.testTarget": {
81751
81801
  capName: "notification-output",
81752
81802
  capScope: "system",
@@ -81754,13 +81804,6 @@ Object.freeze({
81754
81804
  access: "create",
81755
81805
  role: "admin"
81756
81806
  },
81757
- "notificationOutput.upsertTarget": {
81758
- capName: "notification-output",
81759
- capScope: "system",
81760
- addonId: null,
81761
- access: "create",
81762
- role: "admin"
81763
- },
81764
81807
  "notificationRules.activateAlarmProfile": {
81765
81808
  capName: "notification-rules",
81766
81809
  capScope: "system",
@@ -82020,41 +82063,6 @@ Object.freeze({
82020
82063
  access: "view",
82021
82064
  role: "admin"
82022
82065
  },
82023
- "oidcConnections.connectionFormSchema": {
82024
- capName: "oidc-connections",
82025
- capScope: "system",
82026
- addonId: null,
82027
- access: "view",
82028
- role: "admin"
82029
- },
82030
- "oidcConnections.createConnection": {
82031
- capName: "oidc-connections",
82032
- capScope: "system",
82033
- addonId: null,
82034
- access: "create",
82035
- role: "admin"
82036
- },
82037
- "oidcConnections.deleteConnection": {
82038
- capName: "oidc-connections",
82039
- capScope: "system",
82040
- addonId: null,
82041
- access: "delete",
82042
- role: "admin"
82043
- },
82044
- "oidcConnections.listConnections": {
82045
- capName: "oidc-connections",
82046
- capScope: "system",
82047
- addonId: null,
82048
- access: "view",
82049
- role: "admin"
82050
- },
82051
- "oidcConnections.updateConnection": {
82052
- capName: "oidc-connections",
82053
- capScope: "system",
82054
- addonId: null,
82055
- access: "create",
82056
- role: "admin"
82057
- },
82058
82066
  "osd.getStatus": {
82059
82067
  capName: "osd",
82060
82068
  capScope: "device",
@@ -85086,13 +85094,6 @@ Object.freeze({
85086
85094
  access: "delete",
85087
85095
  role: "admin"
85088
85096
  },
85089
- "storage.deleteLocation": {
85090
- capName: "storage",
85091
- capScope: "system",
85092
- addonId: null,
85093
- access: "delete",
85094
- role: "admin"
85095
- },
85096
85097
  "storage.endDownload": {
85097
85098
  capName: "storage",
85098
85099
  capScope: "system",
@@ -85149,13 +85150,6 @@ Object.freeze({
85149
85150
  access: "view",
85150
85151
  role: "admin"
85151
85152
  },
85152
- "storage.listProviders": {
85153
- capName: "storage",
85154
- capScope: "system",
85155
- addonId: null,
85156
- access: "view",
85157
- role: "admin"
85158
- },
85159
85153
  "storage.read": {
85160
85154
  capName: "storage",
85161
85155
  capScope: "system",
@@ -85177,27 +85171,6 @@ Object.freeze({
85177
85171
  access: "view",
85178
85172
  role: "admin"
85179
85173
  },
85180
- "storage.testConfig": {
85181
- capName: "storage",
85182
- capScope: "system",
85183
- addonId: null,
85184
- access: "view",
85185
- role: "admin"
85186
- },
85187
- "storage.testLocation": {
85188
- capName: "storage",
85189
- capScope: "system",
85190
- addonId: null,
85191
- access: "view",
85192
- role: "admin"
85193
- },
85194
- "storage.upsertLocation": {
85195
- capName: "storage",
85196
- capScope: "system",
85197
- addonId: null,
85198
- access: "create",
85199
- role: "admin"
85200
- },
85201
85174
  "storage.write": {
85202
85175
  capName: "storage",
85203
85176
  capScope: "system",
@@ -85975,27 +85948,6 @@ Object.freeze({
85975
85948
  access: "create",
85976
85949
  role: "admin"
85977
85950
  },
85978
- "terminalSession.createInstance": {
85979
- capName: "terminal-session",
85980
- capScope: "system",
85981
- addonId: null,
85982
- access: "create",
85983
- role: "admin"
85984
- },
85985
- "terminalSession.deleteInstance": {
85986
- capName: "terminal-session",
85987
- capScope: "system",
85988
- addonId: null,
85989
- access: "delete",
85990
- role: "admin"
85991
- },
85992
- "terminalSession.listInstances": {
85993
- capName: "terminal-session",
85994
- capScope: "system",
85995
- addonId: null,
85996
- access: "view",
85997
- role: "admin"
85998
- },
85999
85951
  "terminalSession.listLegacyCameras": {
86000
85952
  capName: "terminal-session",
86001
85953
  capScope: "system",
@@ -86038,20 +85990,6 @@ Object.freeze({
86038
85990
  access: "create",
86039
85991
  role: "admin"
86040
85992
  },
86041
- "terminalSession.setInstanceEnabled": {
86042
- capName: "terminal-session",
86043
- capScope: "system",
86044
- addonId: null,
86045
- access: "create",
86046
- role: "admin"
86047
- },
86048
- "terminalSession.updateInstance": {
86049
- capName: "terminal-session",
86050
- capScope: "system",
86051
- addonId: null,
86052
- access: "create",
86053
- role: "admin"
86054
- },
86055
85993
  "terminalSession.writeInput": {
86056
85994
  capName: "terminal-session",
86057
85995
  capScope: "system",
@@ -145983,17 +145921,6 @@ function scrub(message, secret) {
145983
145921
  function mintRelayId() {
145984
145922
  return `relay-${randomUUID().slice(0, 8)}`;
145985
145923
  }
145986
- /** The keys the settings page edits: the relay's connection fields. */
145987
- var RELAY_FORM_KEYS = new Set([
145988
- "host",
145989
- "port",
145990
- "secure",
145991
- "username",
145992
- "password",
145993
- "fromAddress",
145994
- "fromName"
145995
- ]);
145996
- var DEFAULT_RELAY_NAME = "Email (SMTP)";
145997
145924
  var RELAY_LIST_MAX = 200;
145998
145925
  function hasText(value) {
145999
145926
  return typeof value === "string" && value.length > 0;
@@ -146246,44 +146173,10 @@ var SmtpNodemailerAddon = class extends BaseAddon {
146246
146173
  const rows = (await records.kind(smtpRelaysKind).query({ limit: RELAY_LIST_MAX })).records;
146247
146174
  return (rows.find((row) => row.data.host.length > 0) ?? rows[0])?.id;
146248
146175
  }
146249
- /** The relay form. Its values live on the relay record, never in `addon-settings`. */
146176
+ /** The relay form. */
146250
146177
  async integrationFormSchema(_instance) {
146251
146178
  return this.relayForm();
146252
146179
  }
146253
- async getGlobalSettings() {
146254
- const schema = this.relayForm();
146255
- if (this.instances === null) return hydrateSchema(schema, {}, { inlineSecretsAsViews: true });
146256
- const id = await this.firstRelayId();
146257
- return hydrateSchema(schema, (id === void 0 ? null : await this.instances.readResolved(id))?.body ?? {}, { inlineSecretsAsViews: true });
146258
- }
146259
- async updateGlobalSettingsAsOperator(patch) {
146260
- await this.writeDefaultRelay(patch);
146261
- }
146262
- async updateGlobalSettings(patch) {
146263
- await this.writeDefaultRelay(patch);
146264
- }
146265
- /** Write the form's values onto the default relay, creating it on the first save. */
146266
- async writeDefaultRelay(patch) {
146267
- if (this.instances === null) throw new Error("smtp-nodemailer is not initialised");
146268
- const values = Object.fromEntries(Object.entries(patch).filter(([key]) => RELAY_FORM_KEYS.has(key)));
146269
- const existing = await this.firstRelayId();
146270
- if (existing !== void 0) {
146271
- await this.instances.updateInstance({
146272
- id: existing,
146273
- values
146274
- });
146275
- return;
146276
- }
146277
- const id = mintRelayId();
146278
- await this.instances.createInstance({
146279
- id,
146280
- name: DEFAULT_RELAY_NAME
146281
- });
146282
- await this.instances.updateInstance({
146283
- id,
146284
- values
146285
- });
146286
- }
146287
146180
  /** Probe a relay (EHLO + AUTH) with the form's values, falling back to the stored ones. */
146288
146181
  async probe(id, values) {
146289
146182
  const stored = id !== null && this.instances !== null ? await this.instances.getResolved(id) : null;