@camstack/addon-provider-enpal 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/addon.js +1599 -1763
- package/dist/addon.mjs +1599 -1763
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
//#region ../types/dist/event-category-
|
|
2
|
+
//#region ../types/dist/event-category-78tcBemo.mjs
|
|
3
3
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4
4
|
EventCategory["SystemBoot"] = "system.boot";
|
|
5
5
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -120,6 +120,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
120
120
|
*/
|
|
121
121
|
EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
|
|
122
122
|
/**
|
|
123
|
+
* An addon is RUNNING although its installed package is below what the
|
|
124
|
+
* running server requires (`camstack.requires.addons` of the server, or the
|
|
125
|
+
* addon's own `requires.server`). Raised once per streak by the unsupported-
|
|
126
|
+
* addon tracker, on the hub and on agents; AlertCenter turns it into one
|
|
127
|
+
* operator alert per node and package. The addon is never held back for it.
|
|
128
|
+
*
|
|
129
|
+
* Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
|
|
130
|
+
*/
|
|
131
|
+
EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
|
|
132
|
+
/**
|
|
133
|
+
* The package of an {@link AddonUnsupportedVersion} alert reached a supported
|
|
134
|
+
* version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
|
|
135
|
+
*
|
|
136
|
+
* Payload: `{ packageName, addonId, nodeId }`.
|
|
137
|
+
*/
|
|
138
|
+
EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
|
|
139
|
+
/**
|
|
123
140
|
* Monitor scheduled the next retry for a failed addon. Transient —
|
|
124
141
|
* surfaced to the UI for live-updating the "next retry in Ns"
|
|
125
142
|
* countdown on the Addons page row, NOT persisted as an alert.
|
|
@@ -230,7 +247,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
230
247
|
EventCategory["ProcessRestarted"] = "process.restarted";
|
|
231
248
|
/**
|
|
232
249
|
* The SET of storage locations changed — one was created, edited, enabled,
|
|
233
|
-
* disabled or deleted through
|
|
250
|
+
* disabled or deleted through the location instances or the forced-delete action.
|
|
234
251
|
*
|
|
235
252
|
* Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
|
|
236
253
|
* it must also converge on its own periodic path, because a dropped event
|
|
@@ -785,7 +802,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
785
802
|
return EventCategory;
|
|
786
803
|
}({});
|
|
787
804
|
//#endregion
|
|
788
|
-
//#region ../types/dist/device-type-
|
|
805
|
+
//#region ../types/dist/device-type-DxCT0krO.mjs
|
|
789
806
|
/**
|
|
790
807
|
import { errMsg } from '@camstack/types'
|
|
791
808
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -947,6 +964,13 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
947
964
|
* filters that want "all devices with on-motion behaviour".
|
|
948
965
|
*/
|
|
949
966
|
DeviceFeature["MotionTrigger"] = "motion-trigger";
|
|
967
|
+
/**
|
|
968
|
+
* Device can be run for a chosen time and then stopped. Mirrors the
|
|
969
|
+
* `timed-run` wrapper cap — the cap is bound by the core-blocks addon, not by
|
|
970
|
+
* a driver, so the flag is derived from the device's bindings
|
|
971
|
+
* (`featuresOfBoundCaps`). Gates the quick-run control in the viewer.
|
|
972
|
+
*/
|
|
973
|
+
DeviceFeature["TimedRun"] = "timed-run";
|
|
950
974
|
/** Light supports rgb-triplet color via `color` cap. */
|
|
951
975
|
DeviceFeature["LightColorRgb"] = "light-color-rgb";
|
|
952
976
|
/** Light supports HSV color via `color` cap. */
|
|
@@ -5981,7 +6005,7 @@ var ZodIssueCode = {
|
|
|
5981
6005
|
var ZodFirstPartyTypeKind;
|
|
5982
6006
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5983
6007
|
//#endregion
|
|
5984
|
-
//#region ../types/dist/sleep-
|
|
6008
|
+
//#region ../types/dist/sleep-CGPZyzB5.mjs
|
|
5985
6009
|
/**
|
|
5986
6010
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5987
6011
|
* window to float samples (D455).
|
|
@@ -6345,6 +6369,59 @@ Object.fromEntries([
|
|
|
6345
6369
|
function isValuelessField(field) {
|
|
6346
6370
|
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";
|
|
6347
6371
|
}
|
|
6372
|
+
/**
|
|
6373
|
+
* Base collector: flatten a hydrated `ConfigUISchemaWithValues` into
|
|
6374
|
+
* `key → { value, default }`, recursing through `group` and `sub-tabs`
|
|
6375
|
+
* layout fields. Keeping `value` and `default` SEPARATE (rather than
|
|
6376
|
+
* pre-resolving) lets callers both read the effective value AND detect
|
|
6377
|
+
* overrides (`value` differs from `default`). Structural over `unknown`;
|
|
6378
|
+
* malformed input yields an empty map, never throws.
|
|
6379
|
+
*/
|
|
6380
|
+
function collectHydratedFieldEntries(schema) {
|
|
6381
|
+
const out = /* @__PURE__ */ new Map();
|
|
6382
|
+
if (typeof schema !== "object" || schema === null) return out;
|
|
6383
|
+
const sections = schema.sections;
|
|
6384
|
+
if (!Array.isArray(sections)) return out;
|
|
6385
|
+
for (const section of sections) if (typeof section === "object" && section !== null) collectFieldEntriesInto(section.fields, out);
|
|
6386
|
+
return out;
|
|
6387
|
+
}
|
|
6388
|
+
/** Recursive field walker for {@link collectHydratedFieldEntries}. */
|
|
6389
|
+
function collectFieldEntriesInto(fields, into) {
|
|
6390
|
+
if (!Array.isArray(fields)) return;
|
|
6391
|
+
for (const field of fields) {
|
|
6392
|
+
if (typeof field !== "object" || field === null) continue;
|
|
6393
|
+
const type = field.type;
|
|
6394
|
+
if (type === "group") {
|
|
6395
|
+
collectFieldEntriesInto(field.fields, into);
|
|
6396
|
+
continue;
|
|
6397
|
+
}
|
|
6398
|
+
if (type === "sub-tabs") {
|
|
6399
|
+
const tabs = field.tabs;
|
|
6400
|
+
if (Array.isArray(tabs)) {
|
|
6401
|
+
for (const tab of tabs) if (typeof tab === "object" && tab !== null) collectFieldEntriesInto(tab.fields, into);
|
|
6402
|
+
}
|
|
6403
|
+
continue;
|
|
6404
|
+
}
|
|
6405
|
+
const key = field.key;
|
|
6406
|
+
if (typeof key !== "string" || key.length === 0) continue;
|
|
6407
|
+
if (!("value" in field)) continue;
|
|
6408
|
+
into.set(key, {
|
|
6409
|
+
value: field.value,
|
|
6410
|
+
default: field.default
|
|
6411
|
+
});
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
/**
|
|
6415
|
+
* Flatten a hydrated settings payload into `key → EFFECTIVE value`
|
|
6416
|
+
* (`value ?? default`). The read-side inverse of `hydrateSchema` for the
|
|
6417
|
+
* common "just give me the resolved values" case. Built on
|
|
6418
|
+
* {@link collectHydratedFieldEntries}.
|
|
6419
|
+
*/
|
|
6420
|
+
function collectHydratedFieldValues(schema) {
|
|
6421
|
+
const out = /* @__PURE__ */ new Map();
|
|
6422
|
+
for (const [key, entry] of collectHydratedFieldEntries(schema)) out.set(key, entry.value ?? entry.default);
|
|
6423
|
+
return out;
|
|
6424
|
+
}
|
|
6348
6425
|
function hydrateSchema(schema, values, options = {}) {
|
|
6349
6426
|
return {
|
|
6350
6427
|
...schema.tabs ? { tabs: [...schema.tabs] } : {},
|
|
@@ -6750,10 +6827,33 @@ function rowsDepsOver(api) {
|
|
|
6750
6827
|
};
|
|
6751
6828
|
}
|
|
6752
6829
|
var PREFIX = "records/";
|
|
6830
|
+
/**
|
|
6831
|
+
* Cross-realm brand. An addon bundles its own copy of this module (D15) while
|
|
6832
|
+
* `ctx.records` is the HOST's door, so an error thrown by the host is an
|
|
6833
|
+
* instance of the host's classes, not of the addon's. The brand plus the code
|
|
6834
|
+
* make `instanceof` answer by identity of the failure, not of the copy.
|
|
6835
|
+
*/
|
|
6836
|
+
var RECORDS_ERROR_BRAND = Symbol.for("camstack.records-error");
|
|
6837
|
+
function carriesBrand(value) {
|
|
6838
|
+
return typeof value === "object" && value !== null && Reflect.get(value, RECORDS_ERROR_BRAND) === true;
|
|
6839
|
+
}
|
|
6753
6840
|
/** The base of every records failure. `detail` is the human part of the message. */
|
|
6754
6841
|
var RecordsError = class extends Error {
|
|
6842
|
+
/** The code a subclass stands for; the base stands for every code. */
|
|
6843
|
+
static code = void 0;
|
|
6844
|
+
/**
|
|
6845
|
+
* `instanceof` across copies of this module: any branded error is a
|
|
6846
|
+
* `RecordsError`, and is an instance of a subclass only when its code is the
|
|
6847
|
+
* subclass's code. Same-copy answers are unchanged.
|
|
6848
|
+
*/
|
|
6849
|
+
static [Symbol.hasInstance](value) {
|
|
6850
|
+
if (Function.prototype[Symbol.hasInstance].call(this, value)) return true;
|
|
6851
|
+
if (!carriesBrand(value)) return false;
|
|
6852
|
+
return this.code === void 0 || value.code === this.code;
|
|
6853
|
+
}
|
|
6755
6854
|
code;
|
|
6756
6855
|
detail;
|
|
6856
|
+
[RECORDS_ERROR_BRAND] = true;
|
|
6757
6857
|
constructor(code, detail) {
|
|
6758
6858
|
super(`${PREFIX}${code}: ${detail}`);
|
|
6759
6859
|
this.name = "RecordsError";
|
|
@@ -6762,29 +6862,33 @@ var RecordsError = class extends Error {
|
|
|
6762
6862
|
}
|
|
6763
6863
|
};
|
|
6764
6864
|
/** A write guarded by `ifRev` found another revision (or an existing row for `ifRev: 0`). */
|
|
6765
|
-
var RevConflictError = class extends RecordsError {
|
|
6865
|
+
var RevConflictError = class RevConflictError extends RecordsError {
|
|
6866
|
+
static code = "rev-conflict";
|
|
6766
6867
|
constructor(detail) {
|
|
6767
|
-
super(
|
|
6868
|
+
super(RevConflictError.code, detail);
|
|
6768
6869
|
this.name = "RevConflictError";
|
|
6769
6870
|
}
|
|
6770
6871
|
};
|
|
6771
6872
|
/** The keychain could not store or serve a value: explicit, never an empty string. */
|
|
6772
|
-
var RecordSecretUnavailableError = class extends RecordsError {
|
|
6873
|
+
var RecordSecretUnavailableError = class RecordSecretUnavailableError extends RecordsError {
|
|
6874
|
+
static code = "secret-unavailable";
|
|
6773
6875
|
constructor(detail) {
|
|
6774
|
-
super(
|
|
6876
|
+
super(RecordSecretUnavailableError.code, detail);
|
|
6775
6877
|
this.name = "RecordSecretUnavailableError";
|
|
6776
6878
|
}
|
|
6777
6879
|
};
|
|
6778
|
-
var InvalidRecordError = class extends RecordsError {
|
|
6880
|
+
var InvalidRecordError = class InvalidRecordError extends RecordsError {
|
|
6881
|
+
static code = "invalid-record";
|
|
6779
6882
|
constructor(detail) {
|
|
6780
|
-
super(
|
|
6883
|
+
super(InvalidRecordError.code, detail);
|
|
6781
6884
|
this.name = "InvalidRecordError";
|
|
6782
6885
|
}
|
|
6783
6886
|
};
|
|
6784
6887
|
/** A kind declaration the engine refuses (non-additive change, bad shape, wrong file). */
|
|
6785
|
-
var DeclarationRefusedError = class extends RecordsError {
|
|
6888
|
+
var DeclarationRefusedError = class DeclarationRefusedError extends RecordsError {
|
|
6889
|
+
static code = "declaration-refused";
|
|
6786
6890
|
constructor(detail) {
|
|
6787
|
-
super(
|
|
6891
|
+
super(DeclarationRefusedError.code, detail);
|
|
6788
6892
|
this.name = "DeclarationRefusedError";
|
|
6789
6893
|
}
|
|
6790
6894
|
};
|
|
@@ -7186,6 +7290,7 @@ var RecordIntegrationInstances = class {
|
|
|
7186
7290
|
this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
|
|
7187
7291
|
this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
|
|
7188
7292
|
this.nameField = kind.declaration.entry?.nameField ?? "name";
|
|
7293
|
+
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`);
|
|
7189
7294
|
}
|
|
7190
7295
|
/**
|
|
7191
7296
|
* One instance read for the addon's own runtime; null when absent. When the keychain is
|
|
@@ -7227,10 +7332,21 @@ var RecordIntegrationInstances = class {
|
|
|
7227
7332
|
}
|
|
7228
7333
|
return out;
|
|
7229
7334
|
}
|
|
7335
|
+
/** The kind an instance body is of: the provider's `kindOf`, else its one declared kind. */
|
|
7336
|
+
kindOfBody(body) {
|
|
7337
|
+
if (this.hooks.kindOf !== void 0) {
|
|
7338
|
+
const id = this.hooks.kindOf(body);
|
|
7339
|
+
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)`);
|
|
7340
|
+
return id;
|
|
7341
|
+
}
|
|
7342
|
+
const only = this.hooks.description.kinds[0];
|
|
7343
|
+
if (only === void 0) throw new Error(`record kind '${this.kind.name}' declares no kinds`);
|
|
7344
|
+
return only.id;
|
|
7345
|
+
}
|
|
7230
7346
|
viewOf(record) {
|
|
7231
7347
|
const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
|
|
7232
7348
|
const rawName = record.data[this.nameField];
|
|
7233
|
-
const kind = this.
|
|
7349
|
+
const kind = this.kindOfBody(record.data);
|
|
7234
7350
|
const health = this.hooks.health?.(record.data, record.id);
|
|
7235
7351
|
return {
|
|
7236
7352
|
id: record.id,
|
|
@@ -7238,7 +7354,7 @@ var RecordIntegrationInstances = class {
|
|
|
7238
7354
|
configured: this.hooks.configured(record.data),
|
|
7239
7355
|
enabled: isInstanceEnabled(record.data),
|
|
7240
7356
|
formValues: shown,
|
|
7241
|
-
|
|
7357
|
+
kind,
|
|
7242
7358
|
...health?.status !== void 0 ? { status: health.status } : {},
|
|
7243
7359
|
...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
|
|
7244
7360
|
};
|
|
@@ -7271,17 +7387,35 @@ var RecordIntegrationInstances = class {
|
|
|
7271
7387
|
...kind !== void 0 ? { kind } : {}
|
|
7272
7388
|
});
|
|
7273
7389
|
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
7390
|
+
const initial = this.hooks.initialBody(name, kind);
|
|
7391
|
+
const patch = this.patchOf(values ?? {});
|
|
7392
|
+
if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
|
|
7393
|
+
kind,
|
|
7394
|
+
body: {
|
|
7395
|
+
...initial,
|
|
7396
|
+
...patch
|
|
7397
|
+
}
|
|
7398
|
+
}));
|
|
7274
7399
|
await this.handle.put({
|
|
7275
7400
|
id: minted,
|
|
7276
|
-
data:
|
|
7401
|
+
data: initial
|
|
7277
7402
|
}, { ifRev: 0 });
|
|
7278
|
-
const patch = this.patchOf(values ?? {});
|
|
7279
7403
|
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
7280
7404
|
await this.hooks.onChanged?.(minted, "created");
|
|
7281
7405
|
return { id: minted };
|
|
7282
7406
|
};
|
|
7283
7407
|
updateInstance = async ({ id, name, values }) => {
|
|
7284
7408
|
const patch = this.patchOf(values);
|
|
7409
|
+
if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
|
|
7410
|
+
const stored = await this.handle.get(id);
|
|
7411
|
+
if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
|
|
7412
|
+
kind: void 0,
|
|
7413
|
+
body: {
|
|
7414
|
+
...stored.data,
|
|
7415
|
+
...patch
|
|
7416
|
+
}
|
|
7417
|
+
}));
|
|
7418
|
+
}
|
|
7285
7419
|
if (name !== void 0) patch[this.nameField] = name;
|
|
7286
7420
|
await this.handle.patch(id, patch);
|
|
7287
7421
|
await this.hooks.onChanged?.(id, "updated");
|
|
@@ -7592,6 +7726,21 @@ var SCOPED_SETTINGS_SCOPES = [
|
|
|
7592
7726
|
"device-defaults",
|
|
7593
7727
|
"node"
|
|
7594
7728
|
];
|
|
7729
|
+
/** The one refusal of a key a scope did not declare. */
|
|
7730
|
+
function assertDeclaredKeys(keys, declared) {
|
|
7731
|
+
const undeclared = keys.filter((key) => !declared.has(key));
|
|
7732
|
+
if (undeclared.length > 0) throw new Error(`scoped settings: [${undeclared.join(", ")}] not declared for this scope (declared: [${[...declared].join(", ")}])`);
|
|
7733
|
+
}
|
|
7734
|
+
/** Build the handle over an addon's scoped read, write and unset. */
|
|
7735
|
+
function createScopedSettings(deps) {
|
|
7736
|
+
return {
|
|
7737
|
+
async get() {
|
|
7738
|
+
return deps.schema.parse(await deps.read());
|
|
7739
|
+
},
|
|
7740
|
+
patch: (patch) => deps.write(Object.fromEntries(Object.entries(patch))),
|
|
7741
|
+
unset: (keys) => deps.unset(keys)
|
|
7742
|
+
};
|
|
7743
|
+
}
|
|
7595
7744
|
/** A write to a secret path could not be stored in the keychain, so it was refused. */
|
|
7596
7745
|
var KeychainWriteRefusedError = class extends Error {
|
|
7597
7746
|
fieldPath;
|
|
@@ -8167,7 +8316,16 @@ function createHandle(raw, opts) {
|
|
|
8167
8316
|
return blob;
|
|
8168
8317
|
}
|
|
8169
8318
|
},
|
|
8170
|
-
writeAddonStore: writeAddon
|
|
8319
|
+
writeAddonStore: writeAddon,
|
|
8320
|
+
removeClearedKeys: (keys) => serialize("addon", async () => {
|
|
8321
|
+
const stored = await readStoredAddonBlob(raw);
|
|
8322
|
+
index.seen("addon", stored);
|
|
8323
|
+
const removable = keys.filter((key) => collectSecretMarkerRefs(stored[key]).length === 0);
|
|
8324
|
+
if (removable.length === 0) return;
|
|
8325
|
+
const patch = Object.fromEntries(removable.map((key) => [key, void 0]));
|
|
8326
|
+
await raw.writeAddonStore(patch);
|
|
8327
|
+
index.wrote("addon", patch);
|
|
8328
|
+
})
|
|
8171
8329
|
};
|
|
8172
8330
|
}
|
|
8173
8331
|
/** The context the addon sees, plus the private raw view and write outcome. */
|
|
@@ -8297,7 +8455,7 @@ function boundOf(list) {
|
|
|
8297
8455
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
8298
8456
|
*
|
|
8299
8457
|
* - Typed `config` property with automatic resolution from store + defaults
|
|
8300
|
-
* -
|
|
8458
|
+
* - the scoped settings API (`clusterSettings` / `nodeSettings` / `deviceDefaults`) auto-implemented
|
|
8301
8459
|
* - `getAddonSettings()` / `updateAddonSettings()` auto-implemented
|
|
8302
8460
|
* - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented
|
|
8303
8461
|
* - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)
|
|
@@ -8307,7 +8465,7 @@ function boundOf(list) {
|
|
|
8307
8465
|
* Subclasses override:
|
|
8308
8466
|
* - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]
|
|
8309
8467
|
* - `onShutdown()` — cleanup
|
|
8310
|
-
* - `
|
|
8468
|
+
* - `clusterSettingsSchema()` / `nodeSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
|
|
8311
8469
|
*
|
|
8312
8470
|
* @example
|
|
8313
8471
|
* ```ts
|
|
@@ -8321,7 +8479,7 @@ function boundOf(list) {
|
|
|
8321
8479
|
* super({ maxRetries: 3, endpoint: 'http://localhost' })
|
|
8322
8480
|
* }
|
|
8323
8481
|
*
|
|
8324
|
-
* protected
|
|
8482
|
+
* protected clusterSettingsSchema() {
|
|
8325
8483
|
* return this.schema({
|
|
8326
8484
|
* sections: [{
|
|
8327
8485
|
* id: 'main', title: 'Settings',
|
|
@@ -8364,7 +8522,7 @@ var BaseAddon = class {
|
|
|
8364
8522
|
defaults;
|
|
8365
8523
|
/**
|
|
8366
8524
|
* Hidden-state paths that hold secrets (D787) — values the addon writes
|
|
8367
|
-
* itself (`
|
|
8525
|
+
* itself (`state()`, the scoped writers) that no form field declares.
|
|
8368
8526
|
* Stored in the keychain exactly like a `password` field. Syntax:
|
|
8369
8527
|
* `'snapshotSigningKey'`, `'identity.pincode'`, `'tokens[userId].accessToken'`
|
|
8370
8528
|
* (`key[<id field>]` addresses array items by id, never by index).
|
|
@@ -8374,7 +8532,7 @@ var BaseAddon = class {
|
|
|
8374
8532
|
/**
|
|
8375
8533
|
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
8376
8534
|
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
8377
|
-
* see them. Empty until loaded
|
|
8535
|
+
* see them. Empty until loaded.
|
|
8378
8536
|
*/
|
|
8379
8537
|
_scopedByCap = /* @__PURE__ */ new Map();
|
|
8380
8538
|
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
@@ -8560,7 +8718,7 @@ var BaseAddon = class {
|
|
|
8560
8718
|
/** Addon-specific cleanup. Override if needed. */
|
|
8561
8719
|
async onShutdown() {}
|
|
8562
8720
|
/**
|
|
8563
|
-
* Called after config is resolved during
|
|
8721
|
+
* Called after config is resolved during a settings write.
|
|
8564
8722
|
* Override to react to config changes (e.g. restart a sampler, reconnect a service).
|
|
8565
8723
|
* Not called during initialize() — use onInitialize() for initial setup.
|
|
8566
8724
|
*/
|
|
@@ -8589,82 +8747,12 @@ var BaseAddon = class {
|
|
|
8589
8747
|
scopedSettingsCapabilities() {
|
|
8590
8748
|
return [];
|
|
8591
8749
|
}
|
|
8592
|
-
/** Override to provide global-level settings UI schema. */
|
|
8593
|
-
globalSettingsSchema(_cap) {
|
|
8594
|
-
return null;
|
|
8595
|
-
}
|
|
8596
8750
|
/** Override to provide device-level settings UI schema. */
|
|
8597
8751
|
deviceSettingsSchema() {
|
|
8598
8752
|
return null;
|
|
8599
8753
|
}
|
|
8600
8754
|
/**
|
|
8601
|
-
*
|
|
8602
|
-
* ARE the configuration of its integration.
|
|
8603
|
-
*
|
|
8604
|
-
* Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
|
|
8605
|
-
* operator should find on the addon's integration page (System →
|
|
8606
|
-
* Integrations → <name>) rather than only in the cluster-wide list of every
|
|
8607
|
-
* addon. Empty (the default) means the addon has no integration-level
|
|
8608
|
-
* settings and no such surface is offered — this is opt-in, because whether
|
|
8609
|
-
* an addon's configuration IS its integration's configuration depends on the
|
|
8610
|
-
* nature of the integration.
|
|
8611
|
-
*
|
|
8612
|
-
* WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
|
|
8613
|
-
* the ONE global schema, in the ONE addon store, written by the ONE
|
|
8614
|
-
* `updateGlobalSettings` path. There is deliberately no
|
|
8615
|
-
* `updateIntegrationSettings`: a second write path is how a surface acquires
|
|
8616
|
-
* a second store key, and this repo has shipped that twice (`btmPath@hub`,
|
|
8617
|
-
* D266). Selecting sections cannot introduce a key that selecting cannot.
|
|
8618
|
-
*
|
|
8619
|
-
* WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
|
|
8620
|
-
* `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
|
|
8621
|
-
* removed with the reason recorded at
|
|
8622
|
-
* `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
|
|
8623
|
-
* determined by WHICH schema it lives in, not by a field-level marker."* A
|
|
8624
|
-
* marker sprinkled across sections also has to borrow a field that already
|
|
8625
|
-
* means something else; borrowing `section.tab` put the literal word
|
|
8626
|
-
* "integration" into an operator-facing tab bar, because `tab` means "how to
|
|
8627
|
-
* GROUP this visually" and cannot also mean "where this lives" (D269
|
|
8628
|
-
* supersedes D268). One declaration, in one place, next to the schema whose
|
|
8629
|
-
* ids it names.
|
|
8630
|
-
*/
|
|
8631
|
-
integrationSettingSections() {
|
|
8632
|
-
return [];
|
|
8633
|
-
}
|
|
8634
|
-
async getGlobalSettings(overlay, cap, nodeId) {
|
|
8635
|
-
await this.loadScopedSchemas(cap);
|
|
8636
|
-
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
8637
|
-
if (!schema) return { sections: [] };
|
|
8638
|
-
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
8639
|
-
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
8640
|
-
}
|
|
8641
|
-
/**
|
|
8642
|
-
* The integration-level view of this addon's settings: exactly the sections
|
|
8643
|
-
* named by {@link integrationSettingSections}, hydrated from the SAME store
|
|
8644
|
-
* `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
|
|
8645
|
-
*
|
|
8646
|
-
* Returns `null` when the addon declared nothing — an addon that opts out has
|
|
8647
|
-
* no integration settings surface at all, rather than an empty one that reads
|
|
8648
|
-
* as a failed load.
|
|
8649
|
-
*
|
|
8650
|
-
* Three properties hold BY CONSTRUCTION, which is why they are here in core
|
|
8651
|
-
* and not in whichever UI happens to render this:
|
|
8652
|
-
*
|
|
8653
|
-
* 1. **One key.** The payload is a SUBSET of the global schema, so a field
|
|
8654
|
-
* shown here is the same field, with the same bare key, that the addon's
|
|
8655
|
-
* own page shows. There is no integration-specific writer — callers save
|
|
8656
|
-
* through `updateGlobalSettings` — so a second store key is unreachable,
|
|
8657
|
-
* not merely discouraged.
|
|
8658
|
-
* 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
|
|
8659
|
-
* is `<key>@<nodeId>` and an integration is not a node; whichever node
|
|
8660
|
-
* such a field silently picked would be a wrong answer for the operator
|
|
8661
|
-
* who opened the page (D266).
|
|
8662
|
-
* 3. **No silent typo.** A declared id that names no section throws. The
|
|
8663
|
-
* alternative — skip it — turns a rename into a surface that quietly
|
|
8664
|
-
* empties, which looks exactly like an addon with nothing to configure.
|
|
8665
|
-
*/
|
|
8666
|
-
/**
|
|
8667
|
-
* The form schema of ONE integration instance, or `null` to use {@link globalSettingsSchema}.
|
|
8755
|
+
* The form schema of ONE integration instance, or `null` to use the scoped form.
|
|
8668
8756
|
*
|
|
8669
8757
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
8670
8758
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
@@ -8712,37 +8800,178 @@ var BaseAddon = class {
|
|
|
8712
8800
|
}
|
|
8713
8801
|
}
|
|
8714
8802
|
/**
|
|
8803
|
+
* Hook: values layered over the stored ones when the FORM of one scope is served
|
|
8804
|
+
* ({@link getScopedSettings}) — a preview that derives dependent options without writing.
|
|
8805
|
+
* Never applied to {@link ScopedSettings.get} or to a write. `null` ⇒ no overlay.
|
|
8806
|
+
*/
|
|
8807
|
+
async scopedSettingsOverlay(_scope, _cap, _nodeId) {
|
|
8808
|
+
return null;
|
|
8809
|
+
}
|
|
8810
|
+
/**
|
|
8811
|
+
* Hook: a write of one scope changed stored values (not called when the store reports no change,
|
|
8812
|
+
* e.g. the write-back of a stored secret). `keys` are the bare field keys of the write, node keys included.
|
|
8813
|
+
*/
|
|
8814
|
+
async onScopedSettingsChanged(_scope, _cap, _keys) {}
|
|
8815
|
+
/** Typed handle over the CLUSTER settings `cap` declares (`clusterSettingsSchema`). */
|
|
8816
|
+
clusterSettings(cap, schema) {
|
|
8817
|
+
return this.scopedHandle("cluster", cap, schema);
|
|
8818
|
+
}
|
|
8819
|
+
/** Typed handle over the fleet defaults of the per-device settings `cap` declares. */
|
|
8820
|
+
deviceDefaults(cap, schema) {
|
|
8821
|
+
return this.scopedHandle("device-defaults", cap, schema);
|
|
8822
|
+
}
|
|
8823
|
+
/**
|
|
8824
|
+
* Typed handle over the settings `cap` owns PER NODE. `nodeId` omitted ⇒ the local node. A node
|
|
8825
|
+
* with no row yields the schema defaults — never a bare-key fallback.
|
|
8826
|
+
*/
|
|
8827
|
+
nodeSettings(cap, schema, nodeId) {
|
|
8828
|
+
return this.scopedHandle("node", cap, schema, nodeId);
|
|
8829
|
+
}
|
|
8830
|
+
/**
|
|
8831
|
+
* {@link state} for a value that is kept per node: stored under `<key>@<nodeId>`, and the
|
|
8832
|
+
* settings store carries `nodeId` in the row data (D986 §1), so a forgotten node's rows go in one
|
|
8833
|
+
* central delete. `nodeId` omitted ⇒ the local node.
|
|
8834
|
+
*/
|
|
8835
|
+
nodeState(key, schema, fallback, nodeId) {
|
|
8836
|
+
this._declaredStateKeys.add(key);
|
|
8837
|
+
return createDurableState({
|
|
8838
|
+
key: nodeScopedKey(key, nodeId ?? this._ctx?.kernel?.localNodeId ?? ""),
|
|
8839
|
+
schema,
|
|
8840
|
+
fallback,
|
|
8841
|
+
read: async () => this.resolveStoredSecrets(await this.readAddonStoreWithRetry()),
|
|
8842
|
+
write: async (patch) => {
|
|
8843
|
+
await this._ctx?.settings?.writeAddonStore(patch);
|
|
8844
|
+
},
|
|
8845
|
+
onParseError: (k, e) => this._ctx?.logger?.warn?.(`durable-state: stored "${k}" failed validation — using fallback`, { meta: {
|
|
8846
|
+
addonId: this._ctx?.id,
|
|
8847
|
+
key: k,
|
|
8848
|
+
error: String(e)
|
|
8849
|
+
} })
|
|
8850
|
+
});
|
|
8851
|
+
}
|
|
8852
|
+
/** The loaded schema `cap` declares for `scope`, or `null`. */
|
|
8853
|
+
async declaredScopedSchema(scope, cap) {
|
|
8854
|
+
await this.loadScopedSchemas(cap);
|
|
8855
|
+
return this.loadedScopedSchemas(cap).find((entry) => entry.scope === scope)?.schema ?? null;
|
|
8856
|
+
}
|
|
8857
|
+
async requireScopedSchema(scope, cap) {
|
|
8858
|
+
const schema = await this.declaredScopedSchema(scope, cap);
|
|
8859
|
+
if (schema === null) throw new Error(`${this.constructor.name}: cap "${cap}" declares no ${scope} settings`);
|
|
8860
|
+
return schema;
|
|
8861
|
+
}
|
|
8862
|
+
scopedKeys(schema) {
|
|
8863
|
+
return new Set(schema.sections.flatMap((section) => collectValueFieldKeys(section.fields)));
|
|
8864
|
+
}
|
|
8865
|
+
/** `store` narrowed to the keys `schema` declares. */
|
|
8866
|
+
narrowToScope(store, schema) {
|
|
8867
|
+
const keys = this.scopedKeys(schema);
|
|
8868
|
+
return Object.fromEntries(Object.entries(store).filter(([key]) => keys.has(key)));
|
|
8869
|
+
}
|
|
8870
|
+
scopedHandle(scope, cap, schema, nodeId) {
|
|
8871
|
+
return createScopedSettings({
|
|
8872
|
+
schema,
|
|
8873
|
+
read: async () => {
|
|
8874
|
+
const declared = await this.requireScopedSchema(scope, cap);
|
|
8875
|
+
const stored = this.narrowToScope(await this.resolveGlobalStore(nodeId, cap), declared);
|
|
8876
|
+
const effective = collectHydratedFieldValues(hydrateSchema(declared, stored));
|
|
8877
|
+
return {
|
|
8878
|
+
...Object.fromEntries([...effective].filter(([, value]) => value !== void 0)),
|
|
8879
|
+
...stored
|
|
8880
|
+
};
|
|
8881
|
+
},
|
|
8882
|
+
write: (patch) => this.writeScoped(scope, cap, patch, nodeId),
|
|
8883
|
+
unset: (keys) => this.unsetScoped(scope, cap, keys, nodeId)
|
|
8884
|
+
});
|
|
8885
|
+
}
|
|
8886
|
+
/** The one write path of the scoped API: declared keys only, then the global patch. */
|
|
8887
|
+
async writeScoped(scope, cap, patch, nodeId) {
|
|
8888
|
+
assertDeclaredKeys(Object.keys(patch), this.scopedKeys(await this.requireScopedSchema(scope, cap)));
|
|
8889
|
+
if (await this.applyGlobalPatch(patch, scope === "node" ? nodeId : void 0, cap)) await this.onScopedSettingsChanged(scope, cap, Object.keys(patch));
|
|
8890
|
+
}
|
|
8891
|
+
/**
|
|
8892
|
+
* Remove declared keys of one scope. A secret goes through {@link clearSecrets} — its keychain
|
|
8893
|
+
* value is released exactly as there — and the emptied row is then removed; every other key is
|
|
8894
|
+
* removed by an `undefined` patch (which a secret transform would read as "keep").
|
|
8895
|
+
*/
|
|
8896
|
+
async unsetScoped(scope, cap, keys, nodeId) {
|
|
8897
|
+
assertDeclaredKeys(keys, this.scopedKeys(await this.requireScopedSchema(scope, cap)));
|
|
8898
|
+
const target = scope === "node" ? nodeId : void 0;
|
|
8899
|
+
const tree = this.addonSecretTree();
|
|
8900
|
+
const secretKeys = keys.filter((key) => tree.has(key));
|
|
8901
|
+
const plainKeys = keys.filter((key) => !tree.has(key));
|
|
8902
|
+
let changed = false;
|
|
8903
|
+
if (plainKeys.length > 0) {
|
|
8904
|
+
const patch = Object.fromEntries(plainKeys.map((key) => [key, void 0]));
|
|
8905
|
+
changed = await this.applyGlobalPatch(patch, target, cap) || changed;
|
|
8906
|
+
}
|
|
8907
|
+
if (secretKeys.length > 0) {
|
|
8908
|
+
await this.clearSecretKeys(secretKeys, target, cap);
|
|
8909
|
+
const node = normalizeNodeId(target ?? this._ctx?.kernel?.localNodeId);
|
|
8910
|
+
const rows = secretKeys.map((key) => scope === "node" ? nodeScopedKey(key, node) : key);
|
|
8911
|
+
if (this._secretSettings !== null) await this._secretSettings.removeClearedKeys(rows);
|
|
8912
|
+
else await this._ctx?.settings?.writeAddonStore(Object.fromEntries(rows.map((row) => [row, void 0])));
|
|
8913
|
+
changed = true;
|
|
8914
|
+
}
|
|
8915
|
+
if (changed) await this.onScopedSettingsChanged(scope, cap, [...keys]);
|
|
8916
|
+
}
|
|
8917
|
+
/**
|
|
8918
|
+
* The hydrated form of one scope of one capability's settings (any
|
|
8919
|
+
* {@link scopedSettingsOverlay} applied), or `null` when the capability declares nothing there.
|
|
8920
|
+
*/
|
|
8921
|
+
async getScopedSettings(scope, cap, nodeId) {
|
|
8922
|
+
const schema = await this.declaredScopedSchema(scope, cap);
|
|
8923
|
+
if (schema === null) return null;
|
|
8924
|
+
const stored = this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema);
|
|
8925
|
+
const overlay = await this.scopedSettingsOverlay(scope, cap, nodeId);
|
|
8926
|
+
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(stored, overlay) : stored, { inlineSecretsAsViews: true });
|
|
8927
|
+
}
|
|
8928
|
+
/**
|
|
8929
|
+
* The scoped schemas this addon serves: one entry per loaded (capability, scope), capabilities
|
|
8930
|
+
* in declaration order, scopes in {@link SCOPED_SETTINGS_SCOPES} order. `[]` when it declares
|
|
8931
|
+
* nothing.
|
|
8932
|
+
*/
|
|
8933
|
+
async listScopedSettings() {
|
|
8934
|
+
await this.loadScopedSchemas();
|
|
8935
|
+
return [...this._scopedByCap].flatMap(([cap, entries]) => entries.map((entry) => ({
|
|
8936
|
+
cap,
|
|
8937
|
+
scope: entry.scope,
|
|
8938
|
+
sectionIds: entry.schema.sections.map((section) => section.id)
|
|
8939
|
+
})));
|
|
8940
|
+
}
|
|
8941
|
+
/** The OPERATOR's write of declared keys of one scope (D787 secret semantics). */
|
|
8942
|
+
async updateScopedSettings(scope, cap, patch, nodeId) {
|
|
8943
|
+
this._operatorWriteDepth++;
|
|
8944
|
+
try {
|
|
8945
|
+
await this.writeScoped(scope, cap, patch, nodeId);
|
|
8946
|
+
} finally {
|
|
8947
|
+
this._operatorWriteDepth--;
|
|
8948
|
+
}
|
|
8949
|
+
}
|
|
8950
|
+
/**
|
|
8951
|
+
* The effective values (`stored ?? default`) of one scope, a secret as its `set` view — never
|
|
8952
|
+
* its value. `null` when the capability declares nothing for the scope.
|
|
8953
|
+
*/
|
|
8954
|
+
async readScopedValues(scope, cap, nodeId) {
|
|
8955
|
+
const schema = await this.declaredScopedSchema(scope, cap);
|
|
8956
|
+
if (schema === null) return null;
|
|
8957
|
+
const form = hydrateSchema(schema, this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema), { inlineSecretsAsViews: true });
|
|
8958
|
+
return Object.fromEntries([...collectHydratedFieldValues(form)].filter(([, value]) => value !== void 0));
|
|
8959
|
+
}
|
|
8960
|
+
/**
|
|
8715
8961
|
* What the create wizard and the integration edit page render for one instance: the addon's
|
|
8716
8962
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8717
8963
|
*/
|
|
8718
8964
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8719
8965
|
const scoped = await this.integrationFormSchema(instance);
|
|
8720
|
-
if (scoped === null) return this.
|
|
8966
|
+
if (scoped === null) return this.scopedForm(nodeId);
|
|
8721
8967
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8722
8968
|
}
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
const schema = this.
|
|
8727
|
-
if (!schema)
|
|
8728
|
-
|
|
8729
|
-
const sections = [];
|
|
8730
|
-
for (const id of declared) {
|
|
8731
|
-
const section = byId.get(id);
|
|
8732
|
-
if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
|
|
8733
|
-
const fields = dropPerNodeFields(section.fields);
|
|
8734
|
-
if (fields.length === 0) continue;
|
|
8735
|
-
sections.push({
|
|
8736
|
-
...section,
|
|
8737
|
-
fields
|
|
8738
|
-
});
|
|
8739
|
-
}
|
|
8740
|
-
if (sections.length === 0) return null;
|
|
8741
|
-
const projected = await this.readRawProjectedStore(nodeId);
|
|
8742
|
-
return hydrateSchema({
|
|
8743
|
-
...schema,
|
|
8744
|
-
sections
|
|
8745
|
-
}, projected, { inlineSecretsAsViews: true });
|
|
8969
|
+
/** The form of the scoped schemas this addon declared, hydrated from its store. */
|
|
8970
|
+
async scopedForm(nodeId) {
|
|
8971
|
+
await this.loadScopedSchemas();
|
|
8972
|
+
const schema = this.scopedFormSchema();
|
|
8973
|
+
if (!schema) return { sections: [] };
|
|
8974
|
+
return hydrateSchema(schema, await this.readRawProjectedStore(nodeId), { inlineSecretsAsViews: true });
|
|
8746
8975
|
}
|
|
8747
8976
|
/**
|
|
8748
8977
|
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
@@ -8751,8 +8980,8 @@ var BaseAddon = class {
|
|
|
8751
8980
|
* bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
|
|
8752
8981
|
* A no-op passthrough when the schema declares no `perNode` field.
|
|
8753
8982
|
*
|
|
8754
|
-
* This is the sanctioned way for
|
|
8755
|
-
* the store for custom
|
|
8983
|
+
* This is the sanctioned way for an addon that needs
|
|
8984
|
+
* the store for custom logic (option narrowing, value snapping) to
|
|
8756
8985
|
* read it per-node — never `ctx.settings.readAddonStore()` directly.
|
|
8757
8986
|
* `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
|
|
8758
8987
|
*
|
|
@@ -8804,7 +9033,7 @@ var BaseAddon = class {
|
|
|
8804
9033
|
}
|
|
8805
9034
|
/**
|
|
8806
9035
|
* Whether ONE global key of this addon's store was written, read three-way
|
|
8807
|
-
* (D651). For a question a hydrated view cannot answer:
|
|
9036
|
+
* (D651). For a question a hydrated view cannot answer: a hydrated form
|
|
8808
9037
|
* reports `value ?? default`, so "the operator chose the default" and "nobody
|
|
8809
9038
|
* chose" look the same, and a failed read looks like both. A `perNode` key is
|
|
8810
9039
|
* not supported here: its stored name is scoped.
|
|
@@ -8838,7 +9067,7 @@ var BaseAddon = class {
|
|
|
8838
9067
|
}
|
|
8839
9068
|
/**
|
|
8840
9069
|
* The OPERATOR's write — a settings form or an admin API call, dispatched by
|
|
8841
|
-
* the kernel.
|
|
9070
|
+
* the kernel. The one in-process write of the addon blob that is not a scoped write; a secret
|
|
8842
9071
|
* the operator sends is stored as sent even when it equals a pre-keychain
|
|
8843
9072
|
* inline value: the operator re-entering a password is a change, an addon
|
|
8844
9073
|
* writing back what it read is not (D787).
|
|
@@ -8851,9 +9080,6 @@ var BaseAddon = class {
|
|
|
8851
9080
|
this._operatorWriteDepth--;
|
|
8852
9081
|
}
|
|
8853
9082
|
}
|
|
8854
|
-
async updateGlobalSettings(patch, nodeId) {
|
|
8855
|
-
await this.applyGlobalPatch(patch, nodeId);
|
|
8856
|
-
}
|
|
8857
9083
|
/**
|
|
8858
9084
|
* REMOVE stored secrets — their keychain values and their markers (D787).
|
|
8859
9085
|
*
|
|
@@ -8863,6 +9089,9 @@ var BaseAddon = class {
|
|
|
8863
9089
|
* a keychain (an older kernel) it writes `''`, as such code always did.
|
|
8864
9090
|
*/
|
|
8865
9091
|
async clearSecrets(keys, nodeId) {
|
|
9092
|
+
await this.clearSecretKeys(keys, nodeId);
|
|
9093
|
+
}
|
|
9094
|
+
async clearSecretKeys(keys, nodeId, cap) {
|
|
8866
9095
|
const tree = this.addonSecretTree();
|
|
8867
9096
|
const keychain = this._ctx?.secrets !== void 0;
|
|
8868
9097
|
const stored = keychain ? await this.readAddonStoreWithRetry() : {};
|
|
@@ -8875,25 +9104,26 @@ var BaseAddon = class {
|
|
|
8875
9104
|
const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
|
|
8876
9105
|
patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
|
|
8877
9106
|
}
|
|
8878
|
-
await this.applyGlobalPatch(patch, nodeId);
|
|
9107
|
+
await this.applyGlobalPatch(patch, nodeId, cap);
|
|
8879
9108
|
}
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
9109
|
+
/** Resolves `true` when the write changed stored values. */
|
|
9110
|
+
async applyGlobalPatch(barePatch, nodeId, cap) {
|
|
9111
|
+
await this.loadScopedSchemas(cap);
|
|
9112
|
+
const keys = this.perNodeKeys(cap);
|
|
8883
9113
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8884
9114
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
8885
9115
|
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
8886
9116
|
const handle = this._secretSettings;
|
|
8887
9117
|
if (handle !== null) {
|
|
8888
9118
|
const { changed } = await handle.writeAddonStore(scoped);
|
|
8889
|
-
if (!changed) return;
|
|
9119
|
+
if (!changed) return false;
|
|
8890
9120
|
} else await this._ctx?.settings?.writeAddonStore(scoped);
|
|
8891
|
-
if (target !== localNode) return;
|
|
9121
|
+
if (target !== localNode) return true;
|
|
8892
9122
|
await this.resolveConfig();
|
|
8893
9123
|
await this.onConfigChanged();
|
|
8894
9124
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8895
|
-
|
|
8896
|
-
|
|
9125
|
+
this.maybeAutoRestart(Object.keys(barePatch), this.loadedScopedSchemas().map((entry) => entry.schema));
|
|
9126
|
+
return true;
|
|
8897
9127
|
}
|
|
8898
9128
|
/**
|
|
8899
9129
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8930,10 +9160,7 @@ var BaseAddon = class {
|
|
|
8930
9160
|
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8931
9161
|
this._addonSecretTree = null;
|
|
8932
9162
|
}
|
|
8933
|
-
/**
|
|
8934
|
-
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8935
|
-
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8936
|
-
*/
|
|
9163
|
+
/** The form of an addon's scoped schemas: their sections, each shown once. */
|
|
8937
9164
|
scopedFormSchema(cap) {
|
|
8938
9165
|
const byId = /* @__PURE__ */ new Map();
|
|
8939
9166
|
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
@@ -8945,8 +9172,7 @@ var BaseAddon = class {
|
|
|
8945
9172
|
return [...this._scopedByCap.values()].flat();
|
|
8946
9173
|
}
|
|
8947
9174
|
/**
|
|
8948
|
-
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}
|
|
8949
|
-
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
9175
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}. Derived once
|
|
8950
9176
|
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8951
9177
|
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8952
9178
|
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
@@ -8955,8 +9181,7 @@ var BaseAddon = class {
|
|
|
8955
9181
|
const cacheKey = cap ?? "";
|
|
8956
9182
|
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8957
9183
|
if (cached) return cached;
|
|
8958
|
-
const
|
|
8959
|
-
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
9184
|
+
const keys = /* @__PURE__ */ new Set();
|
|
8960
9185
|
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8961
9186
|
if (scope !== "node") continue;
|
|
8962
9187
|
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
@@ -9174,8 +9399,6 @@ var BaseAddon = class {
|
|
|
9174
9399
|
}
|
|
9175
9400
|
declaredKeySet() {
|
|
9176
9401
|
const keys = new Set(Object.keys(this.defaults));
|
|
9177
|
-
const schema = this.globalSettingsSchema();
|
|
9178
|
-
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
9179
9402
|
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
9180
9403
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
9181
9404
|
for (const path of this.secretStatePaths) {
|
|
@@ -9273,7 +9496,7 @@ var BaseAddon = class {
|
|
|
9273
9496
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
9274
9497
|
addonSecretTree() {
|
|
9275
9498
|
if (this._addonSecretTree === null) {
|
|
9276
|
-
const tree = new Map(
|
|
9499
|
+
const tree = /* @__PURE__ */ new Map();
|
|
9277
9500
|
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
9278
9501
|
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
9279
9502
|
}
|
|
@@ -9485,47 +9708,6 @@ function classifyStoreError(err) {
|
|
|
9485
9708
|
if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
|
|
9486
9709
|
return "permanent";
|
|
9487
9710
|
}
|
|
9488
|
-
/**
|
|
9489
|
-
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
9490
|
-
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
9491
|
-
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
9492
|
-
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
9493
|
-
*/
|
|
9494
|
-
/**
|
|
9495
|
-
* The same fields with every `perNode: true` one removed, recursing into layout
|
|
9496
|
-
* containers exactly as {@link collectPerNodeFieldKeys} does. A container left
|
|
9497
|
-
* with no child is dropped rather than rendered empty.
|
|
9498
|
-
*
|
|
9499
|
-
* Used by `getIntegrationSettings`: an integration is not a node, so a field
|
|
9500
|
-
* whose store key is `<key>@<nodeId>` has no node to belong to there.
|
|
9501
|
-
*/
|
|
9502
|
-
function dropPerNodeFields(fields) {
|
|
9503
|
-
const kept = [];
|
|
9504
|
-
for (const field of fields) {
|
|
9505
|
-
if (field.type === "group") {
|
|
9506
|
-
const inner = dropPerNodeFields(field.fields);
|
|
9507
|
-
if (inner.length > 0) kept.push({
|
|
9508
|
-
...field,
|
|
9509
|
-
fields: inner
|
|
9510
|
-
});
|
|
9511
|
-
continue;
|
|
9512
|
-
}
|
|
9513
|
-
if (field.type === "sub-tabs") {
|
|
9514
|
-
const tabs = field.tabs.map((tab) => ({
|
|
9515
|
-
...tab,
|
|
9516
|
-
fields: dropPerNodeFields(tab.fields)
|
|
9517
|
-
})).filter((tab) => tab.fields.length > 0);
|
|
9518
|
-
if (tabs.length > 0) kept.push({
|
|
9519
|
-
...field,
|
|
9520
|
-
tabs
|
|
9521
|
-
});
|
|
9522
|
-
continue;
|
|
9523
|
-
}
|
|
9524
|
-
if ("perNode" in field && field.perNode === true) continue;
|
|
9525
|
-
kept.push(field);
|
|
9526
|
-
}
|
|
9527
|
-
return kept;
|
|
9528
|
-
}
|
|
9529
9711
|
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
9530
9712
|
function collectValueFieldKeys(fields) {
|
|
9531
9713
|
const collected = [];
|
|
@@ -9558,21 +9740,6 @@ function collectFieldKeys(fields) {
|
|
|
9558
9740
|
}
|
|
9559
9741
|
return collected;
|
|
9560
9742
|
}
|
|
9561
|
-
function collectPerNodeFieldKeys(fields) {
|
|
9562
|
-
const collected = [];
|
|
9563
|
-
for (const field of fields) {
|
|
9564
|
-
if (field.type === "group") {
|
|
9565
|
-
collected.push(...collectPerNodeFieldKeys(field.fields));
|
|
9566
|
-
continue;
|
|
9567
|
-
}
|
|
9568
|
-
if (field.type === "sub-tabs") {
|
|
9569
|
-
for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
|
|
9570
|
-
continue;
|
|
9571
|
-
}
|
|
9572
|
-
if ("perNode" in field && field.perNode === true) collected.push(field.key);
|
|
9573
|
-
}
|
|
9574
|
-
return collected;
|
|
9575
|
-
}
|
|
9576
9743
|
/**
|
|
9577
9744
|
* Normalize an `ICamstackAddon.initialize()` return value into the
|
|
9578
9745
|
* `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
|
|
@@ -9588,30 +9755,98 @@ function normalizeAddonInitResult(result) {
|
|
|
9588
9755
|
if (Array.isArray(result)) return { providers: result };
|
|
9589
9756
|
return result;
|
|
9590
9757
|
}
|
|
9758
|
+
object({
|
|
9759
|
+
field: string().min(1),
|
|
9760
|
+
on: literal("advance")
|
|
9761
|
+
});
|
|
9591
9762
|
/**
|
|
9592
|
-
*
|
|
9593
|
-
*
|
|
9594
|
-
*
|
|
9763
|
+
* Output schema shared by the contribution + live methods.
|
|
9764
|
+
*
|
|
9765
|
+
* Mirrors the `ConfigUISchemaWithValues` shape (sections[] + optional
|
|
9766
|
+
* tabs[]) without importing from `../interfaces/config-ui.js` — a
|
|
9767
|
+
* concrete-but-lenient Zod object keeps tRPC output inference happy
|
|
9768
|
+
* (using `z.unknown()` here collapses unrelated router branches to
|
|
9769
|
+
* `unknown` when the generator re-inlines the huge AppRouter type).
|
|
9770
|
+
*
|
|
9771
|
+
* `.passthrough()` on sections/fields accepts whatever FormBuilder
|
|
9772
|
+
* extensions the caller adds (showWhen, displayScale, …) without
|
|
9773
|
+
* rebuilding every time a new field kind is introduced.
|
|
9595
9774
|
*/
|
|
9596
|
-
var
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9775
|
+
var ContributionSectionSchema = object({
|
|
9776
|
+
id: string(),
|
|
9777
|
+
title: string(),
|
|
9778
|
+
description: string().optional(),
|
|
9779
|
+
style: _enum(["card", "accordion"]).optional(),
|
|
9780
|
+
defaultCollapsed: boolean().optional(),
|
|
9781
|
+
columns: union([
|
|
9782
|
+
literal(1),
|
|
9783
|
+
literal(2),
|
|
9784
|
+
literal(3),
|
|
9785
|
+
literal(4)
|
|
9786
|
+
]).optional(),
|
|
9787
|
+
tab: string().optional(),
|
|
9788
|
+
location: _enum([
|
|
9789
|
+
"settings",
|
|
9790
|
+
"top-tab",
|
|
9791
|
+
"hero"
|
|
9792
|
+
]).optional(),
|
|
9793
|
+
order: number().optional(),
|
|
9794
|
+
fields: array(any())
|
|
9614
9795
|
});
|
|
9796
|
+
object({
|
|
9797
|
+
tabs: array(object({
|
|
9798
|
+
id: string(),
|
|
9799
|
+
label: string(),
|
|
9800
|
+
icon: string(),
|
|
9801
|
+
order: number().optional()
|
|
9802
|
+
})).optional(),
|
|
9803
|
+
sections: array(ContributionSectionSchema)
|
|
9804
|
+
}).nullable();
|
|
9805
|
+
object({ deviceId: number() }), object({ deviceId: number() }), object({
|
|
9806
|
+
deviceId: number(),
|
|
9807
|
+
patch: record(string(), unknown())
|
|
9808
|
+
}), object({ success: literal(true) });
|
|
9809
|
+
object({ deviceId: number() }), unknown().nullable();
|
|
9810
|
+
function method(input, output, options) {
|
|
9811
|
+
return {
|
|
9812
|
+
input,
|
|
9813
|
+
output,
|
|
9814
|
+
kind: options?.kind ?? "query",
|
|
9815
|
+
...options?.auth !== void 0 ? { auth: options.auth } : {},
|
|
9816
|
+
...options?.access !== void 0 ? { access: options.access } : {},
|
|
9817
|
+
...options?.role !== void 0 ? { role: options.role } : {},
|
|
9818
|
+
...options?.caller !== void 0 ? { caller: options.caller } : {},
|
|
9819
|
+
timeoutMs: options?.timeoutMs
|
|
9820
|
+
};
|
|
9821
|
+
}
|
|
9822
|
+
/**
|
|
9823
|
+
* A wrapper/system-only method: served exclusively by the cap's system-level
|
|
9824
|
+
* provider (`InferProvider`), and OPTIONAL on `InferNativeProvider` so per-device
|
|
9825
|
+
* driver natives don't stub out a wrapper concern (e.g. a cross-device cache
|
|
9826
|
+
* overview). The `systemOnly: true` literal is what `InferNativeProvider` keys on.
|
|
9827
|
+
*/
|
|
9828
|
+
function systemMethod(input, output, options) {
|
|
9829
|
+
return {
|
|
9830
|
+
...method(input, output, options),
|
|
9831
|
+
systemOnly: true
|
|
9832
|
+
};
|
|
9833
|
+
}
|
|
9834
|
+
/**
|
|
9835
|
+
* A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
|
|
9836
|
+
* on `InferProvider`. The `providerOptional: true` literal is what
|
|
9837
|
+
* `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
|
|
9838
|
+
* for when this is the honest answer and when it is a soft stub.
|
|
9839
|
+
*/
|
|
9840
|
+
function optionalMethod(input, output, options) {
|
|
9841
|
+
return {
|
|
9842
|
+
...method(input, output, options),
|
|
9843
|
+
providerOptional: true
|
|
9844
|
+
};
|
|
9845
|
+
}
|
|
9846
|
+
/** Shorthand to define an event schema */
|
|
9847
|
+
function event(data) {
|
|
9848
|
+
return { data };
|
|
9849
|
+
}
|
|
9615
9850
|
function defOf(schema) {
|
|
9616
9851
|
const zodBag = Reflect.get(schema, "_zod");
|
|
9617
9852
|
if (typeof zodBag !== "object" || zodBag === null) return {};
|
|
@@ -9868,98 +10103,30 @@ function defineEntryKind(name, spec) {
|
|
|
9868
10103
|
if (defined === void 0) throw new DeclarationRefusedError(`entry kind "${name}" was not built`);
|
|
9869
10104
|
return defined;
|
|
9870
10105
|
}
|
|
9871
|
-
object({
|
|
9872
|
-
field: string().min(1),
|
|
9873
|
-
on: literal("advance")
|
|
9874
|
-
});
|
|
9875
10106
|
/**
|
|
9876
|
-
*
|
|
9877
|
-
*
|
|
9878
|
-
*
|
|
9879
|
-
* tabs[]) without importing from `../interfaces/config-ui.js` — a
|
|
9880
|
-
* concrete-but-lenient Zod object keeps tRPC output inference happy
|
|
9881
|
-
* (using `z.unknown()` here collapses unrelated router branches to
|
|
9882
|
-
* `unknown` when the generator re-inlines the huge AppRouter type).
|
|
9883
|
-
*
|
|
9884
|
-
* `.passthrough()` on sections/fields accepts whatever FormBuilder
|
|
9885
|
-
* extensions the caller adds (showWhen, displayScale, …) without
|
|
9886
|
-
* rebuilding every time a new field kind is introduced.
|
|
10107
|
+
* The share-view scope kinds a share-link token can carry. The hub mints
|
|
10108
|
+
* and validates them (`ShareViewGrantSchema` builds its
|
|
10109
|
+
* enum from this list); a data plane names the kinds it ACCEPTS in `share`.
|
|
9887
10110
|
*/
|
|
9888
|
-
var
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
order: number().optional(),
|
|
9907
|
-
fields: array(any())
|
|
10111
|
+
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
10112
|
+
/** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
|
|
10113
|
+
var PeerBytesTicketSchema = object({
|
|
10114
|
+
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
10115
|
+
url: string().min(1),
|
|
10116
|
+
/**
|
|
10117
|
+
* The HOST node this URL means something on — the hub or a named agent,
|
|
10118
|
+
* never a runner. {@link AddonPeerBytes.open} compares it to its own and
|
|
10119
|
+
* refuses `cross-node` by name when they differ, without dialling.
|
|
10120
|
+
*/
|
|
10121
|
+
hostNodeId: string().min(1),
|
|
10122
|
+
expiresAtMs: number().int().nonnegative(),
|
|
10123
|
+
/**
|
|
10124
|
+
* What the producer DECLARED the body to be, when it knows — `null` when it
|
|
10125
|
+
* does not. Never `0` for unknown (D393): a consumer sizing a bound off this
|
|
10126
|
+
* must be able to tell "the producer did not say" from "the body is empty".
|
|
10127
|
+
*/
|
|
10128
|
+
declaredBytes: number().int().nonnegative().nullable()
|
|
9908
10129
|
});
|
|
9909
|
-
object({
|
|
9910
|
-
tabs: array(object({
|
|
9911
|
-
id: string(),
|
|
9912
|
-
label: string(),
|
|
9913
|
-
icon: string(),
|
|
9914
|
-
order: number().optional()
|
|
9915
|
-
})).optional(),
|
|
9916
|
-
sections: array(ContributionSectionSchema)
|
|
9917
|
-
}).nullable();
|
|
9918
|
-
object({ deviceId: number() }), object({ deviceId: number() }), object({
|
|
9919
|
-
deviceId: number(),
|
|
9920
|
-
patch: record(string(), unknown())
|
|
9921
|
-
}), object({ success: literal(true) });
|
|
9922
|
-
object({ deviceId: number() }), unknown().nullable();
|
|
9923
|
-
function method(input, output, options) {
|
|
9924
|
-
return {
|
|
9925
|
-
input,
|
|
9926
|
-
output,
|
|
9927
|
-
kind: options?.kind ?? "query",
|
|
9928
|
-
...options?.auth !== void 0 ? { auth: options.auth } : {},
|
|
9929
|
-
...options?.access !== void 0 ? { access: options.access } : {},
|
|
9930
|
-
...options?.role !== void 0 ? { role: options.role } : {},
|
|
9931
|
-
...options?.caller !== void 0 ? { caller: options.caller } : {},
|
|
9932
|
-
timeoutMs: options?.timeoutMs
|
|
9933
|
-
};
|
|
9934
|
-
}
|
|
9935
|
-
/**
|
|
9936
|
-
* A wrapper/system-only method: served exclusively by the cap's system-level
|
|
9937
|
-
* provider (`InferProvider`), and OPTIONAL on `InferNativeProvider` so per-device
|
|
9938
|
-
* driver natives don't stub out a wrapper concern (e.g. a cross-device cache
|
|
9939
|
-
* overview). The `systemOnly: true` literal is what `InferNativeProvider` keys on.
|
|
9940
|
-
*/
|
|
9941
|
-
function systemMethod(input, output, options) {
|
|
9942
|
-
return {
|
|
9943
|
-
...method(input, output, options),
|
|
9944
|
-
systemOnly: true
|
|
9945
|
-
};
|
|
9946
|
-
}
|
|
9947
|
-
/**
|
|
9948
|
-
* A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
|
|
9949
|
-
* on `InferProvider`. The `providerOptional: true` literal is what
|
|
9950
|
-
* `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
|
|
9951
|
-
* for when this is the honest answer and when it is a soft stub.
|
|
9952
|
-
*/
|
|
9953
|
-
function optionalMethod(input, output, options) {
|
|
9954
|
-
return {
|
|
9955
|
-
...method(input, output, options),
|
|
9956
|
-
providerOptional: true
|
|
9957
|
-
};
|
|
9958
|
-
}
|
|
9959
|
-
/** Shorthand to define an event schema */
|
|
9960
|
-
function event(data) {
|
|
9961
|
-
return { data };
|
|
9962
|
-
}
|
|
9963
10130
|
var READING_QUANTITIES = [
|
|
9964
10131
|
"temperature",
|
|
9965
10132
|
"pressure",
|
|
@@ -10538,7 +10705,6 @@ var CAP_NAMES = Object.freeze([
|
|
|
10538
10705
|
"climate-control",
|
|
10539
10706
|
"color",
|
|
10540
10707
|
"composer",
|
|
10541
|
-
"connection-test",
|
|
10542
10708
|
"connectivity",
|
|
10543
10709
|
"consumables",
|
|
10544
10710
|
"contact",
|
|
@@ -10628,7 +10794,6 @@ var CAP_NAMES = Object.freeze([
|
|
|
10628
10794
|
"notifier",
|
|
10629
10795
|
"numeric-sensor",
|
|
10630
10796
|
"oauth-integration",
|
|
10631
|
-
"oidc-connections",
|
|
10632
10797
|
"osd",
|
|
10633
10798
|
"osd-manager",
|
|
10634
10799
|
"pet-feeder",
|
|
@@ -12335,13 +12500,25 @@ Object.freeze({
|
|
|
12335
12500
|
"path": "temperature",
|
|
12336
12501
|
"valueType": "number"
|
|
12337
12502
|
}],
|
|
12338
|
-
"timed-run": [
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12503
|
+
"timed-run": [
|
|
12504
|
+
{
|
|
12505
|
+
"path": "activeUntil",
|
|
12506
|
+
"valueType": "number"
|
|
12507
|
+
},
|
|
12508
|
+
{
|
|
12509
|
+
"path": "requestedBy",
|
|
12510
|
+
"valueType": "string"
|
|
12511
|
+
},
|
|
12512
|
+
{
|
|
12513
|
+
"path": "quickRun.duration",
|
|
12514
|
+
"valueType": "number"
|
|
12515
|
+
},
|
|
12516
|
+
{
|
|
12517
|
+
"path": "quickRun.unit",
|
|
12518
|
+
"valueType": "enum",
|
|
12519
|
+
"enumValues": ["s", "min"]
|
|
12520
|
+
}
|
|
12521
|
+
],
|
|
12345
12522
|
"update": [
|
|
12346
12523
|
{
|
|
12347
12524
|
"path": "currentVersion",
|
|
@@ -12520,8 +12697,7 @@ Object.freeze({
|
|
|
12520
12697
|
"device-provider": { "reloadDevice": 18e4 },
|
|
12521
12698
|
"llm": {
|
|
12522
12699
|
"generate": 6e5,
|
|
12523
|
-
"generateVision": 6e5
|
|
12524
|
-
"testProfile": 6e5
|
|
12700
|
+
"generateVision": 6e5
|
|
12525
12701
|
},
|
|
12526
12702
|
"llm-runtime": {
|
|
12527
12703
|
"complete": 6e5,
|
|
@@ -12558,8 +12734,7 @@ Object.freeze({
|
|
|
12558
12734
|
"offerArtifact": 3e5,
|
|
12559
12735
|
"readResult": 3e5,
|
|
12560
12736
|
"submitJob": 18e5,
|
|
12561
|
-
"uploadDataset": 72e5
|
|
12562
|
-
"validateCredentials": 3e4
|
|
12737
|
+
"uploadDataset": 72e5
|
|
12563
12738
|
}
|
|
12564
12739
|
});
|
|
12565
12740
|
object({
|
|
@@ -12567,7 +12742,7 @@ object({
|
|
|
12567
12742
|
params: record(string(), union([string(), number()])).optional()
|
|
12568
12743
|
});
|
|
12569
12744
|
//#endregion
|
|
12570
|
-
//#region ../types/dist/composition-
|
|
12745
|
+
//#region ../types/dist/composition-Bx0JvKUU.mjs
|
|
12571
12746
|
/**
|
|
12572
12747
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
12573
12748
|
*
|
|
@@ -13729,6 +13904,391 @@ var NodeColorIdSchema = _enum([
|
|
|
13729
13904
|
"node-h"
|
|
13730
13905
|
]);
|
|
13731
13906
|
//#endregion
|
|
13907
|
+
//#region ../types/dist/convert-BAzH15HU.mjs
|
|
13908
|
+
/**
|
|
13909
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
13910
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
13911
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
13912
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
13913
|
+
*/
|
|
13914
|
+
var UNIT_TABLE = {
|
|
13915
|
+
"°C": {
|
|
13916
|
+
dimension: "temperature",
|
|
13917
|
+
factor: 1,
|
|
13918
|
+
offset: 0
|
|
13919
|
+
},
|
|
13920
|
+
"°F": {
|
|
13921
|
+
dimension: "temperature",
|
|
13922
|
+
factor: 5 / 9,
|
|
13923
|
+
offset: -160 / 9
|
|
13924
|
+
},
|
|
13925
|
+
K: {
|
|
13926
|
+
dimension: "temperature",
|
|
13927
|
+
factor: 1,
|
|
13928
|
+
offset: -273.15
|
|
13929
|
+
},
|
|
13930
|
+
hPa: {
|
|
13931
|
+
dimension: "pressure",
|
|
13932
|
+
factor: 1,
|
|
13933
|
+
offset: 0
|
|
13934
|
+
},
|
|
13935
|
+
kPa: {
|
|
13936
|
+
dimension: "pressure",
|
|
13937
|
+
factor: 10,
|
|
13938
|
+
offset: 0
|
|
13939
|
+
},
|
|
13940
|
+
Pa: {
|
|
13941
|
+
dimension: "pressure",
|
|
13942
|
+
factor: .01,
|
|
13943
|
+
offset: 0
|
|
13944
|
+
},
|
|
13945
|
+
mbar: {
|
|
13946
|
+
dimension: "pressure",
|
|
13947
|
+
factor: 1,
|
|
13948
|
+
offset: 0
|
|
13949
|
+
},
|
|
13950
|
+
bar: {
|
|
13951
|
+
dimension: "pressure",
|
|
13952
|
+
factor: 1e3,
|
|
13953
|
+
offset: 0
|
|
13954
|
+
},
|
|
13955
|
+
inHg: {
|
|
13956
|
+
dimension: "pressure",
|
|
13957
|
+
factor: 33.8639,
|
|
13958
|
+
offset: 0
|
|
13959
|
+
},
|
|
13960
|
+
mmHg: {
|
|
13961
|
+
dimension: "pressure",
|
|
13962
|
+
factor: 1.33322,
|
|
13963
|
+
offset: 0
|
|
13964
|
+
},
|
|
13965
|
+
psi: {
|
|
13966
|
+
dimension: "pressure",
|
|
13967
|
+
factor: 68.9476,
|
|
13968
|
+
offset: 0
|
|
13969
|
+
},
|
|
13970
|
+
"m/s": {
|
|
13971
|
+
dimension: "speed",
|
|
13972
|
+
factor: 1,
|
|
13973
|
+
offset: 0
|
|
13974
|
+
},
|
|
13975
|
+
"km/h": {
|
|
13976
|
+
dimension: "speed",
|
|
13977
|
+
factor: 1 / 3.6,
|
|
13978
|
+
offset: 0
|
|
13979
|
+
},
|
|
13980
|
+
mph: {
|
|
13981
|
+
dimension: "speed",
|
|
13982
|
+
factor: .44704,
|
|
13983
|
+
offset: 0
|
|
13984
|
+
},
|
|
13985
|
+
kn: {
|
|
13986
|
+
dimension: "speed",
|
|
13987
|
+
factor: .514444,
|
|
13988
|
+
offset: 0
|
|
13989
|
+
},
|
|
13990
|
+
"mm/h": {
|
|
13991
|
+
dimension: "precipitation-rate",
|
|
13992
|
+
factor: 1,
|
|
13993
|
+
offset: 0
|
|
13994
|
+
},
|
|
13995
|
+
"in/h": {
|
|
13996
|
+
dimension: "precipitation-rate",
|
|
13997
|
+
factor: 25.4,
|
|
13998
|
+
offset: 0
|
|
13999
|
+
},
|
|
14000
|
+
m: {
|
|
14001
|
+
dimension: "length",
|
|
14002
|
+
factor: 1,
|
|
14003
|
+
offset: 0
|
|
14004
|
+
},
|
|
14005
|
+
mm: {
|
|
14006
|
+
dimension: "length",
|
|
14007
|
+
factor: .001,
|
|
14008
|
+
offset: 0
|
|
14009
|
+
},
|
|
14010
|
+
cm: {
|
|
14011
|
+
dimension: "length",
|
|
14012
|
+
factor: .01,
|
|
14013
|
+
offset: 0
|
|
14014
|
+
},
|
|
14015
|
+
km: {
|
|
14016
|
+
dimension: "length",
|
|
14017
|
+
factor: 1e3,
|
|
14018
|
+
offset: 0
|
|
14019
|
+
},
|
|
14020
|
+
in: {
|
|
14021
|
+
dimension: "length",
|
|
14022
|
+
factor: .0254,
|
|
14023
|
+
offset: 0
|
|
14024
|
+
},
|
|
14025
|
+
ft: {
|
|
14026
|
+
dimension: "length",
|
|
14027
|
+
factor: .3048,
|
|
14028
|
+
offset: 0
|
|
14029
|
+
},
|
|
14030
|
+
mi: {
|
|
14031
|
+
dimension: "length",
|
|
14032
|
+
factor: 1609.344,
|
|
14033
|
+
offset: 0
|
|
14034
|
+
},
|
|
14035
|
+
"m²": {
|
|
14036
|
+
dimension: "area",
|
|
14037
|
+
factor: 1,
|
|
14038
|
+
offset: 0
|
|
14039
|
+
},
|
|
14040
|
+
lx: {
|
|
14041
|
+
dimension: "illuminance",
|
|
14042
|
+
factor: 1,
|
|
14043
|
+
offset: 0
|
|
14044
|
+
},
|
|
14045
|
+
"W/m²": {
|
|
14046
|
+
dimension: "irradiance",
|
|
14047
|
+
factor: 1,
|
|
14048
|
+
offset: 0
|
|
14049
|
+
},
|
|
14050
|
+
W: {
|
|
14051
|
+
dimension: "power",
|
|
14052
|
+
factor: 1,
|
|
14053
|
+
offset: 0
|
|
14054
|
+
},
|
|
14055
|
+
kW: {
|
|
14056
|
+
dimension: "power",
|
|
14057
|
+
factor: 1e3,
|
|
14058
|
+
offset: 0
|
|
14059
|
+
},
|
|
14060
|
+
VA: {
|
|
14061
|
+
dimension: "apparent-power",
|
|
14062
|
+
factor: 1,
|
|
14063
|
+
offset: 0
|
|
14064
|
+
},
|
|
14065
|
+
Wh: {
|
|
14066
|
+
dimension: "energy",
|
|
14067
|
+
factor: 1,
|
|
14068
|
+
offset: 0
|
|
14069
|
+
},
|
|
14070
|
+
kWh: {
|
|
14071
|
+
dimension: "energy",
|
|
14072
|
+
factor: 1e3,
|
|
14073
|
+
offset: 0
|
|
14074
|
+
},
|
|
14075
|
+
MWh: {
|
|
14076
|
+
dimension: "energy",
|
|
14077
|
+
factor: 1e6,
|
|
14078
|
+
offset: 0
|
|
14079
|
+
},
|
|
14080
|
+
V: {
|
|
14081
|
+
dimension: "voltage",
|
|
14082
|
+
factor: 1,
|
|
14083
|
+
offset: 0
|
|
14084
|
+
},
|
|
14085
|
+
mV: {
|
|
14086
|
+
dimension: "voltage",
|
|
14087
|
+
factor: .001,
|
|
14088
|
+
offset: 0
|
|
14089
|
+
},
|
|
14090
|
+
A: {
|
|
14091
|
+
dimension: "current",
|
|
14092
|
+
factor: 1,
|
|
14093
|
+
offset: 0
|
|
14094
|
+
},
|
|
14095
|
+
mA: {
|
|
14096
|
+
dimension: "current",
|
|
14097
|
+
factor: .001,
|
|
14098
|
+
offset: 0
|
|
14099
|
+
},
|
|
14100
|
+
ppm: {
|
|
14101
|
+
dimension: "concentration-volume",
|
|
14102
|
+
factor: 1,
|
|
14103
|
+
offset: 0
|
|
14104
|
+
},
|
|
14105
|
+
ppb: {
|
|
14106
|
+
dimension: "concentration-volume",
|
|
14107
|
+
factor: .001,
|
|
14108
|
+
offset: 0
|
|
14109
|
+
},
|
|
14110
|
+
"µg/m³": {
|
|
14111
|
+
dimension: "concentration-mass",
|
|
14112
|
+
factor: 1,
|
|
14113
|
+
offset: 0
|
|
14114
|
+
},
|
|
14115
|
+
"mg/m³": {
|
|
14116
|
+
dimension: "concentration-mass",
|
|
14117
|
+
factor: 1e3,
|
|
14118
|
+
offset: 0
|
|
14119
|
+
},
|
|
14120
|
+
kg: {
|
|
14121
|
+
dimension: "mass",
|
|
14122
|
+
factor: 1,
|
|
14123
|
+
offset: 0
|
|
14124
|
+
},
|
|
14125
|
+
g: {
|
|
14126
|
+
dimension: "mass",
|
|
14127
|
+
factor: .001,
|
|
14128
|
+
offset: 0
|
|
14129
|
+
},
|
|
14130
|
+
lb: {
|
|
14131
|
+
dimension: "mass",
|
|
14132
|
+
factor: .453592,
|
|
14133
|
+
offset: 0
|
|
14134
|
+
},
|
|
14135
|
+
oz: {
|
|
14136
|
+
dimension: "mass",
|
|
14137
|
+
factor: .0283495,
|
|
14138
|
+
offset: 0
|
|
14139
|
+
},
|
|
14140
|
+
"%": {
|
|
14141
|
+
dimension: "percentage",
|
|
14142
|
+
factor: 1,
|
|
14143
|
+
offset: 0
|
|
14144
|
+
},
|
|
14145
|
+
L: {
|
|
14146
|
+
dimension: "volume",
|
|
14147
|
+
factor: 1,
|
|
14148
|
+
offset: 0
|
|
14149
|
+
},
|
|
14150
|
+
mL: {
|
|
14151
|
+
dimension: "volume",
|
|
14152
|
+
factor: .001,
|
|
14153
|
+
offset: 0
|
|
14154
|
+
},
|
|
14155
|
+
gal: {
|
|
14156
|
+
dimension: "volume",
|
|
14157
|
+
factor: 3.785411784,
|
|
14158
|
+
offset: 0
|
|
14159
|
+
},
|
|
14160
|
+
s: {
|
|
14161
|
+
dimension: "duration",
|
|
14162
|
+
factor: 1,
|
|
14163
|
+
offset: 0
|
|
14164
|
+
},
|
|
14165
|
+
ms: {
|
|
14166
|
+
dimension: "duration",
|
|
14167
|
+
factor: .001,
|
|
14168
|
+
offset: 0
|
|
14169
|
+
},
|
|
14170
|
+
min: {
|
|
14171
|
+
dimension: "duration",
|
|
14172
|
+
factor: 60,
|
|
14173
|
+
offset: 0
|
|
14174
|
+
},
|
|
14175
|
+
h: {
|
|
14176
|
+
dimension: "duration",
|
|
14177
|
+
factor: 3600,
|
|
14178
|
+
offset: 0
|
|
14179
|
+
},
|
|
14180
|
+
d: {
|
|
14181
|
+
dimension: "duration",
|
|
14182
|
+
factor: 86400,
|
|
14183
|
+
offset: 0
|
|
14184
|
+
},
|
|
14185
|
+
B: {
|
|
14186
|
+
dimension: "information",
|
|
14187
|
+
factor: 1,
|
|
14188
|
+
offset: 0
|
|
14189
|
+
},
|
|
14190
|
+
KiB: {
|
|
14191
|
+
dimension: "information",
|
|
14192
|
+
factor: 1024,
|
|
14193
|
+
offset: 0
|
|
14194
|
+
},
|
|
14195
|
+
MiB: {
|
|
14196
|
+
dimension: "information",
|
|
14197
|
+
factor: 1024 ** 2,
|
|
14198
|
+
offset: 0
|
|
14199
|
+
},
|
|
14200
|
+
GiB: {
|
|
14201
|
+
dimension: "information",
|
|
14202
|
+
factor: 1024 ** 3,
|
|
14203
|
+
offset: 0
|
|
14204
|
+
},
|
|
14205
|
+
TiB: {
|
|
14206
|
+
dimension: "information",
|
|
14207
|
+
factor: 1024 ** 4,
|
|
14208
|
+
offset: 0
|
|
14209
|
+
},
|
|
14210
|
+
kB: {
|
|
14211
|
+
dimension: "information",
|
|
14212
|
+
factor: 1e3,
|
|
14213
|
+
offset: 0
|
|
14214
|
+
},
|
|
14215
|
+
MB: {
|
|
14216
|
+
dimension: "information",
|
|
14217
|
+
factor: 1e6,
|
|
14218
|
+
offset: 0
|
|
14219
|
+
},
|
|
14220
|
+
GB: {
|
|
14221
|
+
dimension: "information",
|
|
14222
|
+
factor: 1e9,
|
|
14223
|
+
offset: 0
|
|
14224
|
+
},
|
|
14225
|
+
"°": {
|
|
14226
|
+
dimension: "angle",
|
|
14227
|
+
factor: 1,
|
|
14228
|
+
offset: 0
|
|
14229
|
+
},
|
|
14230
|
+
dBFS: {
|
|
14231
|
+
dimension: "sound-level",
|
|
14232
|
+
factor: 1,
|
|
14233
|
+
offset: 0
|
|
14234
|
+
},
|
|
14235
|
+
dBm: {
|
|
14236
|
+
dimension: "signal-level",
|
|
14237
|
+
factor: 1,
|
|
14238
|
+
offset: 0
|
|
14239
|
+
},
|
|
14240
|
+
mired: {
|
|
14241
|
+
dimension: "colour-temperature",
|
|
14242
|
+
factor: 1,
|
|
14243
|
+
offset: 0
|
|
14244
|
+
},
|
|
14245
|
+
kbps: {
|
|
14246
|
+
dimension: "bitrate",
|
|
14247
|
+
factor: 1,
|
|
14248
|
+
offset: 0
|
|
14249
|
+
},
|
|
14250
|
+
Mbps: {
|
|
14251
|
+
dimension: "bitrate",
|
|
14252
|
+
factor: 1e3,
|
|
14253
|
+
offset: 0
|
|
14254
|
+
},
|
|
14255
|
+
fps: {
|
|
14256
|
+
dimension: "rate",
|
|
14257
|
+
factor: 1,
|
|
14258
|
+
offset: 0
|
|
14259
|
+
}
|
|
14260
|
+
};
|
|
14261
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
14262
|
+
function specFor(unit) {
|
|
14263
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
14264
|
+
}
|
|
14265
|
+
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
14266
|
+
function unitDimension(unit) {
|
|
14267
|
+
return specFor(unit)?.dimension;
|
|
14268
|
+
}
|
|
14269
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
14270
|
+
function toCanonical(value, spec) {
|
|
14271
|
+
return value * spec.factor + spec.offset;
|
|
14272
|
+
}
|
|
14273
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
14274
|
+
function fromCanonical(canonical, spec) {
|
|
14275
|
+
return (canonical - spec.offset) / spec.factor;
|
|
14276
|
+
}
|
|
14277
|
+
/**
|
|
14278
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
14279
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
14280
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
14281
|
+
*/
|
|
14282
|
+
function tryConvertUnit(value, from, to) {
|
|
14283
|
+
if (from === to) return value;
|
|
14284
|
+
const fromSpec = specFor(from);
|
|
14285
|
+
if (!fromSpec) return void 0;
|
|
14286
|
+
const toSpec = specFor(to);
|
|
14287
|
+
if (!toSpec) return void 0;
|
|
14288
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
14289
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
14290
|
+
}
|
|
14291
|
+
//#endregion
|
|
13732
14292
|
//#region ../types/dist/index.mjs
|
|
13733
14293
|
var SMTP_INBOUND_DEFAULT_PORT = 2525;
|
|
13734
14294
|
var SMTP_INBOUND_DEFAULT_DOMAIN = "camstack.local";
|
|
@@ -13914,6 +14474,414 @@ var IntegrationInstanceRefSchema = object({
|
|
|
13914
14474
|
]),
|
|
13915
14475
|
candidateStableId: string().optional()
|
|
13916
14476
|
});
|
|
14477
|
+
/**
|
|
14478
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
14479
|
+
*
|
|
14480
|
+
* Apprise-derived model (see
|
|
14481
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
14482
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
14483
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
14484
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
14485
|
+
* message to what the kind supports — callers never special-case a service.
|
|
14486
|
+
*
|
|
14487
|
+
* DESIGN DECISIONS (locked):
|
|
14488
|
+
* - A target is an INSTANCE of this function cap (epic #440, step 6): the
|
|
14489
|
+
* providers serve their targets as `integration-instances` records, one
|
|
14490
|
+
* record kind per target kind, and the admin lists, adds, edits, switches
|
|
14491
|
+
* and deletes them through that cap. This cap keeps what an instance cannot
|
|
14492
|
+
* say: the kind catalogue (`listTargetKinds`: capabilities, delivery
|
|
14493
|
+
* parameters, brand icon), dispatch (`send`, `testTarget`) and the form
|
|
14494
|
+
* helpers (`discoverTargets`, `probeTargetConfig`).
|
|
14495
|
+
* - `listTargetKinds` / `discoverTargets` return arrays → the generated
|
|
14496
|
+
* cap-mount auto-`concatCollection`-fans them across every registered
|
|
14497
|
+
* provider so one catalog is routable. `send` / `testTarget` /
|
|
14498
|
+
* `probeTargetConfig` route to ONE provider by the `addonId` the generated
|
|
14499
|
+
* collection router extracts from the call input.
|
|
14500
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
14501
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
14502
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
14503
|
+
* base64 fallback needed.
|
|
14504
|
+
*
|
|
14505
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
14506
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
14507
|
+
* admin "Integrations" page.
|
|
14508
|
+
*/
|
|
14509
|
+
/**
|
|
14510
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
14511
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
14512
|
+
*/
|
|
14513
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
14514
|
+
"image",
|
|
14515
|
+
"video",
|
|
14516
|
+
"gif",
|
|
14517
|
+
"audio",
|
|
14518
|
+
"icon"
|
|
14519
|
+
]);
|
|
14520
|
+
/**
|
|
14521
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
14522
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
14523
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
14524
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
14525
|
+
*/
|
|
14526
|
+
var AttachmentSchema = object({
|
|
14527
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
14528
|
+
url: string().optional(),
|
|
14529
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
14530
|
+
mime: string().optional(),
|
|
14531
|
+
name: string().optional()
|
|
14532
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
14533
|
+
var NotificationFormatSchema = _enum([
|
|
14534
|
+
"text",
|
|
14535
|
+
"markdown",
|
|
14536
|
+
"html"
|
|
14537
|
+
]);
|
|
14538
|
+
/**
|
|
14539
|
+
* The CLOSED icon vocabulary an action button may use.
|
|
14540
|
+
*
|
|
14541
|
+
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
14542
|
+
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
14543
|
+
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
14544
|
+
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
14545
|
+
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
14546
|
+
*
|
|
14547
|
+
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
14548
|
+
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
14549
|
+
* differently.
|
|
14550
|
+
*
|
|
14551
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
14552
|
+
*
|
|
14553
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
14554
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
14555
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
14556
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
14557
|
+
* decides its glyph, which is the only place that decision can be made
|
|
14558
|
+
* honestly.
|
|
14559
|
+
*
|
|
14560
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
14561
|
+
* `disarm` straight through; iOS feeds that string to
|
|
14562
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
14563
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
14564
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
14565
|
+
*
|
|
14566
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
14567
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
14568
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
14569
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
14570
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
14571
|
+
*/
|
|
14572
|
+
var NotificationActionIconSchema = _enum([
|
|
14573
|
+
"acknowledge",
|
|
14574
|
+
"dismiss",
|
|
14575
|
+
"silence",
|
|
14576
|
+
"snooze",
|
|
14577
|
+
"view",
|
|
14578
|
+
"play",
|
|
14579
|
+
"open",
|
|
14580
|
+
"close",
|
|
14581
|
+
"lock",
|
|
14582
|
+
"unlock",
|
|
14583
|
+
"arm",
|
|
14584
|
+
"arm-home",
|
|
14585
|
+
"arm-away",
|
|
14586
|
+
"arm-night",
|
|
14587
|
+
"disarm",
|
|
14588
|
+
"light",
|
|
14589
|
+
"alert",
|
|
14590
|
+
"camera"
|
|
14591
|
+
]);
|
|
14592
|
+
/** A single tap-through action button. */
|
|
14593
|
+
var NotificationActionSchema = object({
|
|
14594
|
+
id: string(),
|
|
14595
|
+
label: string(),
|
|
14596
|
+
url: string().optional(),
|
|
14597
|
+
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
14598
|
+
icon: NotificationActionIconSchema.optional(),
|
|
14599
|
+
/**
|
|
14600
|
+
* Renders in a warning style where the notifier supports it.
|
|
14601
|
+
*
|
|
14602
|
+
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
14603
|
+
* else — see `notification-center/action-token.ts` for what that does and
|
|
14604
|
+
* does not buy.
|
|
14605
|
+
*/
|
|
14606
|
+
destructive: boolean().optional(),
|
|
14607
|
+
/**
|
|
14608
|
+
* How the tap should REACH the url.
|
|
14609
|
+
*
|
|
14610
|
+
* `navigate` (absent, and every button authored before this field) opens it:
|
|
14611
|
+
* the phone leaves the notification and shows whatever the callback returns.
|
|
14612
|
+
* That is right for a button whose answer the operator wants to read.
|
|
14613
|
+
*
|
|
14614
|
+
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
14615
|
+
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
14616
|
+
* an answer to the notification, and being thrown into a browser tab to
|
|
14617
|
+
* confirm it costs more attention than the notification did. A backend that
|
|
14618
|
+
* cannot do a background call renders it as an ordinary link (the adapters
|
|
14619
|
+
* fall back rather than dropping the button), so this is a preference, never
|
|
14620
|
+
* a requirement.
|
|
14621
|
+
*/
|
|
14622
|
+
mode: _enum(["navigate", "background"]).optional()
|
|
14623
|
+
});
|
|
14624
|
+
/** A delivery parameter value: what a notifier declared it takes. */
|
|
14625
|
+
var DeliveryParamValueSchema = union([
|
|
14626
|
+
string(),
|
|
14627
|
+
number(),
|
|
14628
|
+
boolean()
|
|
14629
|
+
]);
|
|
14630
|
+
/**
|
|
14631
|
+
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
14632
|
+
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
14633
|
+
* never handed one. The admin UI derives its control from this descriptor —
|
|
14634
|
+
* no editor knows a service by name.
|
|
14635
|
+
*
|
|
14636
|
+
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
14637
|
+
* run on the addon cap path).
|
|
14638
|
+
*/
|
|
14639
|
+
var DeliveryParamSchema = object({
|
|
14640
|
+
key: string().min(1),
|
|
14641
|
+
label: string(),
|
|
14642
|
+
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
14643
|
+
labelKey: string().optional(),
|
|
14644
|
+
type: _enum([
|
|
14645
|
+
"enum",
|
|
14646
|
+
"number",
|
|
14647
|
+
"boolean",
|
|
14648
|
+
"string"
|
|
14649
|
+
]),
|
|
14650
|
+
/** `enum` only: the closed set of accepted values. */
|
|
14651
|
+
options: array(object({
|
|
14652
|
+
value: string(),
|
|
14653
|
+
label: string()
|
|
14654
|
+
})).optional(),
|
|
14655
|
+
/** `number` only. */
|
|
14656
|
+
min: number().optional(),
|
|
14657
|
+
max: number().optional(),
|
|
14658
|
+
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
14659
|
+
default: DeliveryParamValueSchema.optional()
|
|
14660
|
+
});
|
|
14661
|
+
/**
|
|
14662
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
14663
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
14664
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
14665
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
14666
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
14667
|
+
* `priority` for that one target.
|
|
14668
|
+
*/
|
|
14669
|
+
var NotificationSchema = object({
|
|
14670
|
+
body: string(),
|
|
14671
|
+
title: string().optional(),
|
|
14672
|
+
format: NotificationFormatSchema.default("text"),
|
|
14673
|
+
priority: number().int().min(1).max(5).default(3),
|
|
14674
|
+
level: string().optional(),
|
|
14675
|
+
attachments: array(AttachmentSchema).optional(),
|
|
14676
|
+
clickUrl: string().optional(),
|
|
14677
|
+
actions: array(NotificationActionSchema).optional(),
|
|
14678
|
+
sound: string().optional(),
|
|
14679
|
+
ttl: number().optional(),
|
|
14680
|
+
tag: string().optional(),
|
|
14681
|
+
deviceId: number().optional(),
|
|
14682
|
+
eventId: string().optional(),
|
|
14683
|
+
metadata: record(string(), unknown()).optional(),
|
|
14684
|
+
/**
|
|
14685
|
+
* Values for the parameters the target's kind DECLARES in
|
|
14686
|
+
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
14687
|
+
* declaration by the sender; a notifier reads only the keys it declared.
|
|
14688
|
+
*/
|
|
14689
|
+
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
14690
|
+
});
|
|
14691
|
+
/** One declared native severity/priority level for a kind. */
|
|
14692
|
+
var TargetKindLevelSchema = object({
|
|
14693
|
+
id: string(),
|
|
14694
|
+
label: string(),
|
|
14695
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
14696
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
14697
|
+
flags: object({
|
|
14698
|
+
critical: boolean().optional(),
|
|
14699
|
+
silent: boolean().optional(),
|
|
14700
|
+
noPush: boolean().optional()
|
|
14701
|
+
}).optional(),
|
|
14702
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
14703
|
+
requires: array(string()).optional(),
|
|
14704
|
+
description: string().optional()
|
|
14705
|
+
});
|
|
14706
|
+
/** The full capability block consulted before dispatch. */
|
|
14707
|
+
var TargetKindCapsSchema = object({
|
|
14708
|
+
attachments: object({
|
|
14709
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
14710
|
+
mode: _enum([
|
|
14711
|
+
"url",
|
|
14712
|
+
"bytes",
|
|
14713
|
+
"both"
|
|
14714
|
+
]),
|
|
14715
|
+
max: number().int().nonnegative(),
|
|
14716
|
+
maxBytes: number().int().positive().optional()
|
|
14717
|
+
}),
|
|
14718
|
+
/** Max action buttons (0 = none). */
|
|
14719
|
+
actions: number().int().nonnegative(),
|
|
14720
|
+
/**
|
|
14721
|
+
* Whether this kind renders a per-action ICON.
|
|
14722
|
+
*
|
|
14723
|
+
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
14724
|
+
* run on the addon cap path — three production failures in one day taught
|
|
14725
|
+
* this repo that once. Absent is read as false by the degrade engine, which
|
|
14726
|
+
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
14727
|
+
* assumed and dropped costs the operator's trust in the field.
|
|
14728
|
+
*/
|
|
14729
|
+
actionIcons: boolean().optional(),
|
|
14730
|
+
levels: array(TargetKindLevelSchema),
|
|
14731
|
+
format: array(NotificationFormatSchema),
|
|
14732
|
+
clickUrl: boolean(),
|
|
14733
|
+
sound: boolean(),
|
|
14734
|
+
ttl: boolean(),
|
|
14735
|
+
bodyMaxLen: number().int().positive()
|
|
14736
|
+
});
|
|
14737
|
+
/**
|
|
14738
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
14739
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
14740
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
14741
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
14742
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14743
|
+
*/
|
|
14744
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
14745
|
+
var TargetKindSchema = object({
|
|
14746
|
+
kind: string(),
|
|
14747
|
+
label: string(),
|
|
14748
|
+
icon: string(),
|
|
14749
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
14750
|
+
addonId: string(),
|
|
14751
|
+
/**
|
|
14752
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
14753
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
14754
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
14755
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
14756
|
+
*
|
|
14757
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
14758
|
+
* client, and a native client joins it onto its own hub base.
|
|
14759
|
+
*
|
|
14760
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
14761
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
14762
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
14763
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
14764
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
14765
|
+
*/
|
|
14766
|
+
iconUrl: string().optional(),
|
|
14767
|
+
/**
|
|
14768
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
14769
|
+
*
|
|
14770
|
+
* The server knows this and therefore says it, because the client cannot
|
|
14771
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
14772
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
14773
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
14774
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
14775
|
+
*
|
|
14776
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
14777
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
14778
|
+
* its raster path, which is the safe default for an unknown image.
|
|
14779
|
+
*/
|
|
14780
|
+
iconMediaType: string().optional(),
|
|
14781
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
14782
|
+
/**
|
|
14783
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
14784
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
14785
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
14786
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
14787
|
+
* on every edit.
|
|
14788
|
+
*
|
|
14789
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
14790
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
14791
|
+
* it absent must assume ANY edit may need the secret.
|
|
14792
|
+
*/
|
|
14793
|
+
destinationKeys: array(string()).optional(),
|
|
14794
|
+
supportsDiscovery: boolean(),
|
|
14795
|
+
/**
|
|
14796
|
+
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
14797
|
+
* Absent = none; the rule editor then offers nothing.
|
|
14798
|
+
*/
|
|
14799
|
+
deliveryParams: array(DeliveryParamSchema).optional(),
|
|
14800
|
+
caps: TargetKindCapsSchema
|
|
14801
|
+
});
|
|
14802
|
+
/**
|
|
14803
|
+
* A target as a provider's own code sees it (the notification-center bridge, the
|
|
14804
|
+
* send path): the flat record body split into the name, the switch and the
|
|
14805
|
+
* kind's fields. `config` holds secrets; a reader that serves it to a client
|
|
14806
|
+
* REDACTS secret fields (a presence marker only) — never round-trip a stored
|
|
14807
|
+
* secret to the UI.
|
|
14808
|
+
*/
|
|
14809
|
+
var TargetSchema = object({
|
|
14810
|
+
id: string(),
|
|
14811
|
+
name: string(),
|
|
14812
|
+
kind: string(),
|
|
14813
|
+
addonId: string(),
|
|
14814
|
+
enabled: boolean(),
|
|
14815
|
+
config: record(string(), unknown())
|
|
14816
|
+
});
|
|
14817
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
14818
|
+
var DiscoveredTargetSchema = object({
|
|
14819
|
+
kind: string(),
|
|
14820
|
+
suggestedName: string(),
|
|
14821
|
+
config: record(string(), unknown())
|
|
14822
|
+
});
|
|
14823
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
14824
|
+
var RenderedAsSchema = object({
|
|
14825
|
+
level: string(),
|
|
14826
|
+
format: NotificationFormatSchema,
|
|
14827
|
+
attachmentsSent: number().int().nonnegative(),
|
|
14828
|
+
actionsSent: number().int().nonnegative(),
|
|
14829
|
+
truncated: boolean(),
|
|
14830
|
+
dropped: array(string())
|
|
14831
|
+
});
|
|
14832
|
+
var SendResultSchema = object({
|
|
14833
|
+
success: boolean(),
|
|
14834
|
+
error: string().optional(),
|
|
14835
|
+
renderedAs: RenderedAsSchema.optional()
|
|
14836
|
+
});
|
|
14837
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
14838
|
+
var TestResultSchema = SendResultSchema;
|
|
14839
|
+
/**
|
|
14840
|
+
* Pre-save credential probe against a partial (unsaved) target config.
|
|
14841
|
+
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
14842
|
+
*/
|
|
14843
|
+
var ProbeTargetConfigResultSchema = object({
|
|
14844
|
+
ok: boolean(),
|
|
14845
|
+
resolvedUrl: string(),
|
|
14846
|
+
httpStatus: number().int().optional(),
|
|
14847
|
+
stateInstance: string().optional(),
|
|
14848
|
+
error: string().optional()
|
|
14849
|
+
});
|
|
14850
|
+
method(object({}), array(TargetKindSchema)), method(object({
|
|
14851
|
+
kind: string(),
|
|
14852
|
+
config: record(string(), unknown()).optional(),
|
|
14853
|
+
/**
|
|
14854
|
+
* When editing / duplicating, the blank password field never round-trips
|
|
14855
|
+
* the stored token. Pass the source target id so the provider can restore
|
|
14856
|
+
* secrets server-side before dialling.
|
|
14857
|
+
*/
|
|
14858
|
+
targetId: string().optional()
|
|
14859
|
+
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
14860
|
+
kind: string(),
|
|
14861
|
+
config: record(string(), unknown()),
|
|
14862
|
+
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
14863
|
+
targetId: string().optional()
|
|
14864
|
+
}), ProbeTargetConfigResultSchema, {
|
|
14865
|
+
kind: "mutation",
|
|
14866
|
+
auth: "admin"
|
|
14867
|
+
}), method(object({
|
|
14868
|
+
targetId: string(),
|
|
14869
|
+
notification: NotificationSchema
|
|
14870
|
+
}), SendResultSchema, {
|
|
14871
|
+
kind: "mutation",
|
|
14872
|
+
auth: "admin"
|
|
14873
|
+
}), method(object({
|
|
14874
|
+
targetId: string(),
|
|
14875
|
+
sample: NotificationSchema.optional()
|
|
14876
|
+
}), TestResultSchema, {
|
|
14877
|
+
kind: "mutation",
|
|
14878
|
+
auth: "admin"
|
|
14879
|
+
});
|
|
14880
|
+
looseObject({
|
|
14881
|
+
name: string().min(1),
|
|
14882
|
+
enabled: boolean().optional()
|
|
14883
|
+
});
|
|
14884
|
+
TargetSchema.omit({ id: true });
|
|
13917
14885
|
var EncodeProfileSchema = object({
|
|
13918
14886
|
video: object({
|
|
13919
14887
|
codec: _enum([
|
|
@@ -22328,7 +23296,6 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
22328
23296
|
"calendar-source": system(INTERNAL),
|
|
22329
23297
|
calendars: system("calendars feed schedules and conditions, not device actions"),
|
|
22330
23298
|
composer: system("device composition — an authoring tool, not an action"),
|
|
22331
|
-
"connection-test": system(INTERNAL),
|
|
22332
23299
|
"core-blocks": system(INTERNAL),
|
|
22333
23300
|
"custom-model-registry": system(ML),
|
|
22334
23301
|
"data-store-provider": system(INTERNAL),
|
|
@@ -22373,7 +23340,6 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
22373
23340
|
"notification-output": system("notification delivery — reached through notifier devices and rules"),
|
|
22374
23341
|
"notification-rules": system("notification rules — authored on their own page"),
|
|
22375
23342
|
"oauth-integration": system(IDENTITY),
|
|
22376
|
-
"oidc-connections": system(IDENTITY),
|
|
22377
23343
|
"osd-manager": system(PIPELINE),
|
|
22378
23344
|
"pipeline-executor": system(PIPELINE),
|
|
22379
23345
|
"pipeline-orchestrator": system(PIPELINE),
|
|
@@ -42394,6 +43360,7 @@ var ExpressionBindingSourceSchema = union([
|
|
|
42394
43360
|
object({
|
|
42395
43361
|
kind: literal("global"),
|
|
42396
43362
|
sourceStableId: string(),
|
|
43363
|
+
addonId: string().optional(),
|
|
42397
43364
|
cap: string(),
|
|
42398
43365
|
fieldPath: string()
|
|
42399
43366
|
})
|
|
@@ -42595,389 +43562,6 @@ function extractSourceInfoFromMetadata(metadata) {
|
|
|
42595
43562
|
return parsed.data;
|
|
42596
43563
|
}
|
|
42597
43564
|
/**
|
|
42598
|
-
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
42599
|
-
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
42600
|
-
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
42601
|
-
* decimals). K derives from `°C = K − 273.15`.
|
|
42602
|
-
*/
|
|
42603
|
-
var UNIT_TABLE = {
|
|
42604
|
-
"°C": {
|
|
42605
|
-
dimension: "temperature",
|
|
42606
|
-
factor: 1,
|
|
42607
|
-
offset: 0
|
|
42608
|
-
},
|
|
42609
|
-
"°F": {
|
|
42610
|
-
dimension: "temperature",
|
|
42611
|
-
factor: 5 / 9,
|
|
42612
|
-
offset: -160 / 9
|
|
42613
|
-
},
|
|
42614
|
-
K: {
|
|
42615
|
-
dimension: "temperature",
|
|
42616
|
-
factor: 1,
|
|
42617
|
-
offset: -273.15
|
|
42618
|
-
},
|
|
42619
|
-
hPa: {
|
|
42620
|
-
dimension: "pressure",
|
|
42621
|
-
factor: 1,
|
|
42622
|
-
offset: 0
|
|
42623
|
-
},
|
|
42624
|
-
kPa: {
|
|
42625
|
-
dimension: "pressure",
|
|
42626
|
-
factor: 10,
|
|
42627
|
-
offset: 0
|
|
42628
|
-
},
|
|
42629
|
-
Pa: {
|
|
42630
|
-
dimension: "pressure",
|
|
42631
|
-
factor: .01,
|
|
42632
|
-
offset: 0
|
|
42633
|
-
},
|
|
42634
|
-
mbar: {
|
|
42635
|
-
dimension: "pressure",
|
|
42636
|
-
factor: 1,
|
|
42637
|
-
offset: 0
|
|
42638
|
-
},
|
|
42639
|
-
bar: {
|
|
42640
|
-
dimension: "pressure",
|
|
42641
|
-
factor: 1e3,
|
|
42642
|
-
offset: 0
|
|
42643
|
-
},
|
|
42644
|
-
inHg: {
|
|
42645
|
-
dimension: "pressure",
|
|
42646
|
-
factor: 33.8639,
|
|
42647
|
-
offset: 0
|
|
42648
|
-
},
|
|
42649
|
-
mmHg: {
|
|
42650
|
-
dimension: "pressure",
|
|
42651
|
-
factor: 1.33322,
|
|
42652
|
-
offset: 0
|
|
42653
|
-
},
|
|
42654
|
-
psi: {
|
|
42655
|
-
dimension: "pressure",
|
|
42656
|
-
factor: 68.9476,
|
|
42657
|
-
offset: 0
|
|
42658
|
-
},
|
|
42659
|
-
"m/s": {
|
|
42660
|
-
dimension: "speed",
|
|
42661
|
-
factor: 1,
|
|
42662
|
-
offset: 0
|
|
42663
|
-
},
|
|
42664
|
-
"km/h": {
|
|
42665
|
-
dimension: "speed",
|
|
42666
|
-
factor: 1 / 3.6,
|
|
42667
|
-
offset: 0
|
|
42668
|
-
},
|
|
42669
|
-
mph: {
|
|
42670
|
-
dimension: "speed",
|
|
42671
|
-
factor: .44704,
|
|
42672
|
-
offset: 0
|
|
42673
|
-
},
|
|
42674
|
-
kn: {
|
|
42675
|
-
dimension: "speed",
|
|
42676
|
-
factor: .514444,
|
|
42677
|
-
offset: 0
|
|
42678
|
-
},
|
|
42679
|
-
"mm/h": {
|
|
42680
|
-
dimension: "precipitation-rate",
|
|
42681
|
-
factor: 1,
|
|
42682
|
-
offset: 0
|
|
42683
|
-
},
|
|
42684
|
-
"in/h": {
|
|
42685
|
-
dimension: "precipitation-rate",
|
|
42686
|
-
factor: 25.4,
|
|
42687
|
-
offset: 0
|
|
42688
|
-
},
|
|
42689
|
-
m: {
|
|
42690
|
-
dimension: "length",
|
|
42691
|
-
factor: 1,
|
|
42692
|
-
offset: 0
|
|
42693
|
-
},
|
|
42694
|
-
mm: {
|
|
42695
|
-
dimension: "length",
|
|
42696
|
-
factor: .001,
|
|
42697
|
-
offset: 0
|
|
42698
|
-
},
|
|
42699
|
-
cm: {
|
|
42700
|
-
dimension: "length",
|
|
42701
|
-
factor: .01,
|
|
42702
|
-
offset: 0
|
|
42703
|
-
},
|
|
42704
|
-
km: {
|
|
42705
|
-
dimension: "length",
|
|
42706
|
-
factor: 1e3,
|
|
42707
|
-
offset: 0
|
|
42708
|
-
},
|
|
42709
|
-
in: {
|
|
42710
|
-
dimension: "length",
|
|
42711
|
-
factor: .0254,
|
|
42712
|
-
offset: 0
|
|
42713
|
-
},
|
|
42714
|
-
ft: {
|
|
42715
|
-
dimension: "length",
|
|
42716
|
-
factor: .3048,
|
|
42717
|
-
offset: 0
|
|
42718
|
-
},
|
|
42719
|
-
mi: {
|
|
42720
|
-
dimension: "length",
|
|
42721
|
-
factor: 1609.344,
|
|
42722
|
-
offset: 0
|
|
42723
|
-
},
|
|
42724
|
-
"m²": {
|
|
42725
|
-
dimension: "area",
|
|
42726
|
-
factor: 1,
|
|
42727
|
-
offset: 0
|
|
42728
|
-
},
|
|
42729
|
-
lx: {
|
|
42730
|
-
dimension: "illuminance",
|
|
42731
|
-
factor: 1,
|
|
42732
|
-
offset: 0
|
|
42733
|
-
},
|
|
42734
|
-
"W/m²": {
|
|
42735
|
-
dimension: "irradiance",
|
|
42736
|
-
factor: 1,
|
|
42737
|
-
offset: 0
|
|
42738
|
-
},
|
|
42739
|
-
W: {
|
|
42740
|
-
dimension: "power",
|
|
42741
|
-
factor: 1,
|
|
42742
|
-
offset: 0
|
|
42743
|
-
},
|
|
42744
|
-
kW: {
|
|
42745
|
-
dimension: "power",
|
|
42746
|
-
factor: 1e3,
|
|
42747
|
-
offset: 0
|
|
42748
|
-
},
|
|
42749
|
-
VA: {
|
|
42750
|
-
dimension: "apparent-power",
|
|
42751
|
-
factor: 1,
|
|
42752
|
-
offset: 0
|
|
42753
|
-
},
|
|
42754
|
-
Wh: {
|
|
42755
|
-
dimension: "energy",
|
|
42756
|
-
factor: 1,
|
|
42757
|
-
offset: 0
|
|
42758
|
-
},
|
|
42759
|
-
kWh: {
|
|
42760
|
-
dimension: "energy",
|
|
42761
|
-
factor: 1e3,
|
|
42762
|
-
offset: 0
|
|
42763
|
-
},
|
|
42764
|
-
MWh: {
|
|
42765
|
-
dimension: "energy",
|
|
42766
|
-
factor: 1e6,
|
|
42767
|
-
offset: 0
|
|
42768
|
-
},
|
|
42769
|
-
V: {
|
|
42770
|
-
dimension: "voltage",
|
|
42771
|
-
factor: 1,
|
|
42772
|
-
offset: 0
|
|
42773
|
-
},
|
|
42774
|
-
mV: {
|
|
42775
|
-
dimension: "voltage",
|
|
42776
|
-
factor: .001,
|
|
42777
|
-
offset: 0
|
|
42778
|
-
},
|
|
42779
|
-
A: {
|
|
42780
|
-
dimension: "current",
|
|
42781
|
-
factor: 1,
|
|
42782
|
-
offset: 0
|
|
42783
|
-
},
|
|
42784
|
-
mA: {
|
|
42785
|
-
dimension: "current",
|
|
42786
|
-
factor: .001,
|
|
42787
|
-
offset: 0
|
|
42788
|
-
},
|
|
42789
|
-
ppm: {
|
|
42790
|
-
dimension: "concentration-volume",
|
|
42791
|
-
factor: 1,
|
|
42792
|
-
offset: 0
|
|
42793
|
-
},
|
|
42794
|
-
ppb: {
|
|
42795
|
-
dimension: "concentration-volume",
|
|
42796
|
-
factor: .001,
|
|
42797
|
-
offset: 0
|
|
42798
|
-
},
|
|
42799
|
-
"µg/m³": {
|
|
42800
|
-
dimension: "concentration-mass",
|
|
42801
|
-
factor: 1,
|
|
42802
|
-
offset: 0
|
|
42803
|
-
},
|
|
42804
|
-
"mg/m³": {
|
|
42805
|
-
dimension: "concentration-mass",
|
|
42806
|
-
factor: 1e3,
|
|
42807
|
-
offset: 0
|
|
42808
|
-
},
|
|
42809
|
-
kg: {
|
|
42810
|
-
dimension: "mass",
|
|
42811
|
-
factor: 1,
|
|
42812
|
-
offset: 0
|
|
42813
|
-
},
|
|
42814
|
-
g: {
|
|
42815
|
-
dimension: "mass",
|
|
42816
|
-
factor: .001,
|
|
42817
|
-
offset: 0
|
|
42818
|
-
},
|
|
42819
|
-
lb: {
|
|
42820
|
-
dimension: "mass",
|
|
42821
|
-
factor: .453592,
|
|
42822
|
-
offset: 0
|
|
42823
|
-
},
|
|
42824
|
-
oz: {
|
|
42825
|
-
dimension: "mass",
|
|
42826
|
-
factor: .0283495,
|
|
42827
|
-
offset: 0
|
|
42828
|
-
},
|
|
42829
|
-
"%": {
|
|
42830
|
-
dimension: "percentage",
|
|
42831
|
-
factor: 1,
|
|
42832
|
-
offset: 0
|
|
42833
|
-
},
|
|
42834
|
-
L: {
|
|
42835
|
-
dimension: "volume",
|
|
42836
|
-
factor: 1,
|
|
42837
|
-
offset: 0
|
|
42838
|
-
},
|
|
42839
|
-
mL: {
|
|
42840
|
-
dimension: "volume",
|
|
42841
|
-
factor: .001,
|
|
42842
|
-
offset: 0
|
|
42843
|
-
},
|
|
42844
|
-
gal: {
|
|
42845
|
-
dimension: "volume",
|
|
42846
|
-
factor: 3.785411784,
|
|
42847
|
-
offset: 0
|
|
42848
|
-
},
|
|
42849
|
-
s: {
|
|
42850
|
-
dimension: "duration",
|
|
42851
|
-
factor: 1,
|
|
42852
|
-
offset: 0
|
|
42853
|
-
},
|
|
42854
|
-
ms: {
|
|
42855
|
-
dimension: "duration",
|
|
42856
|
-
factor: .001,
|
|
42857
|
-
offset: 0
|
|
42858
|
-
},
|
|
42859
|
-
min: {
|
|
42860
|
-
dimension: "duration",
|
|
42861
|
-
factor: 60,
|
|
42862
|
-
offset: 0
|
|
42863
|
-
},
|
|
42864
|
-
h: {
|
|
42865
|
-
dimension: "duration",
|
|
42866
|
-
factor: 3600,
|
|
42867
|
-
offset: 0
|
|
42868
|
-
},
|
|
42869
|
-
d: {
|
|
42870
|
-
dimension: "duration",
|
|
42871
|
-
factor: 86400,
|
|
42872
|
-
offset: 0
|
|
42873
|
-
},
|
|
42874
|
-
B: {
|
|
42875
|
-
dimension: "information",
|
|
42876
|
-
factor: 1,
|
|
42877
|
-
offset: 0
|
|
42878
|
-
},
|
|
42879
|
-
KiB: {
|
|
42880
|
-
dimension: "information",
|
|
42881
|
-
factor: 1024,
|
|
42882
|
-
offset: 0
|
|
42883
|
-
},
|
|
42884
|
-
MiB: {
|
|
42885
|
-
dimension: "information",
|
|
42886
|
-
factor: 1024 ** 2,
|
|
42887
|
-
offset: 0
|
|
42888
|
-
},
|
|
42889
|
-
GiB: {
|
|
42890
|
-
dimension: "information",
|
|
42891
|
-
factor: 1024 ** 3,
|
|
42892
|
-
offset: 0
|
|
42893
|
-
},
|
|
42894
|
-
TiB: {
|
|
42895
|
-
dimension: "information",
|
|
42896
|
-
factor: 1024 ** 4,
|
|
42897
|
-
offset: 0
|
|
42898
|
-
},
|
|
42899
|
-
kB: {
|
|
42900
|
-
dimension: "information",
|
|
42901
|
-
factor: 1e3,
|
|
42902
|
-
offset: 0
|
|
42903
|
-
},
|
|
42904
|
-
MB: {
|
|
42905
|
-
dimension: "information",
|
|
42906
|
-
factor: 1e6,
|
|
42907
|
-
offset: 0
|
|
42908
|
-
},
|
|
42909
|
-
GB: {
|
|
42910
|
-
dimension: "information",
|
|
42911
|
-
factor: 1e9,
|
|
42912
|
-
offset: 0
|
|
42913
|
-
},
|
|
42914
|
-
"°": {
|
|
42915
|
-
dimension: "angle",
|
|
42916
|
-
factor: 1,
|
|
42917
|
-
offset: 0
|
|
42918
|
-
},
|
|
42919
|
-
dBFS: {
|
|
42920
|
-
dimension: "sound-level",
|
|
42921
|
-
factor: 1,
|
|
42922
|
-
offset: 0
|
|
42923
|
-
},
|
|
42924
|
-
dBm: {
|
|
42925
|
-
dimension: "signal-level",
|
|
42926
|
-
factor: 1,
|
|
42927
|
-
offset: 0
|
|
42928
|
-
},
|
|
42929
|
-
mired: {
|
|
42930
|
-
dimension: "colour-temperature",
|
|
42931
|
-
factor: 1,
|
|
42932
|
-
offset: 0
|
|
42933
|
-
},
|
|
42934
|
-
kbps: {
|
|
42935
|
-
dimension: "bitrate",
|
|
42936
|
-
factor: 1,
|
|
42937
|
-
offset: 0
|
|
42938
|
-
},
|
|
42939
|
-
Mbps: {
|
|
42940
|
-
dimension: "bitrate",
|
|
42941
|
-
factor: 1e3,
|
|
42942
|
-
offset: 0
|
|
42943
|
-
},
|
|
42944
|
-
fps: {
|
|
42945
|
-
dimension: "rate",
|
|
42946
|
-
factor: 1,
|
|
42947
|
-
offset: 0
|
|
42948
|
-
}
|
|
42949
|
-
};
|
|
42950
|
-
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
42951
|
-
function specFor(unit) {
|
|
42952
|
-
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
42953
|
-
}
|
|
42954
|
-
/** The dimension a unit belongs to, or `undefined` for an unknown unit. */
|
|
42955
|
-
function unitDimension(unit) {
|
|
42956
|
-
return specFor(unit)?.dimension;
|
|
42957
|
-
}
|
|
42958
|
-
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
42959
|
-
function toCanonical(value, spec) {
|
|
42960
|
-
return value * spec.factor + spec.offset;
|
|
42961
|
-
}
|
|
42962
|
-
/** Canonical-unit value → the unit described by `spec`. */
|
|
42963
|
-
function fromCanonical(canonical, spec) {
|
|
42964
|
-
return (canonical - spec.offset) / spec.factor;
|
|
42965
|
-
}
|
|
42966
|
-
/**
|
|
42967
|
-
* Graceful variant for the render path: returns the converted value, or
|
|
42968
|
-
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
42969
|
-
* Never throws — a render caller falls back to the raw value + source unit.
|
|
42970
|
-
*/
|
|
42971
|
-
function tryConvertUnit(value, from, to) {
|
|
42972
|
-
if (from === to) return value;
|
|
42973
|
-
const fromSpec = specFor(from);
|
|
42974
|
-
if (!fromSpec) return void 0;
|
|
42975
|
-
const toSpec = specFor(to);
|
|
42976
|
-
if (!toSpec) return void 0;
|
|
42977
|
-
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
42978
|
-
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
42979
|
-
}
|
|
42980
|
-
/**
|
|
42981
43565
|
* A number a person types for a physical quantity — the input side of the
|
|
42982
43566
|
* value + unit contract (D972).
|
|
42983
43567
|
*
|
|
@@ -44384,427 +44968,6 @@ var UnitWarningSchema = object({
|
|
|
44384
44968
|
assumedUnit: string(),
|
|
44385
44969
|
message: string()
|
|
44386
44970
|
});
|
|
44387
|
-
/**
|
|
44388
|
-
* notification-output — canonical, capability-gated notification delivery.
|
|
44389
|
-
*
|
|
44390
|
-
* Apprise-derived model (see
|
|
44391
|
-
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
44392
|
-
* callers emit ONE canonical `Notification`; each provider declares a
|
|
44393
|
-
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
44394
|
-
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
44395
|
-
* message to what the kind supports — callers never special-case a service.
|
|
44396
|
-
*
|
|
44397
|
-
* DESIGN DECISIONS (locked):
|
|
44398
|
-
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
44399
|
-
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
44400
|
-
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
44401
|
-
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
44402
|
-
* alternative would fork the UI per addon and cannot host the
|
|
44403
|
-
* discovery→adopt flow.
|
|
44404
|
-
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
44405
|
-
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
44406
|
-
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
44407
|
-
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
44408
|
-
* `addonId` the generated collection router extracts from the call input.
|
|
44409
|
-
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
44410
|
-
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
44411
|
-
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
44412
|
-
* base64 fallback needed.
|
|
44413
|
-
*
|
|
44414
|
-
* TODO (deferred, closed-set change — separate decision): add
|
|
44415
|
-
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
44416
|
-
* admin "Integrations" page.
|
|
44417
|
-
*/
|
|
44418
|
-
/**
|
|
44419
|
-
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
44420
|
-
* adapter picks what it supports and the degrade engine filters the rest.
|
|
44421
|
-
*/
|
|
44422
|
-
var AttachmentMediaTypeSchema = _enum([
|
|
44423
|
-
"image",
|
|
44424
|
-
"video",
|
|
44425
|
-
"gif",
|
|
44426
|
-
"audio",
|
|
44427
|
-
"icon"
|
|
44428
|
-
]);
|
|
44429
|
-
/**
|
|
44430
|
-
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
44431
|
-
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
44432
|
-
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
44433
|
-
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
44434
|
-
*/
|
|
44435
|
-
var AttachmentSchema = object({
|
|
44436
|
-
mediaType: AttachmentMediaTypeSchema,
|
|
44437
|
-
url: string().optional(),
|
|
44438
|
-
bytes: _instanceof(Uint8Array).optional(),
|
|
44439
|
-
mime: string().optional(),
|
|
44440
|
-
name: string().optional()
|
|
44441
|
-
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
44442
|
-
var NotificationFormatSchema = _enum([
|
|
44443
|
-
"text",
|
|
44444
|
-
"markdown",
|
|
44445
|
-
"html"
|
|
44446
|
-
]);
|
|
44447
|
-
/**
|
|
44448
|
-
* The CLOSED icon vocabulary an action button may use.
|
|
44449
|
-
*
|
|
44450
|
-
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
44451
|
-
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
44452
|
-
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
44453
|
-
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
44454
|
-
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
44455
|
-
*
|
|
44456
|
-
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
44457
|
-
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
44458
|
-
* differently.
|
|
44459
|
-
*
|
|
44460
|
-
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
44461
|
-
*
|
|
44462
|
-
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
44463
|
-
* the whole set onto its own renderer's vocabulary through a
|
|
44464
|
-
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
44465
|
-
* fallback, so adding a member here fails every adapter's build until someone
|
|
44466
|
-
* decides its glyph, which is the only place that decision can be made
|
|
44467
|
-
* honestly.
|
|
44468
|
-
*
|
|
44469
|
-
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
44470
|
-
* `disarm` straight through; iOS feeds that string to
|
|
44471
|
-
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
44472
|
-
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
44473
|
-
* mapping, and "the field is documented" is not "the value renders".
|
|
44474
|
-
*
|
|
44475
|
-
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
44476
|
-
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
44477
|
-
* so an addon that emits a token the running hub does not know does not lose an
|
|
44478
|
-
* icon — its whole `send` fails Zod validation and the notification never
|
|
44479
|
-
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
44480
|
-
*/
|
|
44481
|
-
var NotificationActionIconSchema = _enum([
|
|
44482
|
-
"acknowledge",
|
|
44483
|
-
"dismiss",
|
|
44484
|
-
"silence",
|
|
44485
|
-
"snooze",
|
|
44486
|
-
"view",
|
|
44487
|
-
"play",
|
|
44488
|
-
"open",
|
|
44489
|
-
"close",
|
|
44490
|
-
"lock",
|
|
44491
|
-
"unlock",
|
|
44492
|
-
"arm",
|
|
44493
|
-
"arm-home",
|
|
44494
|
-
"arm-away",
|
|
44495
|
-
"arm-night",
|
|
44496
|
-
"disarm",
|
|
44497
|
-
"light",
|
|
44498
|
-
"alert",
|
|
44499
|
-
"camera"
|
|
44500
|
-
]);
|
|
44501
|
-
/** A single tap-through action button. */
|
|
44502
|
-
var NotificationActionSchema = object({
|
|
44503
|
-
id: string(),
|
|
44504
|
-
label: string(),
|
|
44505
|
-
url: string().optional(),
|
|
44506
|
-
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
44507
|
-
icon: NotificationActionIconSchema.optional(),
|
|
44508
|
-
/**
|
|
44509
|
-
* Renders in a warning style where the notifier supports it.
|
|
44510
|
-
*
|
|
44511
|
-
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
44512
|
-
* else — see `notification-center/action-token.ts` for what that does and
|
|
44513
|
-
* does not buy.
|
|
44514
|
-
*/
|
|
44515
|
-
destructive: boolean().optional(),
|
|
44516
|
-
/**
|
|
44517
|
-
* How the tap should REACH the url.
|
|
44518
|
-
*
|
|
44519
|
-
* `navigate` (absent, and every button authored before this field) opens it:
|
|
44520
|
-
* the phone leaves the notification and shows whatever the callback returns.
|
|
44521
|
-
* That is right for a button whose answer the operator wants to read.
|
|
44522
|
-
*
|
|
44523
|
-
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
44524
|
-
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
44525
|
-
* an answer to the notification, and being thrown into a browser tab to
|
|
44526
|
-
* confirm it costs more attention than the notification did. A backend that
|
|
44527
|
-
* cannot do a background call renders it as an ordinary link (the adapters
|
|
44528
|
-
* fall back rather than dropping the button), so this is a preference, never
|
|
44529
|
-
* a requirement.
|
|
44530
|
-
*/
|
|
44531
|
-
mode: _enum(["navigate", "background"]).optional()
|
|
44532
|
-
});
|
|
44533
|
-
/** A delivery parameter value: what a notifier declared it takes. */
|
|
44534
|
-
var DeliveryParamValueSchema = union([
|
|
44535
|
-
string(),
|
|
44536
|
-
number(),
|
|
44537
|
-
boolean()
|
|
44538
|
-
]);
|
|
44539
|
-
/**
|
|
44540
|
-
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
44541
|
-
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
44542
|
-
* never handed one. The admin UI derives its control from this descriptor —
|
|
44543
|
-
* no editor knows a service by name.
|
|
44544
|
-
*
|
|
44545
|
-
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
44546
|
-
* run on the addon cap path).
|
|
44547
|
-
*/
|
|
44548
|
-
var DeliveryParamSchema = object({
|
|
44549
|
-
key: string().min(1),
|
|
44550
|
-
label: string(),
|
|
44551
|
-
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
44552
|
-
labelKey: string().optional(),
|
|
44553
|
-
type: _enum([
|
|
44554
|
-
"enum",
|
|
44555
|
-
"number",
|
|
44556
|
-
"boolean",
|
|
44557
|
-
"string"
|
|
44558
|
-
]),
|
|
44559
|
-
/** `enum` only: the closed set of accepted values. */
|
|
44560
|
-
options: array(object({
|
|
44561
|
-
value: string(),
|
|
44562
|
-
label: string()
|
|
44563
|
-
})).optional(),
|
|
44564
|
-
/** `number` only. */
|
|
44565
|
-
min: number().optional(),
|
|
44566
|
-
max: number().optional(),
|
|
44567
|
-
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
44568
|
-
default: DeliveryParamValueSchema.optional()
|
|
44569
|
-
});
|
|
44570
|
-
/**
|
|
44571
|
-
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
44572
|
-
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
44573
|
-
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
44574
|
-
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
44575
|
-
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
44576
|
-
* `priority` for that one target.
|
|
44577
|
-
*/
|
|
44578
|
-
var NotificationSchema = object({
|
|
44579
|
-
body: string(),
|
|
44580
|
-
title: string().optional(),
|
|
44581
|
-
format: NotificationFormatSchema.default("text"),
|
|
44582
|
-
priority: number().int().min(1).max(5).default(3),
|
|
44583
|
-
level: string().optional(),
|
|
44584
|
-
attachments: array(AttachmentSchema).optional(),
|
|
44585
|
-
clickUrl: string().optional(),
|
|
44586
|
-
actions: array(NotificationActionSchema).optional(),
|
|
44587
|
-
sound: string().optional(),
|
|
44588
|
-
ttl: number().optional(),
|
|
44589
|
-
tag: string().optional(),
|
|
44590
|
-
deviceId: number().optional(),
|
|
44591
|
-
eventId: string().optional(),
|
|
44592
|
-
metadata: record(string(), unknown()).optional(),
|
|
44593
|
-
/**
|
|
44594
|
-
* Values for the parameters the target's kind DECLARES in
|
|
44595
|
-
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
44596
|
-
* declaration by the sender; a notifier reads only the keys it declared.
|
|
44597
|
-
*/
|
|
44598
|
-
delivery: record(string(), DeliveryParamValueSchema).optional()
|
|
44599
|
-
});
|
|
44600
|
-
/** One declared native severity/priority level for a kind. */
|
|
44601
|
-
var TargetKindLevelSchema = object({
|
|
44602
|
-
id: string(),
|
|
44603
|
-
label: string(),
|
|
44604
|
-
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
44605
|
-
ordinal: number().int().min(1).max(5).nullable(),
|
|
44606
|
-
flags: object({
|
|
44607
|
-
critical: boolean().optional(),
|
|
44608
|
-
silent: boolean().optional(),
|
|
44609
|
-
noPush: boolean().optional()
|
|
44610
|
-
}).optional(),
|
|
44611
|
-
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
44612
|
-
requires: array(string()).optional(),
|
|
44613
|
-
description: string().optional()
|
|
44614
|
-
});
|
|
44615
|
-
/** The full capability block consulted before dispatch. */
|
|
44616
|
-
var TargetKindCapsSchema = object({
|
|
44617
|
-
attachments: object({
|
|
44618
|
-
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
44619
|
-
mode: _enum([
|
|
44620
|
-
"url",
|
|
44621
|
-
"bytes",
|
|
44622
|
-
"both"
|
|
44623
|
-
]),
|
|
44624
|
-
max: number().int().nonnegative(),
|
|
44625
|
-
maxBytes: number().int().positive().optional()
|
|
44626
|
-
}),
|
|
44627
|
-
/** Max action buttons (0 = none). */
|
|
44628
|
-
actions: number().int().nonnegative(),
|
|
44629
|
-
/**
|
|
44630
|
-
* Whether this kind renders a per-action ICON.
|
|
44631
|
-
*
|
|
44632
|
-
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
44633
|
-
* run on the addon cap path — three production failures in one day taught
|
|
44634
|
-
* this repo that once. Absent is read as false by the degrade engine, which
|
|
44635
|
-
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
44636
|
-
* assumed and dropped costs the operator's trust in the field.
|
|
44637
|
-
*/
|
|
44638
|
-
actionIcons: boolean().optional(),
|
|
44639
|
-
levels: array(TargetKindLevelSchema),
|
|
44640
|
-
format: array(NotificationFormatSchema),
|
|
44641
|
-
clickUrl: boolean(),
|
|
44642
|
-
sound: boolean(),
|
|
44643
|
-
ttl: boolean(),
|
|
44644
|
-
bodyMaxLen: number().int().positive()
|
|
44645
|
-
});
|
|
44646
|
-
/**
|
|
44647
|
-
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
44648
|
-
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
44649
|
-
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
44650
|
-
* the union is large and not meant for runtime validation here; the exported
|
|
44651
|
-
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
44652
|
-
*/
|
|
44653
|
-
var ConfigSchemaPassthrough$4 = unknown();
|
|
44654
|
-
var TargetKindSchema = object({
|
|
44655
|
-
kind: string(),
|
|
44656
|
-
label: string(),
|
|
44657
|
-
icon: string(),
|
|
44658
|
-
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
44659
|
-
addonId: string(),
|
|
44660
|
-
/**
|
|
44661
|
-
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
44662
|
-
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
44663
|
-
* when the addon bundles no icon for that kind — the client then falls back
|
|
44664
|
-
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
44665
|
-
*
|
|
44666
|
-
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
44667
|
-
* client, and a native client joins it onto its own hub base.
|
|
44668
|
-
*
|
|
44669
|
-
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
44670
|
-
* field that survived only because the runtime cap-router forwards provider
|
|
44671
|
-
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
44672
|
-
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
44673
|
-
* broken silently the moment output validation was tightened anywhere.
|
|
44674
|
-
*/
|
|
44675
|
-
iconUrl: string().optional(),
|
|
44676
|
-
/**
|
|
44677
|
-
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
44678
|
-
*
|
|
44679
|
-
* The server knows this and therefore says it, because the client cannot
|
|
44680
|
-
* safely guess: a React-Native client renders SVG and raster through two
|
|
44681
|
-
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
44682
|
-
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
44683
|
-
* placeholder glyph for every vector icon while the web build looked fine.
|
|
44684
|
-
*
|
|
44685
|
-
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
44686
|
-
* not been updated — a client that cannot determine the type should prefer
|
|
44687
|
-
* its raster path, which is the safe default for an unknown image.
|
|
44688
|
-
*/
|
|
44689
|
-
iconMediaType: string().optional(),
|
|
44690
|
-
configSchema: ConfigSchemaPassthrough$4,
|
|
44691
|
-
/**
|
|
44692
|
-
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
44693
|
-
* base URL, instance, chat, recipient). A save that changes any of them
|
|
44694
|
-
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
44695
|
-
* editor uses this to ask for the secret on exactly those edits instead of
|
|
44696
|
-
* on every edit.
|
|
44697
|
-
*
|
|
44698
|
-
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
44699
|
-
* kinds) and from a provider older than this field — an editor that finds
|
|
44700
|
-
* it absent must assume ANY edit may need the secret.
|
|
44701
|
-
*/
|
|
44702
|
-
destinationKeys: array(string()).optional(),
|
|
44703
|
-
supportsDiscovery: boolean(),
|
|
44704
|
-
/**
|
|
44705
|
-
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
44706
|
-
* Absent = none; the rule editor then offers nothing.
|
|
44707
|
-
*/
|
|
44708
|
-
deliveryParams: array(DeliveryParamSchema).optional(),
|
|
44709
|
-
caps: TargetKindCapsSchema
|
|
44710
|
-
});
|
|
44711
|
-
/**
|
|
44712
|
-
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
44713
|
-
* (return a presence marker only) when serving `listTargets` — never
|
|
44714
|
-
* round-trip a stored secret to the UI.
|
|
44715
|
-
*/
|
|
44716
|
-
var TargetSchema = object({
|
|
44717
|
-
id: string(),
|
|
44718
|
-
name: string(),
|
|
44719
|
-
kind: string(),
|
|
44720
|
-
addonId: string(),
|
|
44721
|
-
enabled: boolean(),
|
|
44722
|
-
config: record(string(), unknown())
|
|
44723
|
-
});
|
|
44724
|
-
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
44725
|
-
var DiscoveredTargetSchema = object({
|
|
44726
|
-
kind: string(),
|
|
44727
|
-
suggestedName: string(),
|
|
44728
|
-
config: record(string(), unknown())
|
|
44729
|
-
});
|
|
44730
|
-
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
44731
|
-
var RenderedAsSchema = object({
|
|
44732
|
-
level: string(),
|
|
44733
|
-
format: NotificationFormatSchema,
|
|
44734
|
-
attachmentsSent: number().int().nonnegative(),
|
|
44735
|
-
actionsSent: number().int().nonnegative(),
|
|
44736
|
-
truncated: boolean(),
|
|
44737
|
-
dropped: array(string())
|
|
44738
|
-
});
|
|
44739
|
-
var SendResultSchema = object({
|
|
44740
|
-
success: boolean(),
|
|
44741
|
-
error: string().optional(),
|
|
44742
|
-
renderedAs: RenderedAsSchema.optional()
|
|
44743
|
-
});
|
|
44744
|
-
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
44745
|
-
var TestResultSchema = SendResultSchema;
|
|
44746
|
-
/**
|
|
44747
|
-
* Pre-save credential probe against a partial (unsaved) target config.
|
|
44748
|
-
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
44749
|
-
*/
|
|
44750
|
-
var ProbeTargetConfigResultSchema = object({
|
|
44751
|
-
ok: boolean(),
|
|
44752
|
-
resolvedUrl: string(),
|
|
44753
|
-
httpStatus: number().int().optional(),
|
|
44754
|
-
stateInstance: string().optional(),
|
|
44755
|
-
error: string().optional()
|
|
44756
|
-
});
|
|
44757
|
-
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({ targetId: string() }), TargetSchema, { auth: "admin" }), method(object({
|
|
44758
|
-
kind: string(),
|
|
44759
|
-
config: record(string(), unknown()).optional(),
|
|
44760
|
-
/**
|
|
44761
|
-
* When editing / duplicating, the blank password field never round-trips
|
|
44762
|
-
* the stored token. Pass the source target id so the provider can restore
|
|
44763
|
-
* secrets server-side before dialling (same merge as upsert).
|
|
44764
|
-
*/
|
|
44765
|
-
targetId: string().optional()
|
|
44766
|
-
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
44767
|
-
kind: string(),
|
|
44768
|
-
config: record(string(), unknown()),
|
|
44769
|
-
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
44770
|
-
targetId: string().optional()
|
|
44771
|
-
}), ProbeTargetConfigResultSchema, {
|
|
44772
|
-
kind: "mutation",
|
|
44773
|
-
auth: "admin"
|
|
44774
|
-
}), method(object({
|
|
44775
|
-
targetId: string(),
|
|
44776
|
-
notification: NotificationSchema
|
|
44777
|
-
}), SendResultSchema, {
|
|
44778
|
-
kind: "mutation",
|
|
44779
|
-
auth: "admin"
|
|
44780
|
-
}), method(object({
|
|
44781
|
-
targetId: string(),
|
|
44782
|
-
sample: NotificationSchema.optional()
|
|
44783
|
-
}), TestResultSchema, {
|
|
44784
|
-
kind: "mutation",
|
|
44785
|
-
auth: "admin"
|
|
44786
|
-
}), method(object({
|
|
44787
|
-
target: TargetSchema,
|
|
44788
|
-
/**
|
|
44789
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
44790
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
44791
|
-
* password fields are restored from this source (D634 still applies if
|
|
44792
|
-
* destinations diverge without a re-typed secret).
|
|
44793
|
-
*/
|
|
44794
|
-
cloneSecretsFrom: string().optional()
|
|
44795
|
-
}), TargetSchema, {
|
|
44796
|
-
kind: "mutation",
|
|
44797
|
-
auth: "admin"
|
|
44798
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
44799
|
-
kind: "mutation",
|
|
44800
|
-
auth: "admin"
|
|
44801
|
-
}), method(object({
|
|
44802
|
-
targetId: string(),
|
|
44803
|
-
enabled: boolean()
|
|
44804
|
-
}), _void(), {
|
|
44805
|
-
kind: "mutation",
|
|
44806
|
-
auth: "admin"
|
|
44807
|
-
});
|
|
44808
44971
|
_enum([
|
|
44809
44972
|
"automation",
|
|
44810
44973
|
"action",
|
|
@@ -45075,15 +45238,28 @@ var SettingsSchemaWithValuesSchema = object({
|
|
|
45075
45238
|
});
|
|
45076
45239
|
/** Patch object — keys are field names, values are the new field values. */
|
|
45077
45240
|
var SettingsPatchSchema = record(string(), unknown());
|
|
45241
|
+
/** The scope of a scoped-settings call (D986): where the capability declared the values. */
|
|
45242
|
+
var ScopedSettingsScopeSchema = _enum(SCOPED_SETTINGS_SCOPES);
|
|
45243
|
+
/** One scoped schema an addon serves: its capability, scope and section ids. */
|
|
45244
|
+
var ScopedSettingsDeclarationSchema = object({
|
|
45245
|
+
cap: string(),
|
|
45246
|
+
scope: ScopedSettingsScopeSchema,
|
|
45247
|
+
sectionIds: array(string())
|
|
45248
|
+
});
|
|
45078
45249
|
/** Standard success response for update operations. */
|
|
45079
45250
|
var SettingsUpdateResultSchema = object({ success: literal(true) });
|
|
45080
45251
|
method(object({
|
|
45081
45252
|
addonId: string(),
|
|
45082
|
-
|
|
45083
|
-
|
|
45084
|
-
|
|
45253
|
+
cap: string(),
|
|
45254
|
+
scope: ScopedSettingsScopeSchema,
|
|
45255
|
+
nodeId: string().optional()
|
|
45085
45256
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
45086
45257
|
addonId: string(),
|
|
45258
|
+
nodeId: string().optional()
|
|
45259
|
+
}), array(ScopedSettingsDeclarationSchema).nullable()), method(object({
|
|
45260
|
+
addonId: string(),
|
|
45261
|
+
cap: string(),
|
|
45262
|
+
scope: ScopedSettingsScopeSchema,
|
|
45087
45263
|
nodeId: string().optional(),
|
|
45088
45264
|
patch: SettingsPatchSchema
|
|
45089
45265
|
}), SettingsUpdateResultSchema, {
|
|
@@ -45091,8 +45267,10 @@ method(object({
|
|
|
45091
45267
|
auth: "admin"
|
|
45092
45268
|
}), method(object({
|
|
45093
45269
|
addonId: string(),
|
|
45270
|
+
cap: string(),
|
|
45271
|
+
scope: ScopedSettingsScopeSchema,
|
|
45094
45272
|
nodeId: string().optional()
|
|
45095
|
-
}),
|
|
45273
|
+
}), record(string(), unknown()).nullable()), method(object({
|
|
45096
45274
|
addonId: string(),
|
|
45097
45275
|
nodeId: string().optional(),
|
|
45098
45276
|
instance: IntegrationInstanceRefSchema
|
|
@@ -46655,7 +46833,7 @@ var BackupDestinationInfoSchema = object({
|
|
|
46655
46833
|
id: string(),
|
|
46656
46834
|
/**
|
|
46657
46835
|
* Manifest id of the owning addon. The Settings button on the
|
|
46658
|
-
* destination card opens that addon's
|
|
46836
|
+
* destination card opens that addon's scoped settings panel.
|
|
46659
46837
|
*/
|
|
46660
46838
|
addonId: string(),
|
|
46661
46839
|
/** Operator-toggled enable flag from the destination-policy table. */
|
|
@@ -49344,37 +49522,6 @@ method(object({
|
|
|
49344
49522
|
kind: "mutation",
|
|
49345
49523
|
auth: "admin"
|
|
49346
49524
|
}), method(object({ blockIds: array(string()) }), object({ states: array(CompositionBlockStateSchema) }), { auth: "admin" });
|
|
49347
|
-
var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
|
|
49348
|
-
object({
|
|
49349
|
-
outcome: literal("validated"),
|
|
49350
|
-
/** Round-trip of the sign-in, when the provider measured it. */
|
|
49351
|
-
latencyMs: number().nonnegative().optional(),
|
|
49352
|
-
/** Optional human detail worth showing next to the tick
|
|
49353
|
-
* ("3 devices visible on this account"). */
|
|
49354
|
-
detail: string().optional()
|
|
49355
|
-
}).strict(),
|
|
49356
|
-
object({
|
|
49357
|
-
outcome: literal("rejected"),
|
|
49358
|
-
error: string()
|
|
49359
|
-
}).strict(),
|
|
49360
|
-
object({
|
|
49361
|
-
outcome: literal("inconclusive"),
|
|
49362
|
-
error: string()
|
|
49363
|
-
}).strict()
|
|
49364
|
-
]);
|
|
49365
|
-
var ConnectionTestInputSchema = object({
|
|
49366
|
-
/** Candidate integration settings, exactly as the create form collected them. */
|
|
49367
|
-
settings: record(string(), unknown()) });
|
|
49368
|
-
/**
|
|
49369
|
-
* What the provider's test actually DOES, so the UI can say it in words before
|
|
49370
|
-
* the operator presses the button ("Signs in to the Dreo cloud"). Purely
|
|
49371
|
-
* descriptive — it never changes routing.
|
|
49372
|
-
*/
|
|
49373
|
-
var ConnectionTestDescriptorSchema = object({ label: string() });
|
|
49374
|
-
method(ConnectionTestInputSchema, ConnectionTestOutcomeSchema, {
|
|
49375
|
-
kind: "mutation",
|
|
49376
|
-
auth: "admin"
|
|
49377
|
-
}), method(_void(), ConnectionTestDescriptorSchema, { auth: "admin" });
|
|
49378
49525
|
/**
|
|
49379
49526
|
* Upstream-system connectivity sensor — distinct from `device-status`,
|
|
49380
49527
|
* which is the kernel-managed online/offline flag for the device's
|
|
@@ -50100,7 +50247,7 @@ var mediaPlayerCapability = {
|
|
|
50100
50247
|
isMuted: { kind: "boolean" }
|
|
50101
50248
|
} }
|
|
50102
50249
|
};
|
|
50103
|
-
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;
|
|
50250
|
+
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;
|
|
50104
50251
|
var NUMBER = {
|
|
50105
50252
|
kind: "fixed",
|
|
50106
50253
|
result: ["number"]
|
|
@@ -52139,6 +52286,7 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
|
|
|
52139
52286
|
kind: "mutation",
|
|
52140
52287
|
auth: "admin"
|
|
52141
52288
|
});
|
|
52289
|
+
var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
52142
52290
|
/**
|
|
52143
52291
|
* A SITE ZONE — a named part of the property that several locations belong to.
|
|
52144
52292
|
*
|
|
@@ -52625,6 +52773,12 @@ var DeviceInfoSchema = object({
|
|
|
52625
52773
|
addonId: string(),
|
|
52626
52774
|
type: _enum(DeviceType),
|
|
52627
52775
|
name: string(),
|
|
52776
|
+
/** The name a value template uses for this device (`{{ stufa_veranda.temperature }}`),
|
|
52777
|
+
* entity_id style. Generated once from the name at first registration, unique
|
|
52778
|
+
* cluster-wide, editable through `setAlias`, NOT changed by a rename (D1021).
|
|
52779
|
+
* Optional: a record produced by an older build omits it until the boot
|
|
52780
|
+
* backfill has run. */
|
|
52781
|
+
alias: DeviceAliasSchema.optional(),
|
|
52628
52782
|
/** Operator-organisational location label. `null` when unset. */
|
|
52629
52783
|
location: string().nullable(),
|
|
52630
52784
|
/** Soft-disabled flag. */
|
|
@@ -52944,6 +53098,25 @@ method(object({
|
|
|
52944
53098
|
}), _void(), {
|
|
52945
53099
|
kind: "mutation",
|
|
52946
53100
|
auth: "admin"
|
|
53101
|
+
}), method(object({
|
|
53102
|
+
deviceId: number(),
|
|
53103
|
+
alias: string()
|
|
53104
|
+
}), discriminatedUnion("ok", [object({
|
|
53105
|
+
ok: literal(true),
|
|
53106
|
+
alias: DeviceAliasSchema
|
|
53107
|
+
}), object({
|
|
53108
|
+
ok: literal(false),
|
|
53109
|
+
reason: _enum([
|
|
53110
|
+
"empty",
|
|
53111
|
+
"too-long",
|
|
53112
|
+
"grammar",
|
|
53113
|
+
"reserved",
|
|
53114
|
+
"taken"
|
|
53115
|
+
]),
|
|
53116
|
+
takenBy: number().optional()
|
|
53117
|
+
})]), {
|
|
53118
|
+
kind: "mutation",
|
|
53119
|
+
auth: "admin"
|
|
52947
53120
|
}), method(object({
|
|
52948
53121
|
deviceId: number(),
|
|
52949
53122
|
location: string().nullable()
|
|
@@ -56549,10 +56722,9 @@ var AvailableIntegrationTypeSchema = object({
|
|
|
56549
56722
|
* locations" checkbox. Provider-declared in the addon manifest. */
|
|
56550
56723
|
supportsLocationImport: boolean(),
|
|
56551
56724
|
/**
|
|
56552
|
-
* True when this integration
|
|
56553
|
-
*
|
|
56554
|
-
*
|
|
56555
|
-
* rather than offering a button that always answers the same nonsense.
|
|
56725
|
+
* True when this integration's instances declare a Test (`integration-instances.describe().canTest`,
|
|
56726
|
+
* D1016). An integration that cannot be tested must say so up front rather than offering a
|
|
56727
|
+
* button that always answers the same nonsense.
|
|
56556
56728
|
*/
|
|
56557
56729
|
canTest: boolean(),
|
|
56558
56730
|
existingInstances: array(object({
|
|
@@ -56606,7 +56778,9 @@ var CreateIntegrationInputSchema = object({
|
|
|
56606
56778
|
info: record(string(), unknown()).optional(),
|
|
56607
56779
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
56608
56780
|
originStableId: string().optional(),
|
|
56609
|
-
settings: record(string(), unknown()).optional()
|
|
56781
|
+
settings: record(string(), unknown()).optional(),
|
|
56782
|
+
/** The instance kind being created, for a provider with several; omitted = its only testable kind. */
|
|
56783
|
+
kind: string().min(1).optional()
|
|
56610
56784
|
});
|
|
56611
56785
|
var UpdateIntegrationInputSchema = object({
|
|
56612
56786
|
id: string(),
|
|
@@ -56632,13 +56806,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
56632
56806
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
56633
56807
|
kind: "mutation",
|
|
56634
56808
|
auth: "admin"
|
|
56635
|
-
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" })
|
|
56636
|
-
addonId: string(),
|
|
56637
|
-
settings: record(string(), unknown())
|
|
56638
|
-
}), TestConnectionResultSchema, {
|
|
56639
|
-
kind: "mutation",
|
|
56640
|
-
auth: "admin"
|
|
56641
|
-
});
|
|
56809
|
+
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" });
|
|
56642
56810
|
/**
|
|
56643
56811
|
* `integration-instances` - the connection of an addon that holds ONE or SEVERAL
|
|
56644
56812
|
* (an account, a relay, a destination) as typed records (D985, D986). The addon's
|
|
@@ -56658,7 +56826,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
56658
56826
|
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
56659
56827
|
* clears it.
|
|
56660
56828
|
*/
|
|
56661
|
-
var ConfigSchemaPassthrough$
|
|
56829
|
+
var ConfigSchemaPassthrough$2 = custom();
|
|
56662
56830
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
56663
56831
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
56664
56832
|
/**
|
|
@@ -56704,7 +56872,16 @@ var IntegrationInstanceKindSchema = object({
|
|
|
56704
56872
|
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
56705
56873
|
connectPath: string().min(1).optional(),
|
|
56706
56874
|
/** Relative hub path to disconnect that account. */
|
|
56707
|
-
disconnectPath: string().min(1).optional()
|
|
56875
|
+
disconnectPath: string().min(1).optional(),
|
|
56876
|
+
/**
|
|
56877
|
+
* Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
|
|
56878
|
+
* GENERIC widget id the client maps to a component; a client that knows none of them shows none.
|
|
56879
|
+
* `connection` widgets write connection values, `model` widgets own the model choice.
|
|
56880
|
+
*/
|
|
56881
|
+
widgets: array(object({
|
|
56882
|
+
id: string().min(1),
|
|
56883
|
+
placement: _enum(["connection", "model"])
|
|
56884
|
+
})).optional()
|
|
56708
56885
|
});
|
|
56709
56886
|
/**
|
|
56710
56887
|
* How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
|
|
@@ -56745,10 +56922,11 @@ var IntegrationInstanceDescriptionSchema = object({
|
|
|
56745
56922
|
/** True when `testInstance` really probes the remote. */
|
|
56746
56923
|
canTest: boolean(),
|
|
56747
56924
|
/**
|
|
56748
|
-
* The kinds an instance can be of
|
|
56749
|
-
* Add flow asks which first and sends it as
|
|
56925
|
+
* The kinds an instance can be of (D1013 rule 1), each naming its `functionCap`. One entry:
|
|
56926
|
+
* nothing to choose; more than one: the Add flow asks which first and sends it as
|
|
56927
|
+
* `createInstance.kind`. Never empty: every instance belongs to exactly one kind.
|
|
56750
56928
|
*/
|
|
56751
|
-
kinds: array(IntegrationInstanceKindSchema).
|
|
56929
|
+
kinds: array(IntegrationInstanceKindSchema).min(1),
|
|
56752
56930
|
/**
|
|
56753
56931
|
* The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
|
|
56754
56932
|
* devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
|
|
@@ -56784,8 +56962,8 @@ var IntegrationInstanceSchema = object({
|
|
|
56784
56962
|
* the body is the one store, `setInstanceEnabled` the one writer (epic #440, step 3).
|
|
56785
56963
|
*/
|
|
56786
56964
|
enabled: boolean().optional(),
|
|
56787
|
-
/** The kind of this instance
|
|
56788
|
-
kind: string().
|
|
56965
|
+
/** The kind of this instance: exactly one of `describe().kinds[].id` (D1013 rule 2). */
|
|
56966
|
+
kind: string().min(1),
|
|
56789
56967
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
56790
56968
|
formValues: record(string(), unknown()),
|
|
56791
56969
|
status: IntegrationInstanceStatusSchema.optional(),
|
|
@@ -56809,7 +56987,7 @@ var integrationInstancesCapability = {
|
|
|
56809
56987
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
56810
56988
|
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
56811
56989
|
kind: string().min(1).optional()
|
|
56812
|
-
}), ConfigSchemaPassthrough$
|
|
56990
|
+
}), ConfigSchemaPassthrough$2, { auth: "admin" }),
|
|
56813
56991
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
56814
56992
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
56815
56993
|
/**
|
|
@@ -57911,12 +58089,12 @@ var LlmModelInfoSchema = object({
|
|
|
57911
58089
|
});
|
|
57912
58090
|
/**
|
|
57913
58091
|
* The provider-specific part of a profile, validated by the provider that owns
|
|
57914
|
-
* the kind (
|
|
58092
|
+
* the kind (saving an instance refuses what it does not accept). A field its
|
|
57915
58093
|
* `connectionSchema` declares `type: 'password'` is a secret: it is held in the
|
|
57916
58094
|
* keychain, never in the row, and reads back as the redaction marker.
|
|
57917
58095
|
*/
|
|
57918
58096
|
var LlmProviderConfigSchema = record(string(), unknown());
|
|
57919
|
-
|
|
58097
|
+
object({
|
|
57920
58098
|
id: string(),
|
|
57921
58099
|
name: string(),
|
|
57922
58100
|
/** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
|
|
@@ -58010,8 +58188,8 @@ var LlmProviderWidgetSchema = object({
|
|
|
58010
58188
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
58011
58189
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
58012
58190
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
58013
|
-
var ConfigSchemaPassthrough$
|
|
58014
|
-
|
|
58191
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
58192
|
+
object({
|
|
58015
58193
|
kind: LlmProfileKindSchema,
|
|
58016
58194
|
label: string(),
|
|
58017
58195
|
/** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
|
|
@@ -58019,9 +58197,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
58019
58197
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
58020
58198
|
addonId: string(),
|
|
58021
58199
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
58022
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
58200
|
+
connectionSchema: ConfigSchemaPassthrough$1,
|
|
58023
58201
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
58024
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
58202
|
+
advancedSchema: ConfigSchemaPassthrough$1.optional(),
|
|
58025
58203
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
58026
58204
|
features: LlmModelFeaturesSchema,
|
|
58027
58205
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -58150,20 +58328,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
58150
58328
|
}), method(object({
|
|
58151
58329
|
addonId: string().optional(),
|
|
58152
58330
|
requestId: string()
|
|
58153
|
-
}), _void(), { kind: "mutation" }), method(
|
|
58154
|
-
kind: "mutation",
|
|
58155
|
-
auth: "admin"
|
|
58156
|
-
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
58157
|
-
kind: "mutation",
|
|
58158
|
-
auth: "admin"
|
|
58159
|
-
}), method(ProfileRefInputSchema, _void(), {
|
|
58160
|
-
kind: "mutation",
|
|
58161
|
-
auth: "admin"
|
|
58162
|
-
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
58163
|
-
kind: "mutation",
|
|
58164
|
-
auth: "admin",
|
|
58165
|
-
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
58166
|
-
}), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
58331
|
+
}), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
58167
58332
|
kind: "mutation",
|
|
58168
58333
|
auth: "admin"
|
|
58169
58334
|
}), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
@@ -59226,20 +59391,7 @@ method(_void(), MeshStatusSchema), method(object({
|
|
|
59226
59391
|
/** Optional hostname override the host should advertise once joined. */
|
|
59227
59392
|
hostname: string().optional() }), object({
|
|
59228
59393
|
/** Authentication URL the operator should open in a browser. */
|
|
59229
|
-
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() }))
|
|
59230
|
-
/** Optional auth key — when provided, probes the key validity
|
|
59231
|
-
* against the provider's API. Omit when already joined to
|
|
59232
|
-
* just ping the daemon. */
|
|
59233
|
-
authKey: string().optional() }), object({
|
|
59234
|
-
ok: boolean(),
|
|
59235
|
-
/** Provider-side identifier resolved by the probe (tailnet
|
|
59236
|
-
* name for Tailscale, network id for ZeroTier, etc.). */
|
|
59237
|
-
tenant: string().optional(),
|
|
59238
|
-
/** Daemon binary version, when reachable. */
|
|
59239
|
-
daemonVersion: string().optional(),
|
|
59240
|
-
/** Human-readable error when `ok: false`. */
|
|
59241
|
-
error: string().optional()
|
|
59242
|
-
}), { kind: "mutation" });
|
|
59394
|
+
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() }));
|
|
59243
59395
|
/**
|
|
59244
59396
|
* A monotonic total between resets (D944). Energy uses `power-meter.energy`
|
|
59245
59397
|
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
@@ -65381,13 +65533,6 @@ var AddBrokerInputSchema = object({
|
|
|
65381
65533
|
});
|
|
65382
65534
|
var AddBrokerResultSchema = object({ id: string() });
|
|
65383
65535
|
var IdInputSchema = object({ id: string() });
|
|
65384
|
-
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
65385
|
-
ok: literal(true),
|
|
65386
|
-
latencyMs: number()
|
|
65387
|
-
}), object({
|
|
65388
|
-
ok: literal(false),
|
|
65389
|
-
error: string()
|
|
65390
|
-
})]);
|
|
65391
65536
|
var StartEmbeddedInputSchema = object({
|
|
65392
65537
|
port: number().int().min(1).max(65535).default(1883),
|
|
65393
65538
|
/** Allow anonymous connect (no username/password). Default: false. */
|
|
@@ -65410,9 +65555,6 @@ method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnection
|
|
|
65410
65555
|
}), method(IdInputSchema, _void(), {
|
|
65411
65556
|
kind: "mutation",
|
|
65412
65557
|
auth: "admin"
|
|
65413
|
-
}), method(IdInputSchema, TestResultSchema$1, {
|
|
65414
|
-
kind: "mutation",
|
|
65415
|
-
auth: "admin"
|
|
65416
65558
|
}), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
|
|
65417
65559
|
kind: "mutation",
|
|
65418
65560
|
auth: "admin"
|
|
@@ -67076,6 +67218,11 @@ var NcScheduleSchema = object({
|
|
|
67076
67218
|
* as a STATE SUMMARY at the end of each window of `schedule`.
|
|
67077
67219
|
*/
|
|
67078
67220
|
var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
|
|
67221
|
+
_enum([
|
|
67222
|
+
"activation",
|
|
67223
|
+
"deactivation",
|
|
67224
|
+
"schedule"
|
|
67225
|
+
]);
|
|
67079
67226
|
/**
|
|
67080
67227
|
* How a summary window is SENT (D698): `rule` (absent) = ONE notification with
|
|
67081
67228
|
* every section; `section` = one notification per section with something to
|
|
@@ -67092,11 +67239,26 @@ var NcStateSummarySplitSchema = _enum(["rule", "section"]);
|
|
|
67092
67239
|
* midnight crossing); the send time is each window's END. Refused on the
|
|
67093
67240
|
* `alarm` family, which stays instant (`systemEventDeliveryProblems`).
|
|
67094
67241
|
*/
|
|
67095
|
-
var NcSystemEventDeliverySchema = discriminatedUnion("mode", [
|
|
67096
|
-
mode: literal("
|
|
67097
|
-
|
|
67098
|
-
|
|
67099
|
-
|
|
67242
|
+
var NcSystemEventDeliverySchema = discriminatedUnion("mode", [
|
|
67243
|
+
object({ mode: literal("instant") }),
|
|
67244
|
+
object({
|
|
67245
|
+
mode: literal("summary"),
|
|
67246
|
+
schedule: NcScheduleSchema,
|
|
67247
|
+
split: NcStateSummarySplitSchema.optional()
|
|
67248
|
+
}),
|
|
67249
|
+
object({
|
|
67250
|
+
mode: literal("modes"),
|
|
67251
|
+
/** The bad edge, and every kind that has a single edge. */
|
|
67252
|
+
onActivation: boolean(),
|
|
67253
|
+
/** The recovery edges of the classes of the selected kinds (derived, never stored per kind). */
|
|
67254
|
+
onDeactivation: boolean(),
|
|
67255
|
+
/** A state summary at the end of each window; absent = no summary. */
|
|
67256
|
+
onSchedule: object({
|
|
67257
|
+
schedule: NcScheduleSchema,
|
|
67258
|
+
grouping: NcStateSummarySplitSchema
|
|
67259
|
+
}).optional()
|
|
67260
|
+
})
|
|
67261
|
+
]);
|
|
67100
67262
|
/** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
|
|
67101
67263
|
var NcPlateMatcherSchema = object({
|
|
67102
67264
|
/**
|
|
@@ -69456,61 +69618,6 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
69456
69618
|
});
|
|
69457
69619
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
69458
69620
|
/**
|
|
69459
|
-
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
69460
|
-
*
|
|
69461
|
-
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
69462
|
-
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
69463
|
-
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
69464
|
-
* immutable after create.
|
|
69465
|
-
*/
|
|
69466
|
-
var ConfigSchemaPassthrough$1 = unknown();
|
|
69467
|
-
var OidcConnectionSchema = object({
|
|
69468
|
-
id: string().min(1),
|
|
69469
|
-
displayName: string(),
|
|
69470
|
-
icon: string(),
|
|
69471
|
-
issuerUrl: string(),
|
|
69472
|
-
clientId: string(),
|
|
69473
|
-
/** Client id + secret present — never the secret value. */
|
|
69474
|
-
configured: boolean(),
|
|
69475
|
-
/**
|
|
69476
|
-
* Values for the connection form schema. `clientSecret` is a keychain
|
|
69477
|
-
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
69478
|
-
*/
|
|
69479
|
-
formValues: record(string(), unknown()).optional()
|
|
69480
|
-
});
|
|
69481
|
-
/**
|
|
69482
|
-
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
69483
|
-
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
69484
|
-
*/
|
|
69485
|
-
var OidcConnectionUpdateInputSchema = object({
|
|
69486
|
-
id: string().min(1),
|
|
69487
|
-
displayName: string().min(1),
|
|
69488
|
-
icon: string().optional(),
|
|
69489
|
-
issuerUrl: string().optional(),
|
|
69490
|
-
clientId: string().optional(),
|
|
69491
|
-
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
69492
|
-
redirectUri: string().optional(),
|
|
69493
|
-
scopes: string().optional(),
|
|
69494
|
-
usernameClaim: _enum([
|
|
69495
|
-
"preferred_username",
|
|
69496
|
-
"email",
|
|
69497
|
-
"sub"
|
|
69498
|
-
]).optional()
|
|
69499
|
-
});
|
|
69500
|
-
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
69501
|
-
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
69502
|
-
displayName: string().min(1)
|
|
69503
|
-
}), object({ id: string() }), {
|
|
69504
|
-
kind: "mutation",
|
|
69505
|
-
auth: "admin"
|
|
69506
|
-
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
69507
|
-
kind: "mutation",
|
|
69508
|
-
auth: "admin"
|
|
69509
|
-
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
69510
|
-
kind: "mutation",
|
|
69511
|
-
auth: "admin"
|
|
69512
|
-
});
|
|
69513
|
-
/**
|
|
69514
69621
|
* Generic on-screen-display (video overlay) cap. Each camera exposes
|
|
69515
69622
|
* a firmware-defined list of overlays (channel name, timestamp,
|
|
69516
69623
|
* watermark, logo, …); the cap models them as a homogeneous array of
|
|
@@ -75225,6 +75332,16 @@ var RemoteLayoutComponentSchema = object({
|
|
|
75225
75332
|
method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
|
|
75226
75333
|
var RUNNING_POWER_START_DELAY_MS = 3e4;
|
|
75227
75334
|
var RUNNING_POWER_STOP_DELAY_MS = 18e4;
|
|
75335
|
+
/**
|
|
75336
|
+
* Per-type overrides of the dwell defaults. A media player draws power the
|
|
75337
|
+
* moment it is on and stops drawing the moment it is off, so it needs neither
|
|
75338
|
+
* a long dwell nor a second sample. A type absent here keeps the global values.
|
|
75339
|
+
*/
|
|
75340
|
+
var RUNNING_POWER_DEFAULTS_BY_TYPE = { [DeviceType.MediaPlayer]: {
|
|
75341
|
+
startDwellMs: 1e4,
|
|
75342
|
+
stopDwellMs: 1e4,
|
|
75343
|
+
minSamples: 1
|
|
75344
|
+
} };
|
|
75228
75345
|
var RunningStateSourceSchema = _enum(["native", "assigned"]);
|
|
75229
75346
|
var RunningStateStatusSchema = object({
|
|
75230
75347
|
/** null = unknown — never coerced to false (D393). */
|
|
@@ -75242,93 +75359,27 @@ var RunningFlipEventDataSchema = object({
|
|
|
75242
75359
|
phase: string().nullable()
|
|
75243
75360
|
});
|
|
75244
75361
|
var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
|
|
75245
|
-
/**
|
|
75246
|
-
|
|
75247
|
-
|
|
75248
|
-
|
|
75249
|
-
|
|
75250
|
-
|
|
75251
|
-
|
|
75252
|
-
|
|
75253
|
-
|
|
75254
|
-
|
|
75255
|
-
|
|
75256
|
-
|
|
75257
|
-
|
|
75258
|
-
|
|
75259
|
-
|
|
75260
|
-
|
|
75261
|
-
|
|
75262
|
-
|
|
75263
|
-
|
|
75264
|
-
|
|
75265
|
-
|
|
75266
|
-
DeviceType.WaterHeater,
|
|
75267
|
-
DeviceType.Humidifier,
|
|
75268
|
-
DeviceType.Fan,
|
|
75269
|
-
DeviceType.Climate,
|
|
75270
|
-
DeviceType.Thermostat,
|
|
75271
|
-
DeviceType.PetFeeder,
|
|
75272
|
-
DeviceType.Heater
|
|
75273
|
-
],
|
|
75274
|
-
apply: "add",
|
|
75275
|
-
path: "running",
|
|
75276
|
-
sources: [
|
|
75277
|
-
{
|
|
75278
|
-
kind: "switch",
|
|
75279
|
-
transforms: [{ kind: "identity" }]
|
|
75280
|
-
},
|
|
75281
|
-
{
|
|
75282
|
-
kind: "binary",
|
|
75283
|
-
transforms: [{ kind: "identity" }, { kind: "invert" }]
|
|
75284
|
-
},
|
|
75285
|
-
{
|
|
75286
|
-
kind: "numeric",
|
|
75287
|
-
requiredUnit: "W",
|
|
75288
|
-
transforms: [{
|
|
75289
|
-
kind: "threshold",
|
|
75290
|
-
comparisons: ["gte", "lte"],
|
|
75291
|
-
comparison: "gte",
|
|
75292
|
-
defaultThreshold: 5,
|
|
75293
|
-
defaultStartDwellMs: RUNNING_POWER_START_DELAY_MS,
|
|
75294
|
-
defaultStopDwellMs: RUNNING_POWER_STOP_DELAY_MS,
|
|
75295
|
-
thresholdUnit: "W"
|
|
75296
|
-
}]
|
|
75297
|
-
}
|
|
75298
|
-
],
|
|
75299
|
-
companions: [
|
|
75300
|
-
{
|
|
75301
|
-
kind: "constant",
|
|
75302
|
-
path: "source",
|
|
75303
|
-
value: "assigned",
|
|
75304
|
-
when: "always"
|
|
75305
|
-
},
|
|
75306
|
-
{
|
|
75307
|
-
kind: "constant",
|
|
75308
|
-
path: "phase",
|
|
75309
|
-
value: null,
|
|
75310
|
-
when: "add"
|
|
75311
|
-
},
|
|
75312
|
-
{
|
|
75313
|
-
kind: "clock",
|
|
75314
|
-
path: "lastFetchedAt",
|
|
75315
|
-
when: "always"
|
|
75316
|
-
},
|
|
75317
|
-
{
|
|
75318
|
-
kind: "transition-time",
|
|
75319
|
-
path: "since",
|
|
75320
|
-
sourceChangedAt: "prefer"
|
|
75321
|
-
}
|
|
75322
|
-
],
|
|
75323
|
-
sourceReport: {
|
|
75324
|
-
status: "assignment-status",
|
|
75325
|
-
runtimeField: "source",
|
|
75326
|
-
nativeValue: "native",
|
|
75327
|
-
assignedValue: "assigned"
|
|
75328
|
-
},
|
|
75329
|
-
advancedExpression: true
|
|
75330
|
-
}]
|
|
75331
|
-
};
|
|
75362
|
+
/**
|
|
75363
|
+
* Customize offers "running" only where a job makes sense (D933): a cycle,
|
|
75364
|
+
* a clean, a mow, playback, or another appliance that is on and working.
|
|
75365
|
+
* The cap itself stays on every type — a camera can still report running
|
|
75366
|
+
* natively; it just has no card.
|
|
75367
|
+
*/
|
|
75368
|
+
var RUNNING_CUSTOMIZE_DEVICE_TYPES = [
|
|
75369
|
+
DeviceType.Washer,
|
|
75370
|
+
DeviceType.Dryer,
|
|
75371
|
+
DeviceType.Dishwasher,
|
|
75372
|
+
DeviceType.Vacuum,
|
|
75373
|
+
DeviceType.LawnMower,
|
|
75374
|
+
DeviceType.MediaPlayer,
|
|
75375
|
+
DeviceType.WaterHeater,
|
|
75376
|
+
DeviceType.Humidifier,
|
|
75377
|
+
DeviceType.Fan,
|
|
75378
|
+
DeviceType.Climate,
|
|
75379
|
+
DeviceType.Thermostat,
|
|
75380
|
+
DeviceType.PetFeeder,
|
|
75381
|
+
DeviceType.Heater
|
|
75382
|
+
];
|
|
75332
75383
|
var runningStateCapability = {
|
|
75333
75384
|
name: "running-state",
|
|
75334
75385
|
scope: "device",
|
|
@@ -75348,7 +75399,79 @@ var runningStateCapability = {
|
|
|
75348
75399
|
durability: "restored",
|
|
75349
75400
|
history: { fields: { running: { kind: "boolean" } } },
|
|
75350
75401
|
volatileStateFields: ["lastFetchedAt"],
|
|
75351
|
-
customize:
|
|
75402
|
+
customize: {
|
|
75403
|
+
version: 1,
|
|
75404
|
+
overridable: [{
|
|
75405
|
+
path: "running",
|
|
75406
|
+
by: "running"
|
|
75407
|
+
}],
|
|
75408
|
+
targets: [{
|
|
75409
|
+
id: "running",
|
|
75410
|
+
cardinality: "scalar",
|
|
75411
|
+
vocabulary: {
|
|
75412
|
+
cap: "running-state",
|
|
75413
|
+
stateField: "running"
|
|
75414
|
+
},
|
|
75415
|
+
deviceTypes: RUNNING_CUSTOMIZE_DEVICE_TYPES,
|
|
75416
|
+
apply: "add",
|
|
75417
|
+
path: "running",
|
|
75418
|
+
sources: [
|
|
75419
|
+
{
|
|
75420
|
+
kind: "switch",
|
|
75421
|
+
transforms: [{ kind: "identity" }]
|
|
75422
|
+
},
|
|
75423
|
+
{
|
|
75424
|
+
kind: "binary",
|
|
75425
|
+
transforms: [{ kind: "identity" }, { kind: "invert" }]
|
|
75426
|
+
},
|
|
75427
|
+
{
|
|
75428
|
+
kind: "numeric",
|
|
75429
|
+
requiredUnit: "W",
|
|
75430
|
+
transforms: [{
|
|
75431
|
+
kind: "threshold",
|
|
75432
|
+
comparisons: ["gte", "lte"],
|
|
75433
|
+
comparison: "gte",
|
|
75434
|
+
defaultThreshold: 5,
|
|
75435
|
+
defaultStartDwellMs: RUNNING_POWER_START_DELAY_MS,
|
|
75436
|
+
defaultStopDwellMs: RUNNING_POWER_STOP_DELAY_MS,
|
|
75437
|
+
deviceTypeDefaults: RUNNING_POWER_DEFAULTS_BY_TYPE,
|
|
75438
|
+
thresholdUnit: "W"
|
|
75439
|
+
}]
|
|
75440
|
+
}
|
|
75441
|
+
],
|
|
75442
|
+
companions: [
|
|
75443
|
+
{
|
|
75444
|
+
kind: "constant",
|
|
75445
|
+
path: "source",
|
|
75446
|
+
value: "assigned",
|
|
75447
|
+
when: "always"
|
|
75448
|
+
},
|
|
75449
|
+
{
|
|
75450
|
+
kind: "constant",
|
|
75451
|
+
path: "phase",
|
|
75452
|
+
value: null,
|
|
75453
|
+
when: "add"
|
|
75454
|
+
},
|
|
75455
|
+
{
|
|
75456
|
+
kind: "clock",
|
|
75457
|
+
path: "lastFetchedAt",
|
|
75458
|
+
when: "always"
|
|
75459
|
+
},
|
|
75460
|
+
{
|
|
75461
|
+
kind: "transition-time",
|
|
75462
|
+
path: "since",
|
|
75463
|
+
sourceChangedAt: "prefer"
|
|
75464
|
+
}
|
|
75465
|
+
],
|
|
75466
|
+
sourceReport: {
|
|
75467
|
+
status: "assignment-status",
|
|
75468
|
+
runtimeField: "source",
|
|
75469
|
+
nativeValue: "native",
|
|
75470
|
+
assignedValue: "assigned"
|
|
75471
|
+
},
|
|
75472
|
+
advancedExpression: true
|
|
75473
|
+
}]
|
|
75474
|
+
}
|
|
75352
75475
|
};
|
|
75353
75476
|
/**
|
|
75354
75477
|
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
@@ -76592,21 +76715,6 @@ method(object({
|
|
|
76592
76715
|
*/
|
|
76593
76716
|
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
76594
76717
|
}), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
|
|
76595
|
-
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
76596
|
-
providerId: string().min(1),
|
|
76597
|
-
displayName: string().min(1),
|
|
76598
|
-
configSchema: unknown(),
|
|
76599
|
-
nodeLocal: boolean(),
|
|
76600
|
-
shouldSaveDiskSpace: literal(true),
|
|
76601
|
-
minFreePercent: number().min(0).max(100)
|
|
76602
|
-
}), object({
|
|
76603
|
-
providerId: string().min(1),
|
|
76604
|
-
displayName: string().min(1),
|
|
76605
|
-
configSchema: unknown(),
|
|
76606
|
-
nodeLocal: boolean(),
|
|
76607
|
-
shouldSaveDiskSpace: literal(false),
|
|
76608
|
-
minFreePercent: literal(null)
|
|
76609
|
-
})]);
|
|
76610
76718
|
method(object({
|
|
76611
76719
|
location: StorageLocationRefSchema,
|
|
76612
76720
|
relativePath: string()
|
|
@@ -76668,42 +76776,7 @@ method(object({
|
|
|
76668
76776
|
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
|
|
76669
76777
|
kind: "mutation",
|
|
76670
76778
|
auth: "admin"
|
|
76671
|
-
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(
|
|
76672
|
-
createdAt: true,
|
|
76673
|
-
updatedAt: true
|
|
76674
|
-
}), StorageLocationSchema, {
|
|
76675
|
-
kind: "mutation",
|
|
76676
|
-
auth: "admin"
|
|
76677
|
-
}), method(object({
|
|
76678
|
-
id: string(),
|
|
76679
|
-
force: boolean().optional()
|
|
76680
|
-
}), _void(), {
|
|
76681
|
-
kind: "mutation",
|
|
76682
|
-
auth: "admin"
|
|
76683
|
-
}), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
|
|
76684
|
-
ok: boolean(),
|
|
76685
|
-
error: string().optional()
|
|
76686
|
-
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
76687
|
-
providerId: string(),
|
|
76688
|
-
/**
|
|
76689
|
-
* The location this config is an UNSAVED edit of, when there is one.
|
|
76690
|
-
*
|
|
76691
|
-
* `listLocations` replaces every declared secret with the redaction
|
|
76692
|
-
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
76693
|
-
* credential the operator did not retype — and posting that here
|
|
76694
|
-
* without a way to resolve it makes the provider try to authenticate
|
|
76695
|
-
* as `__camstack_redacted__` and report the operator's own working
|
|
76696
|
-
* password as wrong. Given this id, the orchestrator restores each
|
|
76697
|
-
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
76698
|
-
* before dispatching. Omitted by the "Add location" wizard, where
|
|
76699
|
-
* every value was typed just now and nothing is stored yet.
|
|
76700
|
-
*/
|
|
76701
|
-
locationId: string().optional(),
|
|
76702
|
-
config: record(string(), unknown())
|
|
76703
|
-
}), object({
|
|
76704
|
-
ok: boolean(),
|
|
76705
|
-
error: string().optional()
|
|
76706
|
-
}), { auth: "admin" });
|
|
76779
|
+
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
|
|
76707
76780
|
/** One table of the settings database. */
|
|
76708
76781
|
var StorageTableSchema = object({
|
|
76709
76782
|
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
@@ -78225,7 +78298,7 @@ var TerminalInstanceInfoSchema = object({
|
|
|
78225
78298
|
/**
|
|
78226
78299
|
* False while the node the instance is bound to is not in the cluster
|
|
78227
78300
|
* topology (forgotten, renamed, re-paired). The instance is kept; the
|
|
78228
|
-
* operator re-points it
|
|
78301
|
+
* operator re-points it on the Terminal page.
|
|
78229
78302
|
*/
|
|
78230
78303
|
nodeInCluster: boolean(),
|
|
78231
78304
|
profileId: string(),
|
|
@@ -78263,41 +78336,7 @@ var TerminalOutputBatchSchema = object({
|
|
|
78263
78336
|
snapshot: string().optional(),
|
|
78264
78337
|
events: array(TerminalOutputEventSchema).readonly()
|
|
78265
78338
|
});
|
|
78266
|
-
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(
|
|
78267
|
-
targetNodeId: string().min(1),
|
|
78268
|
-
profileId: string().min(1),
|
|
78269
|
-
name: string().trim().min(1).max(160).optional(),
|
|
78270
|
-
executable: string().max(1024).optional(),
|
|
78271
|
-
args: array(string().max(2048)).max(64).optional(),
|
|
78272
|
-
cwd: string().max(1024).optional(),
|
|
78273
|
-
environment: array(string().max(4096)).max(64).optional(),
|
|
78274
|
-
profileSettings: ProfileSettingsBagSchema.optional()
|
|
78275
|
-
}), TerminalInstanceInfoSchema, {
|
|
78276
|
-
kind: "mutation",
|
|
78277
|
-
auth: "admin"
|
|
78278
|
-
}), method(object({
|
|
78279
|
-
instanceId: string().min(1),
|
|
78280
|
-
/** Re-point the instance to another node; the profile must exist there. */
|
|
78281
|
-
targetNodeId: string().min(1).max(256).optional(),
|
|
78282
|
-
name: string().trim().min(1).max(160).optional(),
|
|
78283
|
-
executable: string().max(1024).optional(),
|
|
78284
|
-
args: array(string().max(2048)).max(64).optional(),
|
|
78285
|
-
cwd: string().max(1024).optional(),
|
|
78286
|
-
environment: array(string().max(4096)).max(64).optional(),
|
|
78287
|
-
profileSettings: ProfileSettingsBagSchema.optional()
|
|
78288
|
-
}), TerminalInstanceInfoSchema, {
|
|
78289
|
-
kind: "mutation",
|
|
78290
|
-
auth: "admin"
|
|
78291
|
-
}), method(object({ instanceId: string().min(1) }), _void(), {
|
|
78292
|
-
kind: "mutation",
|
|
78293
|
-
auth: "admin"
|
|
78294
|
-
}), method(object({
|
|
78295
|
-
instanceId: string().min(1),
|
|
78296
|
-
enabled: boolean()
|
|
78297
|
-
}), TerminalInstanceInfoSchema, {
|
|
78298
|
-
kind: "mutation",
|
|
78299
|
-
auth: "admin"
|
|
78300
|
-
}), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
|
|
78339
|
+
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
|
|
78301
78340
|
stableId: string().min(1),
|
|
78302
78341
|
name: string().trim().min(1).max(160).optional()
|
|
78303
78342
|
}), TerminalInstanceInfoSchema, {
|
|
@@ -78366,6 +78405,11 @@ var TimedRunUnitSchema = _enum(["s", "min"]);
|
|
|
78366
78405
|
_enum(["switch", "pet-fountain"]);
|
|
78367
78406
|
DeviceType.Heater, DeviceType.PetFountain;
|
|
78368
78407
|
var TIMED_RUN_DEVICE_TYPES = [DeviceType.Heater, DeviceType.PetFountain];
|
|
78408
|
+
/** One duration, as the one-tap run asks for it. */
|
|
78409
|
+
var TimedRunQuickRunSchema = object({
|
|
78410
|
+
duration: number().positive().finite(),
|
|
78411
|
+
unit: TimedRunUnitSchema
|
|
78412
|
+
});
|
|
78369
78413
|
/** Why a request was not carried out. Typed, so a caller can say it in words. */
|
|
78370
78414
|
var TimedRunRefusalSchema = _enum([
|
|
78371
78415
|
"unsupported-device",
|
|
@@ -78386,8 +78430,23 @@ var TimedRunStatusSchema = object({
|
|
|
78386
78430
|
activeUntil: number().nullable(),
|
|
78387
78431
|
/** Who asked for the run (a user id or an addon id); `null` when nothing is armed or it did not say. */
|
|
78388
78432
|
requestedBy: string().nullable(),
|
|
78433
|
+
/**
|
|
78434
|
+
* The one-tap run of THIS device: what a "Run" button on any surface starts. `null` when
|
|
78435
|
+
* the device is gone or its type has no timed run; a status from before this field
|
|
78436
|
+
* existed has none, and a surface falls back to the type's default.
|
|
78437
|
+
*/
|
|
78438
|
+
quickRun: TimedRunQuickRunSchema.nullable(),
|
|
78389
78439
|
lastFetchedAt: number()
|
|
78390
78440
|
});
|
|
78441
|
+
/** What the settings form needs: the unit, the bounds and the type's default. */
|
|
78442
|
+
var TimedRunOptionsSchema = object({
|
|
78443
|
+
unit: TimedRunUnitSchema,
|
|
78444
|
+
/** The longest run, in `unit`. */
|
|
78445
|
+
maxDuration: number(),
|
|
78446
|
+
defaultDuration: number()
|
|
78447
|
+
});
|
|
78448
|
+
/** The one-tap run duration, in the type's unit; `null` returns to the type's default. */
|
|
78449
|
+
var TimedRunSettingsPatchSchema = object({ quickRunDuration: number().positive().finite().nullable().optional() });
|
|
78391
78450
|
var timedRunCapability = {
|
|
78392
78451
|
name: "timed-run",
|
|
78393
78452
|
scope: "device",
|
|
@@ -78395,7 +78454,24 @@ var timedRunCapability = {
|
|
|
78395
78454
|
kind: "wrapper",
|
|
78396
78455
|
defaultActive: true,
|
|
78397
78456
|
deviceTypes: TIMED_RUN_DEVICE_TYPES,
|
|
78457
|
+
deviceConfig: { ui: {
|
|
78458
|
+
kind: "derived-form",
|
|
78459
|
+
builderId: "timed-run",
|
|
78460
|
+
tab: "general"
|
|
78461
|
+
} },
|
|
78398
78462
|
methods: {
|
|
78463
|
+
getOptions: method(object({ deviceId: number().int() }), TimedRunOptionsSchema),
|
|
78464
|
+
/**
|
|
78465
|
+
* The one-tap run of this device. A duration above the type's maximum is
|
|
78466
|
+
* REFUSED (thrown), like `runFor`: the maximum is a safety bound.
|
|
78467
|
+
*/
|
|
78468
|
+
setSettings: method(object({
|
|
78469
|
+
deviceId: number().int(),
|
|
78470
|
+
settings: TimedRunSettingsPatchSchema
|
|
78471
|
+
}), _void(), {
|
|
78472
|
+
kind: "mutation",
|
|
78473
|
+
auth: "admin"
|
|
78474
|
+
}),
|
|
78399
78475
|
/**
|
|
78400
78476
|
* Turn the device on and arm its off. A run already armed is replaced (the
|
|
78401
78477
|
* new deadline wins). Refuses with a typed reason above the type's maximum.
|
|
@@ -78519,7 +78595,7 @@ var TrainingProviderTypeSchema = object({
|
|
|
78519
78595
|
label: string(),
|
|
78520
78596
|
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
78521
78597
|
iconAsset: string(),
|
|
78522
|
-
/** Owning `training-provider` addon
|
|
78598
|
+
/** Owning `training-provider` addon. */
|
|
78523
78599
|
addonId: string(),
|
|
78524
78600
|
/**
|
|
78525
78601
|
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
@@ -78564,36 +78640,6 @@ var TrainingProviderDescriptorSchema = object({
|
|
|
78564
78640
|
*/
|
|
78565
78641
|
formValues: record(string(), unknown()).optional()
|
|
78566
78642
|
});
|
|
78567
|
-
/**
|
|
78568
|
-
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
78569
|
-
* An omitted or empty `tokenSecret` means KEEP the stored secret (D787).
|
|
78570
|
-
* `{ $secretClear: true }` clears it.
|
|
78571
|
-
*/
|
|
78572
|
-
var TrainingProviderUpdateEntryInputSchema = object({
|
|
78573
|
-
entryId: string().min(1),
|
|
78574
|
-
name: string().min(1),
|
|
78575
|
-
tokenId: string().optional(),
|
|
78576
|
-
tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
78577
|
-
environment: string().optional(),
|
|
78578
|
-
gpuType: string().optional(),
|
|
78579
|
-
region: string().optional(),
|
|
78580
|
-
costCapUsd: number().nonnegative().optional(),
|
|
78581
|
-
maxRuntimeSec: number().int().positive().optional(),
|
|
78582
|
-
failedDatasetGraceHours: number().nonnegative().optional()
|
|
78583
|
-
});
|
|
78584
|
-
var TrainingCredentialCheckSchema = object({
|
|
78585
|
-
ok: boolean(),
|
|
78586
|
-
/** Workspace / account name, when the provider says. */
|
|
78587
|
-
account: string().optional(),
|
|
78588
|
-
reason: _enum([
|
|
78589
|
-
"missing",
|
|
78590
|
-
"invalid",
|
|
78591
|
-
"forbidden",
|
|
78592
|
-
"network",
|
|
78593
|
-
"quota"
|
|
78594
|
-
]).optional(),
|
|
78595
|
-
detail: string().optional()
|
|
78596
|
-
});
|
|
78597
78643
|
var ProviderJobStateSchema = _enum([
|
|
78598
78644
|
"pending",
|
|
78599
78645
|
"starting",
|
|
@@ -78641,22 +78687,6 @@ var TrainingLogLineSchema = object({
|
|
|
78641
78687
|
text: string()
|
|
78642
78688
|
});
|
|
78643
78689
|
method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderTypeSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderEntrySchema).readonly(), { auth: "admin" }), method(object({
|
|
78644
|
-
typeId: string().min(1),
|
|
78645
|
-
name: string().min(1)
|
|
78646
|
-
}), object({ entryId: string() }), {
|
|
78647
|
-
kind: "mutation",
|
|
78648
|
-
auth: "admin"
|
|
78649
|
-
}), method(object({ entryId: string().min(1) }), object({ ok: literal(true) }), {
|
|
78650
|
-
kind: "mutation",
|
|
78651
|
-
auth: "admin"
|
|
78652
|
-
}), method(TrainingProviderUpdateEntryInputSchema, object({ ok: literal(true) }), {
|
|
78653
|
-
kind: "mutation",
|
|
78654
|
-
auth: "admin"
|
|
78655
|
-
}), method(object({ entryId: string().min(1) }), TrainingCredentialCheckSchema, {
|
|
78656
|
-
kind: "mutation",
|
|
78657
|
-
auth: "admin",
|
|
78658
|
-
timeoutMs: 3e4
|
|
78659
|
-
}), method(object({
|
|
78660
78690
|
entryId: string().min(1),
|
|
78661
78691
|
gpuType: string(),
|
|
78662
78692
|
region: string(),
|
|
@@ -83234,21 +83264,28 @@ Object.freeze({
|
|
|
83234
83264
|
access: "view",
|
|
83235
83265
|
role: "admin"
|
|
83236
83266
|
},
|
|
83237
|
-
"addonSettings.
|
|
83267
|
+
"addonSettings.getIntegrationFormSchema": {
|
|
83238
83268
|
capName: "addon-settings",
|
|
83239
83269
|
capScope: "system",
|
|
83240
83270
|
addonId: null,
|
|
83241
83271
|
access: "view",
|
|
83242
83272
|
role: "admin"
|
|
83243
83273
|
},
|
|
83244
|
-
"addonSettings.
|
|
83274
|
+
"addonSettings.getScopedSettings": {
|
|
83245
83275
|
capName: "addon-settings",
|
|
83246
83276
|
capScope: "system",
|
|
83247
83277
|
addonId: null,
|
|
83248
83278
|
access: "view",
|
|
83249
83279
|
role: "admin"
|
|
83250
83280
|
},
|
|
83251
|
-
"addonSettings.
|
|
83281
|
+
"addonSettings.listScopedSettings": {
|
|
83282
|
+
capName: "addon-settings",
|
|
83283
|
+
capScope: "system",
|
|
83284
|
+
addonId: null,
|
|
83285
|
+
access: "view",
|
|
83286
|
+
role: "admin"
|
|
83287
|
+
},
|
|
83288
|
+
"addonSettings.readScopedValues": {
|
|
83252
83289
|
capName: "addon-settings",
|
|
83253
83290
|
capScope: "system",
|
|
83254
83291
|
addonId: null,
|
|
@@ -83269,7 +83306,7 @@ Object.freeze({
|
|
|
83269
83306
|
access: "create",
|
|
83270
83307
|
role: "admin"
|
|
83271
83308
|
},
|
|
83272
|
-
"addonSettings.
|
|
83309
|
+
"addonSettings.updateScopedSettings": {
|
|
83273
83310
|
capName: "addon-settings",
|
|
83274
83311
|
capScope: "system",
|
|
83275
83312
|
addonId: null,
|
|
@@ -84109,20 +84146,6 @@ Object.freeze({
|
|
|
84109
84146
|
access: "view",
|
|
84110
84147
|
role: "admin"
|
|
84111
84148
|
},
|
|
84112
|
-
"connectionTest.describeTest": {
|
|
84113
|
-
capName: "connection-test",
|
|
84114
|
-
capScope: "system",
|
|
84115
|
-
addonId: null,
|
|
84116
|
-
access: "view",
|
|
84117
|
-
role: "admin"
|
|
84118
|
-
},
|
|
84119
|
-
"connectionTest.testSettings": {
|
|
84120
|
-
capName: "connection-test",
|
|
84121
|
-
capScope: "system",
|
|
84122
|
-
addonId: null,
|
|
84123
|
-
access: "create",
|
|
84124
|
-
role: "admin"
|
|
84125
|
-
},
|
|
84126
84149
|
"connectivity.getStatus": {
|
|
84127
84150
|
capName: "connectivity",
|
|
84128
84151
|
capScope: "device",
|
|
@@ -85453,6 +85476,13 @@ Object.freeze({
|
|
|
85453
85476
|
access: "create",
|
|
85454
85477
|
role: "admin"
|
|
85455
85478
|
},
|
|
85479
|
+
"deviceManager.setAlias": {
|
|
85480
|
+
capName: "device-manager",
|
|
85481
|
+
capScope: "system",
|
|
85482
|
+
addonId: null,
|
|
85483
|
+
access: "create",
|
|
85484
|
+
role: "admin"
|
|
85485
|
+
},
|
|
85456
85486
|
"deviceManager.setChildLayout": {
|
|
85457
85487
|
capName: "device-manager",
|
|
85458
85488
|
capScope: "system",
|
|
@@ -86489,13 +86519,6 @@ Object.freeze({
|
|
|
86489
86519
|
access: "view",
|
|
86490
86520
|
role: "admin"
|
|
86491
86521
|
},
|
|
86492
|
-
"integrations.testConnection": {
|
|
86493
|
-
capName: "integrations",
|
|
86494
|
-
capScope: "system",
|
|
86495
|
-
addonId: null,
|
|
86496
|
-
access: "create",
|
|
86497
|
-
role: "admin"
|
|
86498
|
-
},
|
|
86499
86522
|
"integrations.update": {
|
|
86500
86523
|
capName: "integrations",
|
|
86501
86524
|
capScope: "system",
|
|
@@ -86748,13 +86771,6 @@ Object.freeze({
|
|
|
86748
86771
|
access: "delete",
|
|
86749
86772
|
role: "admin"
|
|
86750
86773
|
},
|
|
86751
|
-
"llm.deleteProfile": {
|
|
86752
|
-
capName: "llm",
|
|
86753
|
-
capScope: "system",
|
|
86754
|
-
addonId: null,
|
|
86755
|
-
access: "delete",
|
|
86756
|
-
role: "admin"
|
|
86757
|
-
},
|
|
86758
86774
|
"llm.generate": {
|
|
86759
86775
|
capName: "llm",
|
|
86760
86776
|
capScope: "system",
|
|
@@ -86818,20 +86834,6 @@ Object.freeze({
|
|
|
86818
86834
|
access: "view",
|
|
86819
86835
|
role: "admin"
|
|
86820
86836
|
},
|
|
86821
|
-
"llm.listProfileKinds": {
|
|
86822
|
-
capName: "llm",
|
|
86823
|
-
capScope: "system",
|
|
86824
|
-
addonId: null,
|
|
86825
|
-
access: "view",
|
|
86826
|
-
role: "admin"
|
|
86827
|
-
},
|
|
86828
|
-
"llm.listProfiles": {
|
|
86829
|
-
capName: "llm",
|
|
86830
|
-
capScope: "system",
|
|
86831
|
-
addonId: null,
|
|
86832
|
-
access: "view",
|
|
86833
|
-
role: "admin"
|
|
86834
|
-
},
|
|
86835
86837
|
"llm.listRuntimeNodes": {
|
|
86836
86838
|
capName: "llm",
|
|
86837
86839
|
capScope: "system",
|
|
@@ -86839,13 +86841,6 @@ Object.freeze({
|
|
|
86839
86841
|
access: "view",
|
|
86840
86842
|
role: "admin"
|
|
86841
86843
|
},
|
|
86842
|
-
"llm.migrateRawSecrets": {
|
|
86843
|
-
capName: "llm",
|
|
86844
|
-
capScope: "system",
|
|
86845
|
-
addonId: null,
|
|
86846
|
-
access: "create",
|
|
86847
|
-
role: "admin"
|
|
86848
|
-
},
|
|
86849
86844
|
"llm.resolveModelRef": {
|
|
86850
86845
|
capName: "llm",
|
|
86851
86846
|
capScope: "system",
|
|
@@ -86874,20 +86869,6 @@ Object.freeze({
|
|
|
86874
86869
|
access: "create",
|
|
86875
86870
|
role: "admin"
|
|
86876
86871
|
},
|
|
86877
|
-
"llm.testProfile": {
|
|
86878
|
-
capName: "llm",
|
|
86879
|
-
capScope: "system",
|
|
86880
|
-
addonId: null,
|
|
86881
|
-
access: "create",
|
|
86882
|
-
role: "admin"
|
|
86883
|
-
},
|
|
86884
|
-
"llm.upsertProfile": {
|
|
86885
|
-
capName: "llm",
|
|
86886
|
-
capScope: "system",
|
|
86887
|
-
addonId: null,
|
|
86888
|
-
access: "create",
|
|
86889
|
-
role: "admin"
|
|
86890
|
-
},
|
|
86891
86872
|
"llmRuntime.appleAvailability": {
|
|
86892
86873
|
capName: "llm-runtime",
|
|
86893
86874
|
capScope: "system",
|
|
@@ -87392,13 +87373,6 @@ Object.freeze({
|
|
|
87392
87373
|
access: "create",
|
|
87393
87374
|
role: "admin"
|
|
87394
87375
|
},
|
|
87395
|
-
"meshNetwork.testConnection": {
|
|
87396
|
-
capName: "mesh-network",
|
|
87397
|
-
capScope: "system",
|
|
87398
|
-
addonId: null,
|
|
87399
|
-
access: "create",
|
|
87400
|
-
role: "admin"
|
|
87401
|
-
},
|
|
87402
87376
|
"meter.getStatus": {
|
|
87403
87377
|
capName: "meter",
|
|
87404
87378
|
capScope: "device",
|
|
@@ -87728,13 +87702,6 @@ Object.freeze({
|
|
|
87728
87702
|
access: "create",
|
|
87729
87703
|
role: "admin"
|
|
87730
87704
|
},
|
|
87731
|
-
"mqttBroker.testConnection": {
|
|
87732
|
-
capName: "mqtt-broker",
|
|
87733
|
-
capScope: "system",
|
|
87734
|
-
addonId: null,
|
|
87735
|
-
access: "create",
|
|
87736
|
-
role: "admin"
|
|
87737
|
-
},
|
|
87738
87705
|
"nativeObjectDetection.getOptions": {
|
|
87739
87706
|
capName: "native-object-detection",
|
|
87740
87707
|
capScope: "device",
|
|
@@ -88064,13 +88031,6 @@ Object.freeze({
|
|
|
88064
88031
|
access: "create",
|
|
88065
88032
|
role: "admin"
|
|
88066
88033
|
},
|
|
88067
|
-
"notificationOutput.deleteTarget": {
|
|
88068
|
-
capName: "notification-output",
|
|
88069
|
-
capScope: "system",
|
|
88070
|
-
addonId: null,
|
|
88071
|
-
access: "delete",
|
|
88072
|
-
role: "admin"
|
|
88073
|
-
},
|
|
88074
88034
|
"notificationOutput.discoverTargets": {
|
|
88075
88035
|
capName: "notification-output",
|
|
88076
88036
|
capScope: "system",
|
|
@@ -88078,13 +88038,6 @@ Object.freeze({
|
|
|
88078
88038
|
access: "view",
|
|
88079
88039
|
role: "admin"
|
|
88080
88040
|
},
|
|
88081
|
-
"notificationOutput.getTarget": {
|
|
88082
|
-
capName: "notification-output",
|
|
88083
|
-
capScope: "system",
|
|
88084
|
-
addonId: null,
|
|
88085
|
-
access: "view",
|
|
88086
|
-
role: "admin"
|
|
88087
|
-
},
|
|
88088
88041
|
"notificationOutput.listTargetKinds": {
|
|
88089
88042
|
capName: "notification-output",
|
|
88090
88043
|
capScope: "system",
|
|
@@ -88092,13 +88045,6 @@ Object.freeze({
|
|
|
88092
88045
|
access: "view",
|
|
88093
88046
|
role: "admin"
|
|
88094
88047
|
},
|
|
88095
|
-
"notificationOutput.listTargets": {
|
|
88096
|
-
capName: "notification-output",
|
|
88097
|
-
capScope: "system",
|
|
88098
|
-
addonId: null,
|
|
88099
|
-
access: "view",
|
|
88100
|
-
role: "admin"
|
|
88101
|
-
},
|
|
88102
88048
|
"notificationOutput.probeTargetConfig": {
|
|
88103
88049
|
capName: "notification-output",
|
|
88104
88050
|
capScope: "system",
|
|
@@ -88113,13 +88059,6 @@ Object.freeze({
|
|
|
88113
88059
|
access: "create",
|
|
88114
88060
|
role: "admin"
|
|
88115
88061
|
},
|
|
88116
|
-
"notificationOutput.setTargetEnabled": {
|
|
88117
|
-
capName: "notification-output",
|
|
88118
|
-
capScope: "system",
|
|
88119
|
-
addonId: null,
|
|
88120
|
-
access: "create",
|
|
88121
|
-
role: "admin"
|
|
88122
|
-
},
|
|
88123
88062
|
"notificationOutput.testTarget": {
|
|
88124
88063
|
capName: "notification-output",
|
|
88125
88064
|
capScope: "system",
|
|
@@ -88127,13 +88066,6 @@ Object.freeze({
|
|
|
88127
88066
|
access: "create",
|
|
88128
88067
|
role: "admin"
|
|
88129
88068
|
},
|
|
88130
|
-
"notificationOutput.upsertTarget": {
|
|
88131
|
-
capName: "notification-output",
|
|
88132
|
-
capScope: "system",
|
|
88133
|
-
addonId: null,
|
|
88134
|
-
access: "create",
|
|
88135
|
-
role: "admin"
|
|
88136
|
-
},
|
|
88137
88069
|
"notificationRules.activateAlarmProfile": {
|
|
88138
88070
|
capName: "notification-rules",
|
|
88139
88071
|
capScope: "system",
|
|
@@ -88393,41 +88325,6 @@ Object.freeze({
|
|
|
88393
88325
|
access: "view",
|
|
88394
88326
|
role: "admin"
|
|
88395
88327
|
},
|
|
88396
|
-
"oidcConnections.connectionFormSchema": {
|
|
88397
|
-
capName: "oidc-connections",
|
|
88398
|
-
capScope: "system",
|
|
88399
|
-
addonId: null,
|
|
88400
|
-
access: "view",
|
|
88401
|
-
role: "admin"
|
|
88402
|
-
},
|
|
88403
|
-
"oidcConnections.createConnection": {
|
|
88404
|
-
capName: "oidc-connections",
|
|
88405
|
-
capScope: "system",
|
|
88406
|
-
addonId: null,
|
|
88407
|
-
access: "create",
|
|
88408
|
-
role: "admin"
|
|
88409
|
-
},
|
|
88410
|
-
"oidcConnections.deleteConnection": {
|
|
88411
|
-
capName: "oidc-connections",
|
|
88412
|
-
capScope: "system",
|
|
88413
|
-
addonId: null,
|
|
88414
|
-
access: "delete",
|
|
88415
|
-
role: "admin"
|
|
88416
|
-
},
|
|
88417
|
-
"oidcConnections.listConnections": {
|
|
88418
|
-
capName: "oidc-connections",
|
|
88419
|
-
capScope: "system",
|
|
88420
|
-
addonId: null,
|
|
88421
|
-
access: "view",
|
|
88422
|
-
role: "admin"
|
|
88423
|
-
},
|
|
88424
|
-
"oidcConnections.updateConnection": {
|
|
88425
|
-
capName: "oidc-connections",
|
|
88426
|
-
capScope: "system",
|
|
88427
|
-
addonId: null,
|
|
88428
|
-
access: "create",
|
|
88429
|
-
role: "admin"
|
|
88430
|
-
},
|
|
88431
88328
|
"osd.getStatus": {
|
|
88432
88329
|
capName: "osd",
|
|
88433
88330
|
capScope: "device",
|
|
@@ -91459,13 +91356,6 @@ Object.freeze({
|
|
|
91459
91356
|
access: "delete",
|
|
91460
91357
|
role: "admin"
|
|
91461
91358
|
},
|
|
91462
|
-
"storage.deleteLocation": {
|
|
91463
|
-
capName: "storage",
|
|
91464
|
-
capScope: "system",
|
|
91465
|
-
addonId: null,
|
|
91466
|
-
access: "delete",
|
|
91467
|
-
role: "admin"
|
|
91468
|
-
},
|
|
91469
91359
|
"storage.endDownload": {
|
|
91470
91360
|
capName: "storage",
|
|
91471
91361
|
capScope: "system",
|
|
@@ -91522,13 +91412,6 @@ Object.freeze({
|
|
|
91522
91412
|
access: "view",
|
|
91523
91413
|
role: "admin"
|
|
91524
91414
|
},
|
|
91525
|
-
"storage.listProviders": {
|
|
91526
|
-
capName: "storage",
|
|
91527
|
-
capScope: "system",
|
|
91528
|
-
addonId: null,
|
|
91529
|
-
access: "view",
|
|
91530
|
-
role: "admin"
|
|
91531
|
-
},
|
|
91532
91415
|
"storage.read": {
|
|
91533
91416
|
capName: "storage",
|
|
91534
91417
|
capScope: "system",
|
|
@@ -91550,27 +91433,6 @@ Object.freeze({
|
|
|
91550
91433
|
access: "view",
|
|
91551
91434
|
role: "admin"
|
|
91552
91435
|
},
|
|
91553
|
-
"storage.testConfig": {
|
|
91554
|
-
capName: "storage",
|
|
91555
|
-
capScope: "system",
|
|
91556
|
-
addonId: null,
|
|
91557
|
-
access: "view",
|
|
91558
|
-
role: "admin"
|
|
91559
|
-
},
|
|
91560
|
-
"storage.testLocation": {
|
|
91561
|
-
capName: "storage",
|
|
91562
|
-
capScope: "system",
|
|
91563
|
-
addonId: null,
|
|
91564
|
-
access: "view",
|
|
91565
|
-
role: "admin"
|
|
91566
|
-
},
|
|
91567
|
-
"storage.upsertLocation": {
|
|
91568
|
-
capName: "storage",
|
|
91569
|
-
capScope: "system",
|
|
91570
|
-
addonId: null,
|
|
91571
|
-
access: "create",
|
|
91572
|
-
role: "admin"
|
|
91573
|
-
},
|
|
91574
91436
|
"storage.write": {
|
|
91575
91437
|
capName: "storage",
|
|
91576
91438
|
capScope: "system",
|
|
@@ -92348,27 +92210,6 @@ Object.freeze({
|
|
|
92348
92210
|
access: "create",
|
|
92349
92211
|
role: "admin"
|
|
92350
92212
|
},
|
|
92351
|
-
"terminalSession.createInstance": {
|
|
92352
|
-
capName: "terminal-session",
|
|
92353
|
-
capScope: "system",
|
|
92354
|
-
addonId: null,
|
|
92355
|
-
access: "create",
|
|
92356
|
-
role: "admin"
|
|
92357
|
-
},
|
|
92358
|
-
"terminalSession.deleteInstance": {
|
|
92359
|
-
capName: "terminal-session",
|
|
92360
|
-
capScope: "system",
|
|
92361
|
-
addonId: null,
|
|
92362
|
-
access: "delete",
|
|
92363
|
-
role: "admin"
|
|
92364
|
-
},
|
|
92365
|
-
"terminalSession.listInstances": {
|
|
92366
|
-
capName: "terminal-session",
|
|
92367
|
-
capScope: "system",
|
|
92368
|
-
addonId: null,
|
|
92369
|
-
access: "view",
|
|
92370
|
-
role: "admin"
|
|
92371
|
-
},
|
|
92372
92213
|
"terminalSession.listLegacyCameras": {
|
|
92373
92214
|
capName: "terminal-session",
|
|
92374
92215
|
capScope: "system",
|
|
@@ -92411,20 +92252,6 @@ Object.freeze({
|
|
|
92411
92252
|
access: "create",
|
|
92412
92253
|
role: "admin"
|
|
92413
92254
|
},
|
|
92414
|
-
"terminalSession.setInstanceEnabled": {
|
|
92415
|
-
capName: "terminal-session",
|
|
92416
|
-
capScope: "system",
|
|
92417
|
-
addonId: null,
|
|
92418
|
-
access: "create",
|
|
92419
|
-
role: "admin"
|
|
92420
|
-
},
|
|
92421
|
-
"terminalSession.updateInstance": {
|
|
92422
|
-
capName: "terminal-session",
|
|
92423
|
-
capScope: "system",
|
|
92424
|
-
addonId: null,
|
|
92425
|
-
access: "create",
|
|
92426
|
-
role: "admin"
|
|
92427
|
-
},
|
|
92428
92255
|
"terminalSession.writeInput": {
|
|
92429
92256
|
capName: "terminal-session",
|
|
92430
92257
|
capScope: "system",
|
|
@@ -92439,6 +92266,13 @@ Object.freeze({
|
|
|
92439
92266
|
access: "create",
|
|
92440
92267
|
role: "viewer"
|
|
92441
92268
|
},
|
|
92269
|
+
"timedRun.getOptions": {
|
|
92270
|
+
capName: "timed-run",
|
|
92271
|
+
capScope: "device",
|
|
92272
|
+
addonId: null,
|
|
92273
|
+
access: "view",
|
|
92274
|
+
role: "viewer"
|
|
92275
|
+
},
|
|
92442
92276
|
"timedRun.getStatus": {
|
|
92443
92277
|
capName: "timed-run",
|
|
92444
92278
|
capScope: "device",
|
|
@@ -92453,6 +92287,13 @@ Object.freeze({
|
|
|
92453
92287
|
access: "create",
|
|
92454
92288
|
role: "viewer"
|
|
92455
92289
|
},
|
|
92290
|
+
"timedRun.setSettings": {
|
|
92291
|
+
capName: "timed-run",
|
|
92292
|
+
capScope: "device",
|
|
92293
|
+
addonId: null,
|
|
92294
|
+
access: "create",
|
|
92295
|
+
role: "admin"
|
|
92296
|
+
},
|
|
92456
92297
|
"toast.onToast": {
|
|
92457
92298
|
capName: "toast",
|
|
92458
92299
|
capScope: "system",
|
|
@@ -92565,20 +92406,6 @@ Object.freeze({
|
|
|
92565
92406
|
access: "create",
|
|
92566
92407
|
role: "admin"
|
|
92567
92408
|
},
|
|
92568
|
-
"trainingProvider.createEntry": {
|
|
92569
|
-
capName: "training-provider",
|
|
92570
|
-
capScope: "system",
|
|
92571
|
-
addonId: null,
|
|
92572
|
-
access: "create",
|
|
92573
|
-
role: "admin"
|
|
92574
|
-
},
|
|
92575
|
-
"trainingProvider.deleteEntry": {
|
|
92576
|
-
capName: "training-provider",
|
|
92577
|
-
capScope: "system",
|
|
92578
|
-
addonId: null,
|
|
92579
|
-
access: "delete",
|
|
92580
|
-
role: "admin"
|
|
92581
|
-
},
|
|
92582
92409
|
"trainingProvider.describe": {
|
|
92583
92410
|
capName: "training-provider",
|
|
92584
92411
|
capScope: "system",
|
|
@@ -92649,13 +92476,6 @@ Object.freeze({
|
|
|
92649
92476
|
access: "create",
|
|
92650
92477
|
role: "admin"
|
|
92651
92478
|
},
|
|
92652
|
-
"trainingProvider.updateEntry": {
|
|
92653
|
-
capName: "training-provider",
|
|
92654
|
-
capScope: "system",
|
|
92655
|
-
addonId: null,
|
|
92656
|
-
access: "create",
|
|
92657
|
-
role: "admin"
|
|
92658
|
-
},
|
|
92659
92479
|
"trainingProvider.uploadDataset": {
|
|
92660
92480
|
capName: "training-provider",
|
|
92661
92481
|
capScope: "system",
|
|
@@ -92663,13 +92483,6 @@ Object.freeze({
|
|
|
92663
92483
|
access: "create",
|
|
92664
92484
|
role: "admin"
|
|
92665
92485
|
},
|
|
92666
|
-
"trainingProvider.validateCredentials": {
|
|
92667
|
-
capName: "training-provider",
|
|
92668
|
-
capScope: "system",
|
|
92669
|
-
addonId: null,
|
|
92670
|
-
access: "create",
|
|
92671
|
-
role: "admin"
|
|
92672
|
-
},
|
|
92673
92486
|
"turnProvider.getTurnServers": {
|
|
92674
92487
|
capName: "turn-provider",
|
|
92675
92488
|
capScope: "system",
|
|
@@ -93411,6 +93224,7 @@ function offered(received) {
|
|
|
93411
93224
|
};
|
|
93412
93225
|
}
|
|
93413
93226
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
93227
|
+
string().min(1).max(4096);
|
|
93414
93228
|
Object.freeze({
|
|
93415
93229
|
"accessories.getStatus": [{
|
|
93416
93230
|
name: "deviceId",
|
|
@@ -94094,6 +93908,11 @@ Object.freeze({
|
|
|
94094
93908
|
form: "single",
|
|
94095
93909
|
optional: false
|
|
94096
93910
|
}],
|
|
93911
|
+
"deviceManager.setAlias": [{
|
|
93912
|
+
name: "deviceId",
|
|
93913
|
+
form: "single",
|
|
93914
|
+
optional: false
|
|
93915
|
+
}],
|
|
94097
93916
|
"deviceManager.setChildLayout": [{
|
|
94098
93917
|
name: "deviceId",
|
|
94099
93918
|
form: "single",
|
|
@@ -96158,6 +95977,11 @@ Object.freeze({
|
|
|
96158
95977
|
form: "single",
|
|
96159
95978
|
optional: false
|
|
96160
95979
|
}],
|
|
95980
|
+
"timedRun.getOptions": [{
|
|
95981
|
+
name: "deviceId",
|
|
95982
|
+
form: "single",
|
|
95983
|
+
optional: false
|
|
95984
|
+
}],
|
|
96161
95985
|
"timedRun.getStatus": [{
|
|
96162
95986
|
name: "deviceId",
|
|
96163
95987
|
form: "single",
|
|
@@ -96168,6 +95992,11 @@ Object.freeze({
|
|
|
96168
95992
|
form: "single",
|
|
96169
95993
|
optional: false
|
|
96170
95994
|
}],
|
|
95995
|
+
"timedRun.setSettings": [{
|
|
95996
|
+
name: "deviceId",
|
|
95997
|
+
form: "single",
|
|
95998
|
+
optional: false
|
|
95999
|
+
}],
|
|
96171
96000
|
"update.getStatus": [{
|
|
96172
96001
|
name: "deviceId",
|
|
96173
96002
|
form: "single",
|
|
@@ -97030,6 +96859,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
97030
96859
|
[...RULE_TEXT],
|
|
97031
96860
|
[...RULE_TEXT],
|
|
97032
96861
|
[...RULE_TEXT],
|
|
96862
|
+
[...RULE_TEXT],
|
|
97033
96863
|
[...RULE_BODY_ONLY],
|
|
97034
96864
|
[...RULE_TEXT],
|
|
97035
96865
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
@@ -99397,7 +99227,13 @@ function buildEnpalInstances(records, deps) {
|
|
|
99397
99227
|
},
|
|
99398
99228
|
instanceMode: "multiple",
|
|
99399
99229
|
kind: "server",
|
|
99400
|
-
canTest: false
|
|
99230
|
+
canTest: false,
|
|
99231
|
+
kinds: [{
|
|
99232
|
+
id: "connection",
|
|
99233
|
+
label: "Enpal",
|
|
99234
|
+
functionCap: "device-provider",
|
|
99235
|
+
role: "devices"
|
|
99236
|
+
}]
|
|
99401
99237
|
},
|
|
99402
99238
|
formSchema: () => buildConnectionFormSchema(),
|
|
99403
99239
|
initialBody: (name) => ({ name }),
|