@camstack/addon-provider-tuya 0.2.125 → 0.2.126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +1318 -129
- package/dist/addon.mjs +1318 -129
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -819,7 +819,7 @@ async function discoverDevices(options = {}, binder = createDgramBinder()) {
|
|
|
819
819
|
return [...byId.values()];
|
|
820
820
|
}
|
|
821
821
|
//#endregion
|
|
822
|
-
//#region ../types/dist/event-category-
|
|
822
|
+
//#region ../types/dist/event-category-BvOFi6PY.mjs
|
|
823
823
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
824
824
|
EventCategory["SystemBoot"] = "system.boot";
|
|
825
825
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -883,6 +883,24 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
883
883
|
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
884
884
|
*/
|
|
885
885
|
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
886
|
+
/**
|
|
887
|
+
* A runner whose addon refused to start with a typed reason (D877) - parked
|
|
888
|
+
* in `refused`, never counted by the crash breaker, retried on operator
|
|
889
|
+
* action. One event per refusal.
|
|
890
|
+
*/
|
|
891
|
+
EventCategory["AddonRunnerRefused"] = "addon.runner-refused";
|
|
892
|
+
/**
|
|
893
|
+
* A runner settled on a state (D877): `running` (READY), `crashed`, `failed` or
|
|
894
|
+
* `refused`. In-process telemetry for the boot check (re-evaluate while any
|
|
895
|
+
* runner is still pending) and the alert centre (close a recovered runner).
|
|
896
|
+
*/
|
|
897
|
+
EventCategory["ProcessStateChanged"] = "process.state-changed";
|
|
898
|
+
/**
|
|
899
|
+
* The node's one-shot runner boot check (D877): planned runners against the
|
|
900
|
+
* process service, `degraded` when any is failed, refused or missing. The
|
|
901
|
+
* alert centre turns it into one alert and a direct critical push.
|
|
902
|
+
*/
|
|
903
|
+
EventCategory["SystemRunnerCheck"] = "system.runner-check";
|
|
886
904
|
EventCategory["AddonError"] = "addon.error";
|
|
887
905
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
888
906
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -6207,7 +6225,7 @@ var ZodIssueCode = {
|
|
|
6207
6225
|
var ZodFirstPartyTypeKind;
|
|
6208
6226
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6209
6227
|
//#endregion
|
|
6210
|
-
//#region ../types/dist/sleep-
|
|
6228
|
+
//#region ../types/dist/sleep-BOjuyoPC.mjs
|
|
6211
6229
|
/**
|
|
6212
6230
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6213
6231
|
* window to float samples (D455).
|
|
@@ -6324,7 +6342,7 @@ function isKeychainStoredField(field) {
|
|
|
6324
6342
|
var SECRET_CLEAR = { $secretClear: true };
|
|
6325
6343
|
/** The `'***'` token D634 redaction writes for non-admins. */
|
|
6326
6344
|
var LEGACY_DEVICE_REDACTION = "***";
|
|
6327
|
-
function isRecord$
|
|
6345
|
+
function isRecord$5(value) {
|
|
6328
6346
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6329
6347
|
}
|
|
6330
6348
|
function makeSecretMarker(ref) {
|
|
@@ -6335,12 +6353,12 @@ function makeSecretMarker(ref) {
|
|
|
6335
6353
|
}
|
|
6336
6354
|
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
6337
6355
|
function isSecretMarker(value) {
|
|
6338
|
-
if (!isRecord$
|
|
6356
|
+
if (!isRecord$5(value)) return false;
|
|
6339
6357
|
const ref = value["$secret"];
|
|
6340
6358
|
return typeof ref === "string" && ref.length > 0;
|
|
6341
6359
|
}
|
|
6342
6360
|
function isSecretClear(value) {
|
|
6343
|
-
return isRecord$
|
|
6361
|
+
return isRecord$5(value) && value["$secretClear"] === true;
|
|
6344
6362
|
}
|
|
6345
6363
|
/**
|
|
6346
6364
|
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
@@ -6950,7 +6968,7 @@ var KeychainWriteRefusedError = class extends Error {
|
|
|
6950
6968
|
}
|
|
6951
6969
|
};
|
|
6952
6970
|
var KEEP = { kind: "keep" };
|
|
6953
|
-
function isRecord$
|
|
6971
|
+
function isRecord$4(value) {
|
|
6954
6972
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6955
6973
|
}
|
|
6956
6974
|
function defaultMintId() {
|
|
@@ -6963,7 +6981,7 @@ function collectSecretMarkerRefs(value, out = []) {
|
|
|
6963
6981
|
return out;
|
|
6964
6982
|
}
|
|
6965
6983
|
if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
|
|
6966
|
-
else if (isRecord$
|
|
6984
|
+
else if (isRecord$4(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
6967
6985
|
return out;
|
|
6968
6986
|
}
|
|
6969
6987
|
async function storedValueEquals(secrets, marker, candidate) {
|
|
@@ -7018,7 +7036,7 @@ function rowId(row, idKey) {
|
|
|
7018
7036
|
return null;
|
|
7019
7037
|
}
|
|
7020
7038
|
async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
7021
|
-
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$
|
|
7039
|
+
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$4) : [];
|
|
7022
7040
|
if (!Array.isArray(incoming)) {
|
|
7023
7041
|
ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
|
|
7024
7042
|
return {
|
|
@@ -7034,7 +7052,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
|
7034
7052
|
const kept = /* @__PURE__ */ new Set();
|
|
7035
7053
|
const rows = [];
|
|
7036
7054
|
for (const row of incoming) {
|
|
7037
|
-
if (!isRecord$
|
|
7055
|
+
if (!isRecord$4(row)) {
|
|
7038
7056
|
rows.push(row);
|
|
7039
7057
|
continue;
|
|
7040
7058
|
}
|
|
@@ -7059,7 +7077,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
7059
7077
|
case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
|
|
7060
7078
|
case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
|
|
7061
7079
|
case "object": {
|
|
7062
|
-
if (!isRecord$
|
|
7080
|
+
if (!isRecord$4(incoming)) {
|
|
7063
7081
|
if (incoming === void 0) return KEEP;
|
|
7064
7082
|
ctx.deletes.push(...collectSecretMarkerRefs(stored));
|
|
7065
7083
|
return {
|
|
@@ -7067,7 +7085,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
7067
7085
|
value: incoming
|
|
7068
7086
|
};
|
|
7069
7087
|
}
|
|
7070
|
-
const storedObj = isRecord$
|
|
7088
|
+
const storedObj = isRecord$4(stored) ? stored : {};
|
|
7071
7089
|
return {
|
|
7072
7090
|
kind: "value",
|
|
7073
7091
|
value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
|
|
@@ -7190,7 +7208,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
|
|
|
7190
7208
|
value: items
|
|
7191
7209
|
};
|
|
7192
7210
|
}
|
|
7193
|
-
if (isRecord$
|
|
7211
|
+
if (isRecord$4(v)) {
|
|
7194
7212
|
const out = {};
|
|
7195
7213
|
for (const [k, child] of Object.entries(v)) {
|
|
7196
7214
|
const s = substitute(child);
|
|
@@ -7222,7 +7240,7 @@ var SecretOverwriteRefusedError = class extends Error {
|
|
|
7222
7240
|
this.name = "SecretOverwriteRefusedError";
|
|
7223
7241
|
}
|
|
7224
7242
|
};
|
|
7225
|
-
function isRecord(value) {
|
|
7243
|
+
function isRecord$3(value) {
|
|
7226
7244
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7227
7245
|
}
|
|
7228
7246
|
function resolvedValue(pass, marker) {
|
|
@@ -7251,7 +7269,7 @@ function inArray(pass, incoming, stored) {
|
|
|
7251
7269
|
return incoming.map((item, index) => {
|
|
7252
7270
|
const twin = stored[index];
|
|
7253
7271
|
if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
|
|
7254
|
-
if (isRecord(item) && isRecord(twin)) return inArrayRow(pass, item, twin);
|
|
7272
|
+
if (isRecord$3(item) && isRecord$3(twin)) return inArrayRow(pass, item, twin);
|
|
7255
7273
|
if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
|
|
7256
7274
|
return item;
|
|
7257
7275
|
});
|
|
@@ -7262,7 +7280,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
7262
7280
|
const twin = stored[key];
|
|
7263
7281
|
if (isSecretMarker(twin)) {
|
|
7264
7282
|
if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
|
|
7265
|
-
} else if (isRecord(value) && isRecord(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
7283
|
+
} else if (isRecord$3(value) && isRecord$3(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
7266
7284
|
else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
|
|
7267
7285
|
}
|
|
7268
7286
|
return out;
|
|
@@ -7270,7 +7288,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
7270
7288
|
async function walk(pass, incoming, stored) {
|
|
7271
7289
|
if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
|
|
7272
7290
|
if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
|
|
7273
|
-
if (!isRecord(incoming) || !isRecord(stored)) return incoming;
|
|
7291
|
+
if (!isRecord$3(incoming) || !isRecord$3(stored)) return incoming;
|
|
7274
7292
|
const out = { ...incoming };
|
|
7275
7293
|
for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
|
|
7276
7294
|
return out;
|
|
@@ -7301,7 +7319,7 @@ async function restoreUnchangedSecrets(input) {
|
|
|
7301
7319
|
function isJsonEqual(a, b) {
|
|
7302
7320
|
if (Object.is(a, b)) return true;
|
|
7303
7321
|
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
|
|
7304
|
-
if (isRecord(a) && isRecord(b)) {
|
|
7322
|
+
if (isRecord$3(a) && isRecord$3(b)) {
|
|
7305
7323
|
const keys = Object.keys(a);
|
|
7306
7324
|
if (keys.length !== Object.keys(b).length) return false;
|
|
7307
7325
|
return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
|
|
@@ -7325,7 +7343,7 @@ function markerKeysOf(blob, owner) {
|
|
|
7325
7343
|
}
|
|
7326
7344
|
function nodeFromStored(value, owner) {
|
|
7327
7345
|
if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
|
|
7328
|
-
if (!isRecord(value)) return null;
|
|
7346
|
+
if (!isRecord$3(value)) return null;
|
|
7329
7347
|
const children = /* @__PURE__ */ new Map();
|
|
7330
7348
|
for (const [key, child] of Object.entries(value)) {
|
|
7331
7349
|
const node = nodeFromStored(child, owner);
|
|
@@ -7458,7 +7476,6 @@ function createHandle(raw, opts) {
|
|
|
7458
7476
|
const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
|
|
7459
7477
|
const readAddonStoreResult = raw.readAddonStoreResult;
|
|
7460
7478
|
const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
|
|
7461
|
-
const readDeviceStoreBatch = raw.readDeviceStoreBatch;
|
|
7462
7479
|
const overrides = {
|
|
7463
7480
|
readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
|
|
7464
7481
|
...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
|
|
@@ -7482,12 +7499,12 @@ function createHandle(raw, opts) {
|
|
|
7482
7499
|
};
|
|
7483
7500
|
} } : {},
|
|
7484
7501
|
readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
|
|
7485
|
-
|
|
7486
|
-
const rows = await readDeviceStoreBatch
|
|
7502
|
+
readDeviceStoreBatch: async (deviceIds) => {
|
|
7503
|
+
const rows = await raw.readDeviceStoreBatch(deviceIds);
|
|
7487
7504
|
const out = /* @__PURE__ */ new Map();
|
|
7488
7505
|
for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
|
|
7489
7506
|
return out;
|
|
7490
|
-
}
|
|
7507
|
+
},
|
|
7491
7508
|
writeAddonStore: async (patch) => {
|
|
7492
7509
|
await writeAddon(patch);
|
|
7493
7510
|
},
|
|
@@ -8114,7 +8131,7 @@ var BaseAddon = class {
|
|
|
8114
8131
|
if (!ctx) return;
|
|
8115
8132
|
const addonId = ctx.id;
|
|
8116
8133
|
setImmediate(() => {
|
|
8117
|
-
ctx.api.addons
|
|
8134
|
+
ctx.api.addons.restartAddon.mutate({ addonId }).then(() => {
|
|
8118
8135
|
ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: patchKeys.filter((k) => restartKeys.has(k)) } });
|
|
8119
8136
|
}).catch((err) => {
|
|
8120
8137
|
ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
@@ -8314,7 +8331,7 @@ var BaseAddon = class {
|
|
|
8314
8331
|
return this._addonSecretTree;
|
|
8315
8332
|
}
|
|
8316
8333
|
deviceSecretTree() {
|
|
8317
|
-
this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
|
|
8334
|
+
this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
|
|
8318
8335
|
return this._deviceSecretTree;
|
|
8319
8336
|
}
|
|
8320
8337
|
/**
|
|
@@ -8390,42 +8407,135 @@ var BaseAddon = class {
|
|
|
8390
8407
|
this[timerOf] = timer;
|
|
8391
8408
|
}
|
|
8392
8409
|
/**
|
|
8393
|
-
* Wrap `ctx.settings.readAddonStore()` with a
|
|
8394
|
-
*
|
|
8395
|
-
* swap, or any race where the SqliteSettingsBackend is between shutdown and
|
|
8396
|
-
* re-initialize) doesn't propagate up into `initialize()` and leave the
|
|
8410
|
+
* Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
|
|
8411
|
+
* settings-store outage doesn't propagate up into `initialize()` and leave the
|
|
8397
8412
|
* addon permanently broken.
|
|
8398
8413
|
*
|
|
8399
|
-
*
|
|
8400
|
-
*
|
|
8401
|
-
* `
|
|
8414
|
+
* Two kinds of outage, two budgets:
|
|
8415
|
+
* - the store's own restart fingerprints (`SqliteSettingsBackend not
|
|
8416
|
+
* initialized`, `provider not available`): a short budget, then the addon
|
|
8417
|
+
* falls back to `{}` (defaults) - its first successful patch rehydrates;
|
|
8418
|
+
* - a TRANSPORT failure (the hub is restarting while this runner boots:
|
|
8419
|
+
* `discovery deadline exceeded`, `parent could not route`, a refused or
|
|
8420
|
+
* timed-out connection). Since D870 a runner's settings read THROWS instead
|
|
8421
|
+
* of answering `{}`, so without a budget a hub restart ends in a respawn
|
|
8422
|
+
* loop. A long, bounded budget; only after it the error propagates - the
|
|
8423
|
+
* addon never runs on defaults for a store it could not ask.
|
|
8424
|
+
* Anything else propagates immediately so real bugs surface.
|
|
8402
8425
|
*/
|
|
8403
8426
|
async readAddonStoreWithRetry() {
|
|
8404
8427
|
const settings = this.rawSettings();
|
|
8405
8428
|
if (!settings) return {};
|
|
8406
|
-
const delaysMs = [
|
|
8407
|
-
150,
|
|
8408
|
-
350,
|
|
8409
|
-
600,
|
|
8410
|
-
900
|
|
8411
|
-
];
|
|
8412
8429
|
let lastErr;
|
|
8413
|
-
for (let attempt = 0
|
|
8430
|
+
for (let attempt = 0;; attempt++) try {
|
|
8414
8431
|
const stored = await settings.readAddonStore() ?? {};
|
|
8415
8432
|
this.settingsStoreReady = true;
|
|
8416
8433
|
return stored;
|
|
8417
8434
|
} catch (err) {
|
|
8418
8435
|
lastErr = err;
|
|
8419
|
-
const
|
|
8420
|
-
if (
|
|
8421
|
-
|
|
8422
|
-
|
|
8436
|
+
const kind = classifyStoreError(err);
|
|
8437
|
+
if (kind === "permanent") throw err;
|
|
8438
|
+
const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
|
|
8439
|
+
if (delay === void 0) {
|
|
8440
|
+
if (kind === "transport") throw err;
|
|
8441
|
+
break;
|
|
8442
|
+
}
|
|
8443
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
8423
8444
|
}
|
|
8424
8445
|
this.settingsStoreReady = false;
|
|
8425
8446
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
8426
8447
|
return {};
|
|
8427
8448
|
}
|
|
8428
8449
|
};
|
|
8450
|
+
/** The settings store's own restart fingerprints. */
|
|
8451
|
+
var RESTART_RETRY_DELAYS_MS = [
|
|
8452
|
+
150,
|
|
8453
|
+
350,
|
|
8454
|
+
600,
|
|
8455
|
+
900
|
|
8456
|
+
];
|
|
8457
|
+
/** A hub restarting under a booting runner: ~100 s in total. */
|
|
8458
|
+
var TRANSPORT_RETRY_DELAYS_MS = [
|
|
8459
|
+
150,
|
|
8460
|
+
350,
|
|
8461
|
+
600,
|
|
8462
|
+
900,
|
|
8463
|
+
2e3,
|
|
8464
|
+
5e3,
|
|
8465
|
+
1e4,
|
|
8466
|
+
2e4,
|
|
8467
|
+
3e4,
|
|
8468
|
+
3e4
|
|
8469
|
+
];
|
|
8470
|
+
var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
|
|
8471
|
+
/** Node / socket error codes of a connection that did not happen or broke. */
|
|
8472
|
+
var TRANSPORT_CODES = new Set([
|
|
8473
|
+
"ECONNREFUSED",
|
|
8474
|
+
"ECONNRESET",
|
|
8475
|
+
"ETIMEDOUT",
|
|
8476
|
+
"EPIPE",
|
|
8477
|
+
"EHOSTUNREACH",
|
|
8478
|
+
"ENETUNREACH"
|
|
8479
|
+
]);
|
|
8480
|
+
/** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
|
|
8481
|
+
var UNAVAILABLE_TRPC_CODES = new Set([
|
|
8482
|
+
"TIMEOUT",
|
|
8483
|
+
"SERVICE_UNAVAILABLE",
|
|
8484
|
+
"BAD_GATEWAY",
|
|
8485
|
+
"GATEWAY_TIMEOUT"
|
|
8486
|
+
]);
|
|
8487
|
+
/** The exact phrases OUR transport raises when it cannot reach the hub. */
|
|
8488
|
+
var TRANSPORT_PHRASES = [
|
|
8489
|
+
"discovery deadline exceeded",
|
|
8490
|
+
"parent could not route child unowned cap call",
|
|
8491
|
+
"UDS request timed out after",
|
|
8492
|
+
"channel closed"
|
|
8493
|
+
];
|
|
8494
|
+
/** Moleculer error `type`s of a call that could not be served right now. */
|
|
8495
|
+
var MOLECULER_TRANSIENT_TYPES = new Set([
|
|
8496
|
+
"REQUEST_TIMEOUT",
|
|
8497
|
+
"SERVICE_NOT_FOUND",
|
|
8498
|
+
"SERVICE_NOT_AVAILABLE",
|
|
8499
|
+
"REQUEST_REJECTED",
|
|
8500
|
+
"BROKER_DISCONNECTED"
|
|
8501
|
+
]);
|
|
8502
|
+
function stringProp(value, key) {
|
|
8503
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
8504
|
+
const found = Reflect.get(value, key);
|
|
8505
|
+
return typeof found === "string" ? found : void 0;
|
|
8506
|
+
}
|
|
8507
|
+
/** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
|
|
8508
|
+
function errorCode(err) {
|
|
8509
|
+
const out = {};
|
|
8510
|
+
const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
|
|
8511
|
+
const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
|
|
8512
|
+
const node = stringProp(err, "code") ?? stringProp(cause, "code");
|
|
8513
|
+
if (node !== void 0) out.node = node;
|
|
8514
|
+
const trpc = stringProp(data, "code");
|
|
8515
|
+
if (trpc !== void 0) out.trpc = trpc;
|
|
8516
|
+
return out;
|
|
8517
|
+
}
|
|
8518
|
+
/**
|
|
8519
|
+
* Is a settings read failure worth retrying, and with which budget? Only
|
|
8520
|
+
* EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
|
|
8521
|
+
* one of the exact phrases our own transport raises. A message that merely
|
|
8522
|
+
* contains "timed out" or "unreachable" is not evidence of anything, and
|
|
8523
|
+
* `settings store not wired` is a wiring bug that retrying cannot fix.
|
|
8524
|
+
*/
|
|
8525
|
+
function classifyStoreError(err) {
|
|
8526
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8527
|
+
if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
|
|
8528
|
+
const code = errorCode(err);
|
|
8529
|
+
if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
|
|
8530
|
+
if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
|
|
8531
|
+
if (typeof err === "object" && err !== null) {
|
|
8532
|
+
if (Reflect.get(err, "retryable") === true) return "transport";
|
|
8533
|
+
const type = stringProp(err, "type");
|
|
8534
|
+
if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
|
|
8535
|
+
}
|
|
8536
|
+
if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
|
|
8537
|
+
return "permanent";
|
|
8538
|
+
}
|
|
8429
8539
|
/**
|
|
8430
8540
|
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
8431
8541
|
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
@@ -8515,6 +8625,156 @@ var PeerBytesTicketSchema = object({
|
|
|
8515
8625
|
*/
|
|
8516
8626
|
declaredBytes: number().int().nonnegative().nullable()
|
|
8517
8627
|
});
|
|
8628
|
+
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
8629
|
+
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
8630
|
+
var RECORD_DATABASES = ["settings", "events"];
|
|
8631
|
+
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
8632
|
+
var RECORD_CLASSES = [
|
|
8633
|
+
"config",
|
|
8634
|
+
"ledger",
|
|
8635
|
+
"audit",
|
|
8636
|
+
"mirror",
|
|
8637
|
+
"ephemeral"
|
|
8638
|
+
];
|
|
8639
|
+
var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
|
|
8640
|
+
var RECORD_MAX_DATA_BYTES = 1024 * 1024;
|
|
8641
|
+
var RECORD_BATCH_MAX = 1e3;
|
|
8642
|
+
var RECORD_QUERY_MAX = 1e3;
|
|
8643
|
+
function defOf(schema) {
|
|
8644
|
+
const zodBag = Reflect.get(schema, "_zod");
|
|
8645
|
+
if (typeof zodBag !== "object" || zodBag === null) return {};
|
|
8646
|
+
const def = Reflect.get(zodBag, "def");
|
|
8647
|
+
if (typeof def !== "object" || def === null) return {};
|
|
8648
|
+
return def;
|
|
8649
|
+
}
|
|
8650
|
+
function declaredHere(schema) {
|
|
8651
|
+
return globalRegistry.get(schema)?.secret === true;
|
|
8652
|
+
}
|
|
8653
|
+
/** Wrapper kinds whose `innerType` is the field itself. */
|
|
8654
|
+
var WRAPPERS = new Set([
|
|
8655
|
+
"optional",
|
|
8656
|
+
"nullable",
|
|
8657
|
+
"default",
|
|
8658
|
+
"prefault",
|
|
8659
|
+
"catch",
|
|
8660
|
+
"readonly",
|
|
8661
|
+
"nonoptional"
|
|
8662
|
+
]);
|
|
8663
|
+
/**
|
|
8664
|
+
* The schema layers that make up ONE field: the schema itself and every
|
|
8665
|
+
* wrapper below it (optional / default / nullable / readonly / catch), plus
|
|
8666
|
+
* both sides of a pipe (`z.preprocess` puts the real schema on `out`,
|
|
8667
|
+
* `.transform` on `in`).
|
|
8668
|
+
*/
|
|
8669
|
+
function layersOf(schema) {
|
|
8670
|
+
const out = [];
|
|
8671
|
+
const queue = [schema];
|
|
8672
|
+
while (queue.length > 0) {
|
|
8673
|
+
const s = queue.shift();
|
|
8674
|
+
if (!s || out.includes(s)) continue;
|
|
8675
|
+
out.push(s);
|
|
8676
|
+
const def = defOf(s);
|
|
8677
|
+
if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
|
|
8678
|
+
if (def.type === "pipe") {
|
|
8679
|
+
if (def.in) queue.push(def.in);
|
|
8680
|
+
if (def.out) queue.push(def.out);
|
|
8681
|
+
}
|
|
8682
|
+
}
|
|
8683
|
+
return out;
|
|
8684
|
+
}
|
|
8685
|
+
/** True when any layer of this field declares `.meta({ secret: true })`. */
|
|
8686
|
+
function isZodSecret(schema) {
|
|
8687
|
+
return layersOf(schema).some(declaredHere);
|
|
8688
|
+
}
|
|
8689
|
+
/**
|
|
8690
|
+
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
8691
|
+
* element of this array" (`connection.password`, `rows[].token`). A declared
|
|
8692
|
+
* object or array is reported as one path and not descended into — everything
|
|
8693
|
+
* under it is secret.
|
|
8694
|
+
*/
|
|
8695
|
+
function collectZodSecretPaths(schema, prefix = "") {
|
|
8696
|
+
if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
|
|
8697
|
+
const paths = [];
|
|
8698
|
+
for (const layer of layersOf(schema)) {
|
|
8699
|
+
const def = defOf(layer);
|
|
8700
|
+
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
8701
|
+
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
8702
|
+
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
8703
|
+
else if (def.type === "intersection") {
|
|
8704
|
+
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
8705
|
+
if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
return [...new Set(paths)];
|
|
8709
|
+
}
|
|
8710
|
+
/**
|
|
8711
|
+
* Record kinds (D881): the declaration an addon makes of one kind of fact, the
|
|
8712
|
+
* Zod row schema that describes it, and the helper that turns the two into the
|
|
8713
|
+
* JSON the engine stores.
|
|
8714
|
+
*
|
|
8715
|
+
* A kind is declared ONCE, next to the code that owns it, and the engine's
|
|
8716
|
+
* reconciler creates its indexes - nothing creates DDL at runtime from a write.
|
|
8717
|
+
* Everything the generic machinery needs (secret fields, the entry descriptor)
|
|
8718
|
+
* is DERIVED from the Zod schema: `.meta({ secret: true })` marks a credential,
|
|
8719
|
+
* never a field name (D879).
|
|
8720
|
+
*/
|
|
8721
|
+
/** A JSON field path: `trackId`, `auth.accessToken`. Charset-restricted: it is rendered into DDL. */
|
|
8722
|
+
var RecordFieldPathSchema = string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}(\.[A-Za-z_][A-Za-z0-9_]{0,63}){0,3}$/);
|
|
8723
|
+
var RecordIndexSchema = object({
|
|
8724
|
+
name: string().regex(/^[a-z][a-z0-9_]{0,31}$/),
|
|
8725
|
+
fields: array(RecordFieldPathSchema).min(1).max(4),
|
|
8726
|
+
unique: boolean().optional()
|
|
8727
|
+
}).strict();
|
|
8728
|
+
var RecordRetentionSchema = union([
|
|
8729
|
+
object({
|
|
8730
|
+
ttlMs: number().int().positive(),
|
|
8731
|
+
from: _enum(["at", "write"])
|
|
8732
|
+
}).strict(),
|
|
8733
|
+
object({
|
|
8734
|
+
maxRows: number().int().positive().optional(),
|
|
8735
|
+
maxRowsPerDevice: number().int().positive().optional()
|
|
8736
|
+
}).strict().refine((r) => r.maxRows !== void 0 || r.maxRowsPerDevice !== void 0, { message: "name maxRows, maxRowsPerDevice or both" }),
|
|
8737
|
+
object({
|
|
8738
|
+
owner: string().min(1),
|
|
8739
|
+
reason: string().min(1)
|
|
8740
|
+
}).strict(),
|
|
8741
|
+
object({ none: string().min(1) }).strict()
|
|
8742
|
+
]);
|
|
8743
|
+
/** The descriptor a generic entries UI reads (D881 "generic entries"). */
|
|
8744
|
+
var EntryDescriptorSchema = object({
|
|
8745
|
+
title: string().min(1),
|
|
8746
|
+
/** The record body field shown as the entry's name. */
|
|
8747
|
+
nameField: string().min(1),
|
|
8748
|
+
/** JSON Schema of the entry body; `secret: true` marks a password field. */
|
|
8749
|
+
fields: record(string(), unknown())
|
|
8750
|
+
}).strict();
|
|
8751
|
+
/** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
|
|
8752
|
+
var RecordKindDeclarationSchema = object({
|
|
8753
|
+
database: _enum(RECORD_DATABASES).default("settings"),
|
|
8754
|
+
class: _enum(RECORD_CLASSES),
|
|
8755
|
+
writeTrigger: string().min(1),
|
|
8756
|
+
device: _enum([
|
|
8757
|
+
"required",
|
|
8758
|
+
"optional",
|
|
8759
|
+
"none"
|
|
8760
|
+
]).default("none"),
|
|
8761
|
+
at: _enum(["required", "none"]).default("none"),
|
|
8762
|
+
indexes: array(RecordIndexSchema).max(8).default([]),
|
|
8763
|
+
retention: RecordRetentionSchema,
|
|
8764
|
+
deviceRemoval: _enum(["delete", "none"]).default("none"),
|
|
8765
|
+
maxDataBytes: number().int().positive().max(RECORD_MAX_DATA_BYTES).default(RECORD_DEFAULT_MAX_DATA_BYTES),
|
|
8766
|
+
/** Derived from the Zod schema; never written by hand. */
|
|
8767
|
+
secretPaths: array(string().min(1)).default([]),
|
|
8768
|
+
/** The id key of each array that carries a secret (`{ headers: 'name' }`). */
|
|
8769
|
+
secretArrayKeys: record(string(), string()).default({}),
|
|
8770
|
+
/** The legacy table this kind adopts from; the boot guard reads it (D853: no migration code). */
|
|
8771
|
+
adoptsFrom: string().min(1).optional(),
|
|
8772
|
+
/** Kinds that must agree through `transact` sit in one file under one family name. */
|
|
8773
|
+
transactFamily: string().min(1).optional(),
|
|
8774
|
+
entry: EntryDescriptorSchema.optional()
|
|
8775
|
+
}).strict();
|
|
8776
|
+
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
8777
|
+
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8518
8778
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
8519
8779
|
var CamProfileSchema = _enum([
|
|
8520
8780
|
"high",
|
|
@@ -8979,75 +9239,12 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
|
|
|
8979
9239
|
kind: "mutation",
|
|
8980
9240
|
auth: "admin"
|
|
8981
9241
|
}), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
8982
|
-
function defOf(schema) {
|
|
8983
|
-
return schema._zod?.def ?? {};
|
|
8984
|
-
}
|
|
8985
|
-
function declaredHere(schema) {
|
|
8986
|
-
return globalRegistry.get(schema)?.secret === true;
|
|
8987
|
-
}
|
|
8988
|
-
/** Wrapper kinds whose `innerType` is the field itself. */
|
|
8989
|
-
var WRAPPERS = new Set([
|
|
8990
|
-
"optional",
|
|
8991
|
-
"nullable",
|
|
8992
|
-
"default",
|
|
8993
|
-
"prefault",
|
|
8994
|
-
"catch",
|
|
8995
|
-
"readonly",
|
|
8996
|
-
"nonoptional"
|
|
8997
|
-
]);
|
|
8998
|
-
/**
|
|
8999
|
-
* The schema layers that make up ONE field: the schema itself and every
|
|
9000
|
-
* wrapper below it (optional / default / nullable / readonly / catch), plus
|
|
9001
|
-
* both sides of a pipe (`z.preprocess` puts the real schema on `out`,
|
|
9002
|
-
* `.transform` on `in`).
|
|
9003
|
-
*/
|
|
9004
|
-
function layersOf(schema) {
|
|
9005
|
-
const out = [];
|
|
9006
|
-
const queue = [schema];
|
|
9007
|
-
while (queue.length > 0) {
|
|
9008
|
-
const s = queue.shift();
|
|
9009
|
-
if (!s || out.includes(s)) continue;
|
|
9010
|
-
out.push(s);
|
|
9011
|
-
const def = defOf(s);
|
|
9012
|
-
if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
|
|
9013
|
-
if (def.type === "pipe") {
|
|
9014
|
-
if (def.in) queue.push(def.in);
|
|
9015
|
-
if (def.out) queue.push(def.out);
|
|
9016
|
-
}
|
|
9017
|
-
}
|
|
9018
|
-
return out;
|
|
9019
|
-
}
|
|
9020
|
-
/** True when any layer of this field declares `.meta({ secret: true })`. */
|
|
9021
|
-
function isZodSecret(schema) {
|
|
9022
|
-
return layersOf(schema).some(declaredHere);
|
|
9023
|
-
}
|
|
9024
|
-
/**
|
|
9025
|
-
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
9026
|
-
* element of this array" (`connection.password`, `rows[].token`). A declared
|
|
9027
|
-
* object or array is reported as one path and not descended into — everything
|
|
9028
|
-
* under it is secret.
|
|
9029
|
-
*/
|
|
9030
|
-
function collectZodSecretPaths(schema, prefix = "") {
|
|
9031
|
-
if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
|
|
9032
|
-
const paths = [];
|
|
9033
|
-
for (const layer of layersOf(schema)) {
|
|
9034
|
-
const def = defOf(layer);
|
|
9035
|
-
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
9036
|
-
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
9037
|
-
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
9038
|
-
else if (def.type === "intersection") {
|
|
9039
|
-
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
9040
|
-
if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
|
|
9041
|
-
}
|
|
9042
|
-
}
|
|
9043
|
-
return [...new Set(paths)];
|
|
9044
|
-
}
|
|
9045
9242
|
object({
|
|
9046
9243
|
code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
|
|
9047
9244
|
params: record(string(), union([string(), number()])).optional()
|
|
9048
9245
|
});
|
|
9049
9246
|
//#endregion
|
|
9050
|
-
//#region ../types/dist/err-msg-
|
|
9247
|
+
//#region ../types/dist/err-msg-BXKzRBwg.mjs
|
|
9051
9248
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
9052
9249
|
DeviceType["Camera"] = "camera";
|
|
9053
9250
|
DeviceType["Hub"] = "hub";
|
|
@@ -9139,6 +9336,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
9139
9336
|
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
9140
9337
|
* integrations sharing the same food/desiccant/hopper surface. */
|
|
9141
9338
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
9339
|
+
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
9340
|
+
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
9341
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations. */
|
|
9342
|
+
DeviceType["Dishwasher"] = "dishwasher";
|
|
9142
9343
|
return DeviceType;
|
|
9143
9344
|
}({});
|
|
9144
9345
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -9316,6 +9517,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
9316
9517
|
/** Virtual HA toggle (input_boolean.*) — distinguishable from a
|
|
9317
9518
|
* real Switch device for UI rendering / export adapters. */
|
|
9318
9519
|
DeviceRole["BinaryHelper"] = "binary-helper";
|
|
9520
|
+
/** A smart plug: a `switch` whose source says it is an outlet (HA
|
|
9521
|
+
* `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
|
|
9522
|
+
DeviceRole["Outlet"] = "outlet";
|
|
9319
9523
|
/** Generic motion / occupancy / moving event source. Distinct from
|
|
9320
9524
|
* the camera accessory PirSensor role: that one is a camera child;
|
|
9321
9525
|
* this is a standalone HA / 3rd-party motion sensor. */
|
|
@@ -9393,7 +9597,7 @@ function errMsg(err) {
|
|
|
9393
9597
|
return String(err);
|
|
9394
9598
|
}
|
|
9395
9599
|
//#endregion
|
|
9396
|
-
//#region ../types/dist/composition-
|
|
9600
|
+
//#region ../types/dist/composition-CB6PMT9p.mjs
|
|
9397
9601
|
/**
|
|
9398
9602
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
9399
9603
|
*
|
|
@@ -12597,11 +12801,10 @@ var ClipModelMetaSchema = object({
|
|
|
12597
12801
|
/**
|
|
12598
12802
|
* The `vector-store` index this model's vectors live in. ONE feature space
|
|
12599
12803
|
* per index: two models share an index only when their vectors are ranked
|
|
12600
|
-
* against each other, which for CLIP means never across families.
|
|
12601
|
-
*
|
|
12602
|
-
* `modelId`); any other model gets its own.
|
|
12804
|
+
* against each other, which for CLIP means never across families. Every
|
|
12805
|
+
* model gets its own.
|
|
12603
12806
|
*/
|
|
12604
|
-
vectorIndex: string().regex(/^object-clip
|
|
12807
|
+
vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
|
|
12605
12808
|
/**
|
|
12606
12809
|
* The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
|
|
12607
12810
|
* must be unique per tokenizer: every model's siblings land in ONE flat
|
|
@@ -13780,8 +13983,13 @@ var AlertSeveritySchema = _enum([
|
|
|
13780
13983
|
"info",
|
|
13781
13984
|
"success",
|
|
13782
13985
|
"warning",
|
|
13783
|
-
"error"
|
|
13986
|
+
"error",
|
|
13987
|
+
"critical"
|
|
13784
13988
|
]);
|
|
13989
|
+
var AlertActionSchema = object({
|
|
13990
|
+
label: string(),
|
|
13991
|
+
href: string().optional()
|
|
13992
|
+
});
|
|
13785
13993
|
var AlertStatusSchema = _enum([
|
|
13786
13994
|
"active",
|
|
13787
13995
|
"in-progress",
|
|
@@ -13799,6 +14007,7 @@ var AlertSchema = object({
|
|
|
13799
14007
|
severity: AlertSeveritySchema,
|
|
13800
14008
|
title: string(),
|
|
13801
14009
|
message: string(),
|
|
14010
|
+
action: AlertActionSchema.optional(),
|
|
13802
14011
|
status: AlertStatusSchema,
|
|
13803
14012
|
progress: number().optional(),
|
|
13804
14013
|
read: boolean(),
|
|
@@ -16334,6 +16543,41 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
|
16334
16543
|
keyField: string()
|
|
16335
16544
|
}).optional()
|
|
16336
16545
|
})) });
|
|
16546
|
+
/**
|
|
16547
|
+
* The report of a one-shot "move this owner's plaintext secrets into the
|
|
16548
|
+
* keychain" run (D878). COUNTS only - never a value, never a ref.
|
|
16549
|
+
*
|
|
16550
|
+
* The owner runs it on its own rows, through its own write path; an operator
|
|
16551
|
+
* script (`scripts/hub/migrate-raw-secrets.ts`) calls the owner's cap method.
|
|
16552
|
+
* Dry run (`apply: false`) reports what WOULD move and changes nothing.
|
|
16553
|
+
*/
|
|
16554
|
+
var RawSecretMigrationInputSchema = object({
|
|
16555
|
+
/** `false` (the default of the script) = report only. */
|
|
16556
|
+
apply: boolean() });
|
|
16557
|
+
var RawSecretMigrationReportSchema = object({
|
|
16558
|
+
apply: boolean(),
|
|
16559
|
+
/** Rows that can carry a secret (a profile, an enrolment). */
|
|
16560
|
+
rows: number().int(),
|
|
16561
|
+
/** Rows whose secret was plaintext when the run started. */
|
|
16562
|
+
raw: number().int(),
|
|
16563
|
+
/** Rows that already held a keychain marker when the run started. */
|
|
16564
|
+
markers: number().int(),
|
|
16565
|
+
/** Plaintext rows moved by THIS run (always 0 on a dry run). */
|
|
16566
|
+
migrated: number().int(),
|
|
16567
|
+
/** Plaintext rows left in place because the move or its read-back failed. */
|
|
16568
|
+
failed: number().int(),
|
|
16569
|
+
/** After the run: rows still holding plaintext. */
|
|
16570
|
+
remainingRaw: number().int(),
|
|
16571
|
+
/** After the run: markers the keychain could not resolve right now. */
|
|
16572
|
+
unresolvedMarkers: number().int(),
|
|
16573
|
+
/**
|
|
16574
|
+
* A second, redundant copy of the secrets' rows that the run found (and, on
|
|
16575
|
+
* `apply`, deleted after verifying the first). Owners with a single copy omit it.
|
|
16576
|
+
*/
|
|
16577
|
+
redundantCopies: number().int().optional(),
|
|
16578
|
+
/** True when nothing is raw and every marker resolves. */
|
|
16579
|
+
verified: boolean()
|
|
16580
|
+
});
|
|
16337
16581
|
/** Where a block runs. The operator chooses — a block driving a device on an
|
|
16338
16582
|
* agent is the reason placement is not fixed to the hub. */
|
|
16339
16583
|
var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
|
|
@@ -16560,6 +16804,9 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
16560
16804
|
}), SettingsUpdateResultSchema, {
|
|
16561
16805
|
kind: "mutation",
|
|
16562
16806
|
auth: "admin"
|
|
16807
|
+
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
16808
|
+
kind: "mutation",
|
|
16809
|
+
auth: "admin"
|
|
16563
16810
|
}), method(object({}), object({ libs: array(object({
|
|
16564
16811
|
filePath: string(),
|
|
16565
16812
|
content: string()
|
|
@@ -16823,6 +17070,347 @@ id: union([string(), number()]) }), {
|
|
|
16823
17070
|
auth: "admin"
|
|
16824
17071
|
});
|
|
16825
17072
|
/**
|
|
17073
|
+
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
17074
|
+
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
17075
|
+
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
17076
|
+
*
|
|
17077
|
+
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
17078
|
+
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
17079
|
+
* that with the addon id closed over). It is deliberately NOT in any input
|
|
17080
|
+
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
17081
|
+
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
17082
|
+
* addons go through the owner's capability.
|
|
17083
|
+
*
|
|
17084
|
+
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
17085
|
+
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
17086
|
+
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
17087
|
+
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
17088
|
+
* row's own ref.
|
|
17089
|
+
*
|
|
17090
|
+
* Internal: addons reach it through `ctx.records`, never directly.
|
|
17091
|
+
*/
|
|
17092
|
+
var RecordIdSchema = string().min(1).max(256);
|
|
17093
|
+
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
17094
|
+
var RecordBodySchema = record(string(), unknown());
|
|
17095
|
+
var RecordInputSchema = object({
|
|
17096
|
+
id: RecordIdSchema,
|
|
17097
|
+
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
17098
|
+
deviceId: number().int().optional(),
|
|
17099
|
+
/** The domain clock, ms: observed / created / opened. */
|
|
17100
|
+
at: number().int().optional(),
|
|
17101
|
+
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
17102
|
+
expiresAt: number().int().optional(),
|
|
17103
|
+
data: RecordBodySchema
|
|
17104
|
+
});
|
|
17105
|
+
var RecordViewSchema = object({
|
|
17106
|
+
id: string(),
|
|
17107
|
+
deviceId: number().int().nullable(),
|
|
17108
|
+
at: number().nullable(),
|
|
17109
|
+
expiresAt: number().nullable(),
|
|
17110
|
+
rev: number().int(),
|
|
17111
|
+
updatedAt: number(),
|
|
17112
|
+
data: RecordBodySchema
|
|
17113
|
+
});
|
|
17114
|
+
/** Declared index fields only. Anything else does not compile, by design. */
|
|
17115
|
+
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
17116
|
+
string(),
|
|
17117
|
+
number(),
|
|
17118
|
+
boolean(),
|
|
17119
|
+
object({ in: array(union([
|
|
17120
|
+
string(),
|
|
17121
|
+
number(),
|
|
17122
|
+
boolean()
|
|
17123
|
+
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
17124
|
+
object({
|
|
17125
|
+
gt: union([string(), number()]).optional(),
|
|
17126
|
+
gte: union([string(), number()]).optional(),
|
|
17127
|
+
lt: union([string(), number()]).optional(),
|
|
17128
|
+
lte: union([string(), number()]).optional()
|
|
17129
|
+
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
17130
|
+
]));
|
|
17131
|
+
var KindShape = { kind: RecordKindNameSchema };
|
|
17132
|
+
var TimeWindowShape = {
|
|
17133
|
+
deviceId: number().int().optional(),
|
|
17134
|
+
atFrom: number().optional(),
|
|
17135
|
+
atTo: number().optional()
|
|
17136
|
+
};
|
|
17137
|
+
var RecordQueryInputSchema = object({
|
|
17138
|
+
...KindShape,
|
|
17139
|
+
...TimeWindowShape,
|
|
17140
|
+
where: RecordWhereSchema.optional(),
|
|
17141
|
+
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
17142
|
+
order: _enum(["at", "id"]).optional(),
|
|
17143
|
+
desc: boolean().optional(),
|
|
17144
|
+
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
17145
|
+
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
17146
|
+
cursor: string().min(1).optional()
|
|
17147
|
+
});
|
|
17148
|
+
var RecordQueryResultSchema = object({
|
|
17149
|
+
records: array(RecordViewSchema),
|
|
17150
|
+
nextCursor: string().nullable()
|
|
17151
|
+
});
|
|
17152
|
+
var RecordKindStateSchema = _enum([
|
|
17153
|
+
"created",
|
|
17154
|
+
"unchanged",
|
|
17155
|
+
"updated"
|
|
17156
|
+
]);
|
|
17157
|
+
var DeclareKindResultSchema = object({
|
|
17158
|
+
kindId: number().int(),
|
|
17159
|
+
state: RecordKindStateSchema,
|
|
17160
|
+
createdIndexes: array(string()),
|
|
17161
|
+
/** The boot guard: the kind is empty while its legacy table still holds rows. */
|
|
17162
|
+
adoptionPending: object({
|
|
17163
|
+
legacyTable: string(),
|
|
17164
|
+
legacyRows: number().int()
|
|
17165
|
+
}).nullable()
|
|
17166
|
+
});
|
|
17167
|
+
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
17168
|
+
var SecretRefsList = array(string());
|
|
17169
|
+
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
17170
|
+
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
17171
|
+
kind: "mutation",
|
|
17172
|
+
auth: "admin"
|
|
17173
|
+
}), method(object({
|
|
17174
|
+
...KindShape,
|
|
17175
|
+
id: RecordIdSchema
|
|
17176
|
+
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
17177
|
+
...KindShape,
|
|
17178
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
17179
|
+
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
17180
|
+
...KindShape,
|
|
17181
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
17182
|
+
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
17183
|
+
...KindShape,
|
|
17184
|
+
record: RecordInputSchema,
|
|
17185
|
+
/** Compare-and-set. `0` means "the row must not exist". */
|
|
17186
|
+
ifRev: number().int().min(0).optional()
|
|
17187
|
+
}), object({
|
|
17188
|
+
rev: number().int(),
|
|
17189
|
+
releasedRefs: SecretRefsList
|
|
17190
|
+
}), {
|
|
17191
|
+
kind: "mutation",
|
|
17192
|
+
auth: "admin"
|
|
17193
|
+
}), method(object({
|
|
17194
|
+
...KindShape,
|
|
17195
|
+
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
17196
|
+
}), object({
|
|
17197
|
+
count: number().int(),
|
|
17198
|
+
releasedRefs: SecretRefsList
|
|
17199
|
+
}), {
|
|
17200
|
+
kind: "mutation",
|
|
17201
|
+
auth: "admin"
|
|
17202
|
+
}), method(object({
|
|
17203
|
+
...KindShape,
|
|
17204
|
+
id: RecordIdSchema,
|
|
17205
|
+
data: RecordBodySchema,
|
|
17206
|
+
at: number().int().nullable().optional(),
|
|
17207
|
+
expiresAt: number().int().nullable().optional(),
|
|
17208
|
+
ifRev: number().int().min(1).optional()
|
|
17209
|
+
}), object({
|
|
17210
|
+
rev: number().int(),
|
|
17211
|
+
releasedRefs: SecretRefsList
|
|
17212
|
+
}), {
|
|
17213
|
+
kind: "mutation",
|
|
17214
|
+
auth: "admin"
|
|
17215
|
+
}), method(object({
|
|
17216
|
+
...KindShape,
|
|
17217
|
+
id: RecordIdSchema
|
|
17218
|
+
}), object({
|
|
17219
|
+
deleted: boolean(),
|
|
17220
|
+
secretRefs: SecretRefsList
|
|
17221
|
+
}), {
|
|
17222
|
+
kind: "mutation",
|
|
17223
|
+
auth: "admin"
|
|
17224
|
+
}), method(object({
|
|
17225
|
+
...KindShape,
|
|
17226
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
17227
|
+
}), object({
|
|
17228
|
+
deleted: number().int(),
|
|
17229
|
+
secretRefs: SecretRefsList
|
|
17230
|
+
}), {
|
|
17231
|
+
kind: "mutation",
|
|
17232
|
+
auth: "admin"
|
|
17233
|
+
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
17234
|
+
...KindShape,
|
|
17235
|
+
...TimeWindowShape,
|
|
17236
|
+
where: RecordWhereSchema.optional()
|
|
17237
|
+
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
17238
|
+
...KindShape,
|
|
17239
|
+
deviceId: number().int().optional(),
|
|
17240
|
+
from: number(),
|
|
17241
|
+
to: number(),
|
|
17242
|
+
bucketMs: number().int().positive()
|
|
17243
|
+
}), object({ buckets: array(object({
|
|
17244
|
+
bucketStart: number(),
|
|
17245
|
+
count: number().int()
|
|
17246
|
+
})) }), { auth: "admin" }), method(object({
|
|
17247
|
+
...KindShape,
|
|
17248
|
+
op: _enum([
|
|
17249
|
+
"sum",
|
|
17250
|
+
"min",
|
|
17251
|
+
"max",
|
|
17252
|
+
"count"
|
|
17253
|
+
]),
|
|
17254
|
+
field: RecordFieldPathSchema.optional(),
|
|
17255
|
+
groupBy: RecordFieldPathSchema.optional(),
|
|
17256
|
+
where: RecordWhereSchema.optional()
|
|
17257
|
+
}), object({ rows: array(object({
|
|
17258
|
+
group: union([
|
|
17259
|
+
string(),
|
|
17260
|
+
number(),
|
|
17261
|
+
_null()
|
|
17262
|
+
]),
|
|
17263
|
+
value: number().nullable()
|
|
17264
|
+
})) }), { auth: "admin" }), method(object({
|
|
17265
|
+
...KindShape,
|
|
17266
|
+
deviceId: number().int().optional(),
|
|
17267
|
+
atBefore: number().optional(),
|
|
17268
|
+
where: RecordWhereSchema.optional()
|
|
17269
|
+
}), object({
|
|
17270
|
+
deleted: number().int(),
|
|
17271
|
+
secretRefs: SecretRefsList
|
|
17272
|
+
}), {
|
|
17273
|
+
kind: "mutation",
|
|
17274
|
+
auth: "admin"
|
|
17275
|
+
}), method(object({
|
|
17276
|
+
deviceId: number().int(),
|
|
17277
|
+
kinds: array(RecordKindNameSchema).optional()
|
|
17278
|
+
}), object({
|
|
17279
|
+
deleted: record(string(), number().int()),
|
|
17280
|
+
secretRefs: SecretRefsList
|
|
17281
|
+
}), {
|
|
17282
|
+
kind: "mutation",
|
|
17283
|
+
auth: "admin"
|
|
17284
|
+
}), method(object(KindShape), object({ id: number().int() }), {
|
|
17285
|
+
kind: "mutation",
|
|
17286
|
+
auth: "admin"
|
|
17287
|
+
});
|
|
17288
|
+
/** One table of the settings database. */
|
|
17289
|
+
var StorageTableSchema = object({
|
|
17290
|
+
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
17291
|
+
name: string(),
|
|
17292
|
+
/** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
|
|
17293
|
+
owner: string().nullable(),
|
|
17294
|
+
rows: number().int().nonnegative(),
|
|
17295
|
+
/** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
|
|
17296
|
+
bytes: number().int().nonnegative().nullable(),
|
|
17297
|
+
/** Declared by an addon since this engine process started (a running addon claims it). */
|
|
17298
|
+
declaredThisBoot: boolean()
|
|
17299
|
+
});
|
|
17300
|
+
/** One declared record kind (D881) as System -> Data shows it. */
|
|
17301
|
+
var StorageKindSchema = object({
|
|
17302
|
+
owner: string(),
|
|
17303
|
+
kind: string(),
|
|
17304
|
+
database: _enum(["settings", "events"]),
|
|
17305
|
+
class: string(),
|
|
17306
|
+
state: _enum([
|
|
17307
|
+
"declared",
|
|
17308
|
+
"orphaned",
|
|
17309
|
+
"dropped"
|
|
17310
|
+
]),
|
|
17311
|
+
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
17312
|
+
rows: number().int().nonnegative().nullable(),
|
|
17313
|
+
/** Rows x the average size of a sample; null for an empty kind. */
|
|
17314
|
+
bytesEstimate: number().int().nonnegative().nullable(),
|
|
17315
|
+
writeTrigger: string(),
|
|
17316
|
+
retention: RecordRetentionSchema,
|
|
17317
|
+
/** `true` when the kind is an entry kind (the generic entries UI lists it). */
|
|
17318
|
+
entry: boolean(),
|
|
17319
|
+
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
17320
|
+
declaration: RecordKindDeclarationSchema
|
|
17321
|
+
});
|
|
17322
|
+
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
17323
|
+
collection: string().min(1),
|
|
17324
|
+
expectRows: number().int().nonnegative()
|
|
17325
|
+
}), object({
|
|
17326
|
+
dropped: literal(true),
|
|
17327
|
+
rows: number().int().nonnegative()
|
|
17328
|
+
}), {
|
|
17329
|
+
kind: "mutation",
|
|
17330
|
+
auth: "admin"
|
|
17331
|
+
}), method(object({
|
|
17332
|
+
owner: string().min(1).optional(),
|
|
17333
|
+
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
17334
|
+
counts: boolean().optional()
|
|
17335
|
+
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
17336
|
+
owner: string().min(1),
|
|
17337
|
+
kind: RecordKindNameSchema
|
|
17338
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
17339
|
+
owner: string().min(1),
|
|
17340
|
+
kind: RecordKindNameSchema,
|
|
17341
|
+
records: array(RecordInputSchema).max(RECORD_BATCH_MAX),
|
|
17342
|
+
/** The LAST batch: the copy is complete, the boot guard stops asking. */
|
|
17343
|
+
complete: boolean().optional()
|
|
17344
|
+
}).refine((input) => input.records.length > 0 || input.complete === true, { message: "records must be non-empty unless complete is true" }), object({ adopted: number().int() }), {
|
|
17345
|
+
kind: "mutation",
|
|
17346
|
+
auth: "admin"
|
|
17347
|
+
}), method(object({
|
|
17348
|
+
collection: string().min(1),
|
|
17349
|
+
cursor: string().optional(),
|
|
17350
|
+
limit: number().int().min(1).max(1e3).optional()
|
|
17351
|
+
}), object({
|
|
17352
|
+
rows: array(object({
|
|
17353
|
+
id: string(),
|
|
17354
|
+
data: record(string(), unknown())
|
|
17355
|
+
})),
|
|
17356
|
+
nextCursor: string().nullable()
|
|
17357
|
+
}), { auth: "admin" }), method(object({ collection: string().min(1) }), object({ adopted: boolean() }), { auth: "admin" });
|
|
17358
|
+
/**
|
|
17359
|
+
* record-entries - the ONE admin surface for every list of user-created entries
|
|
17360
|
+
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
17361
|
+
* declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
|
|
17362
|
+
*
|
|
17363
|
+
* The UI is generic. `listEntryKinds` hands it, per kind, the JSON Schema of
|
|
17364
|
+
* the entry body with `secret: true` on every credential field, so a password
|
|
17365
|
+
* field is derived from the schema and never from a name. `putEntry` takes the
|
|
17366
|
+
* redaction token for an untouched secret (the stored value is kept), a string
|
|
17367
|
+
* for a new one, and the clear instruction to remove one. A read never carries
|
|
17368
|
+
* a secret value, and a locked keychain refuses the write with
|
|
17369
|
+
* `records/secret-unavailable` instead of storing plaintext.
|
|
17370
|
+
*
|
|
17371
|
+
* Hub-only, served in-process (it acts for each entry's owner on the keychain).
|
|
17372
|
+
*/
|
|
17373
|
+
var EntryKindInfoSchema = object({
|
|
17374
|
+
owner: string(),
|
|
17375
|
+
kind: RecordKindNameSchema,
|
|
17376
|
+
title: string(),
|
|
17377
|
+
nameField: string(),
|
|
17378
|
+
/** JSON Schema of the body; `secret: true` marks a password field. */
|
|
17379
|
+
fields: record(string(), unknown()),
|
|
17380
|
+
secretPaths: array(string()),
|
|
17381
|
+
rows: number().int().nonnegative()
|
|
17382
|
+
});
|
|
17383
|
+
var EntryViewSchema = object({
|
|
17384
|
+
id: RecordIdSchema,
|
|
17385
|
+
name: string(),
|
|
17386
|
+
rev: number().int(),
|
|
17387
|
+
updatedAt: number(),
|
|
17388
|
+
/** Secret fields hold the redaction token. */
|
|
17389
|
+
data: RecordBodySchema
|
|
17390
|
+
});
|
|
17391
|
+
var EntryAddress = {
|
|
17392
|
+
owner: string().min(1),
|
|
17393
|
+
kind: RecordKindNameSchema
|
|
17394
|
+
};
|
|
17395
|
+
method(object({}), object({ kinds: array(EntryKindInfoSchema) }), { auth: "admin" }), method(object({ ...EntryAddress }), object({ entries: array(EntryViewSchema) }), { auth: "admin" }), method(object({
|
|
17396
|
+
...EntryAddress,
|
|
17397
|
+
id: RecordIdSchema.optional(),
|
|
17398
|
+
data: RecordBodySchema,
|
|
17399
|
+
ifRev: number().int().min(1).optional()
|
|
17400
|
+
}), object({
|
|
17401
|
+
id: string(),
|
|
17402
|
+
rev: number().int()
|
|
17403
|
+
}), {
|
|
17404
|
+
kind: "mutation",
|
|
17405
|
+
auth: "admin"
|
|
17406
|
+
}), method(object({
|
|
17407
|
+
...EntryAddress,
|
|
17408
|
+
id: RecordIdSchema
|
|
17409
|
+
}), object({ deleted: boolean() }), {
|
|
17410
|
+
kind: "mutation",
|
|
17411
|
+
auth: "admin"
|
|
17412
|
+
});
|
|
17413
|
+
/**
|
|
16826
17414
|
* `keychain` — the hub's encrypted store of secret values (D787).
|
|
16827
17415
|
*
|
|
16828
17416
|
* Settings blobs never hold a secret: they hold a marker
|
|
@@ -17363,6 +17951,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
|
|
|
17363
17951
|
kind: "mutation",
|
|
17364
17952
|
auth: "admin",
|
|
17365
17953
|
caller: "required"
|
|
17954
|
+
}), method(object({
|
|
17955
|
+
ref: string().min(1),
|
|
17956
|
+
value: string().min(1)
|
|
17957
|
+
}), object({
|
|
17958
|
+
ref: string(),
|
|
17959
|
+
version: number().int()
|
|
17960
|
+
}), {
|
|
17961
|
+
kind: "mutation",
|
|
17962
|
+
auth: "admin",
|
|
17963
|
+
caller: "required"
|
|
17366
17964
|
}), method(object({ refs: array(string().min(1)).max(1e3) }), object({
|
|
17367
17965
|
deleted: array(string()).readonly(),
|
|
17368
17966
|
kept: array(object({
|
|
@@ -40041,6 +40639,9 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
40041
40639
|
}), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
40042
40640
|
kind: "mutation",
|
|
40043
40641
|
auth: "admin"
|
|
40642
|
+
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
40643
|
+
kind: "mutation",
|
|
40644
|
+
auth: "admin"
|
|
40044
40645
|
}), method(ProfileRefInputSchema, _void(), {
|
|
40045
40646
|
kind: "mutation",
|
|
40046
40647
|
auth: "admin"
|
|
@@ -50550,6 +51151,41 @@ var ServerRollbackInfoSchema = object({
|
|
|
50550
51151
|
atMs: number(),
|
|
50551
51152
|
reason: string()
|
|
50552
51153
|
});
|
|
51154
|
+
/**
|
|
51155
|
+
* The runner boot check (D877): the runners this node PLANNED (hub: one per
|
|
51156
|
+
* `resolveRunnerId` over its forked addons; agent: the runners its process
|
|
51157
|
+
* service holds, because only the hub plans) against the states the process
|
|
51158
|
+
* service reports. Kept in memory, recomputed by the same one-shot check after
|
|
51159
|
+
* every boot, served on `getServerPackageStatus`.
|
|
51160
|
+
*
|
|
51161
|
+
* `failed` is the D6 breaker's terminal state, `refused` a deterministic Init
|
|
51162
|
+
* refusal (an addon that said "I will not start, and here is why"), `missing` a
|
|
51163
|
+
* planned runner the process service has no entry for.
|
|
51164
|
+
*/
|
|
51165
|
+
var RunnerRefusalSchema = object({
|
|
51166
|
+
runnerId: string(),
|
|
51167
|
+
addonIds: array(string()),
|
|
51168
|
+
reason: string()
|
|
51169
|
+
});
|
|
51170
|
+
var RunnerBootCheckSchema = object({
|
|
51171
|
+
checkedAtMs: number(),
|
|
51172
|
+
/** Runners planned for this node. */
|
|
51173
|
+
expected: number(),
|
|
51174
|
+
/** Planned runners whose process is `running`. */
|
|
51175
|
+
running: number(),
|
|
51176
|
+
failed: array(string()),
|
|
51177
|
+
refused: array(RunnerRefusalSchema),
|
|
51178
|
+
missing: array(string()),
|
|
51179
|
+
/**
|
|
51180
|
+
* Runners still on their way: `starting`, or `crashed` waiting on a backoff
|
|
51181
|
+
* respawn. The check is FINAL only when this is empty; until then nothing is
|
|
51182
|
+
* pushed and the verdict is re-evaluated on the next runner state change.
|
|
51183
|
+
* Optional for version skew (absent = settled).
|
|
51184
|
+
*/
|
|
51185
|
+
pending: array(string()).optional(),
|
|
51186
|
+
/** True when the plan is the hub's (`missing` is then meaningful); false on an agent. */
|
|
51187
|
+
planKnown: boolean()
|
|
51188
|
+
});
|
|
50553
51189
|
var ServerPackageStatusSchema = object({
|
|
50554
51190
|
/** Root package name (`@camstack/server` on the hub). */
|
|
50555
51191
|
packageName: string(),
|
|
@@ -50612,7 +51248,14 @@ var ServerPackageStatusSchema = object({
|
|
|
50612
51248
|
* for version skew: an older provider's payload simply omits it, and an
|
|
50613
51249
|
* absent wrapper is unknown — never `native`.
|
|
50614
51250
|
*/
|
|
50615
|
-
wrapper: WrapperIdentitySchema.optional()
|
|
51251
|
+
wrapper: WrapperIdentitySchema.optional(),
|
|
51252
|
+
/**
|
|
51253
|
+
* What this node PLANNED to run against what its process service reports
|
|
51254
|
+
* (D877). Absent until the one-shot boot check has run (~60 s after boot) and
|
|
51255
|
+
* on a provider that predates it. Absent is "not checked yet", never "all
|
|
51256
|
+
* well": a consumer that needs the answer treats it as pending.
|
|
51257
|
+
*/
|
|
51258
|
+
runners: RunnerBootCheckSchema.optional()
|
|
50616
51259
|
});
|
|
50617
51260
|
var ServerUpdateCheckResultSchema = object({
|
|
50618
51261
|
packageName: string(),
|
|
@@ -50663,6 +51306,13 @@ var ServerUpdateOutcomeSchema = _enum([
|
|
|
50663
51306
|
"refused",
|
|
50664
51307
|
"failed"
|
|
50665
51308
|
]);
|
|
51309
|
+
/** One addon the pre-flight found the target version would refuse (D877). */
|
|
51310
|
+
var UpdateBlockerSchema = object({
|
|
51311
|
+
addonId: string(),
|
|
51312
|
+
reason: string(),
|
|
51313
|
+
/** What the operator does about it. */
|
|
51314
|
+
remedy: string()
|
|
51315
|
+
});
|
|
50666
51316
|
var ServerUpdateActionResultSchema = object({
|
|
50667
51317
|
accepted: boolean(),
|
|
50668
51318
|
targetVersion: string().nullable(),
|
|
@@ -50680,14 +51330,27 @@ var ServerUpdateActionResultSchema = object({
|
|
|
50680
51330
|
* The failure itself, verbatim enough to act on (the npm output for a
|
|
50681
51331
|
* staging install). Non-null only for `failed`.
|
|
50682
51332
|
*/
|
|
50683
|
-
error: string().nullable().optional()
|
|
51333
|
+
error: string().nullable().optional(),
|
|
51334
|
+
/**
|
|
51335
|
+
* The addons the target version would refuse to start on this node's live
|
|
51336
|
+
* store (D877), present when the pre-flight refused the update
|
|
51337
|
+
* (`reason: 'addon-would-refuse'`). Optional for version skew.
|
|
51338
|
+
*/
|
|
51339
|
+
blockers: array(UpdateBlockerSchema).optional()
|
|
50684
51340
|
});
|
|
50685
51341
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
50686
51342
|
kind: "mutation",
|
|
50687
51343
|
auth: "admin"
|
|
50688
|
-
}), method(object({
|
|
50689
|
-
/** Explicit target version; omitted = latest from the registry. */
|
|
50690
|
-
version: string().optional()
|
|
51344
|
+
}), method(object({
|
|
51345
|
+
/** Explicit target version; omitted = latest from the registry. */
|
|
51346
|
+
version: string().optional(),
|
|
51347
|
+
/**
|
|
51348
|
+
* Apply even though the pre-flight (D877) found addons the target would
|
|
51349
|
+
* refuse to start. Off by default: those addons stay down until the
|
|
51350
|
+
* remedy is run, which is the incident this gate exists to prevent.
|
|
51351
|
+
*/
|
|
51352
|
+
acknowledgeRefusals: boolean().optional()
|
|
51353
|
+
}), ServerUpdateActionResultSchema, {
|
|
50691
51354
|
kind: "mutation",
|
|
50692
51355
|
auth: "admin",
|
|
50693
51356
|
timeoutMs: 16 * 6e4
|
|
@@ -53133,7 +53796,9 @@ var ManifestShapeSchema = object({
|
|
|
53133
53796
|
var AddonProcessSchema = object({
|
|
53134
53797
|
pid: number().optional(),
|
|
53135
53798
|
mode: string(),
|
|
53136
|
-
state: string()
|
|
53799
|
+
state: string(),
|
|
53800
|
+
/** Why the runner is `refused` (an addon's own words), when it is (D877). */
|
|
53801
|
+
reason: string().optional()
|
|
53137
53802
|
});
|
|
53138
53803
|
/**
|
|
53139
53804
|
* Per-addon health snapshot from the kernel's AddonHealthMonitor.
|
|
@@ -53901,7 +54566,8 @@ var batteryCapability = {
|
|
|
53901
54566
|
DeviceType.Button,
|
|
53902
54567
|
DeviceType.Switch,
|
|
53903
54568
|
DeviceType.PetFeeder,
|
|
53904
|
-
DeviceType.Container
|
|
54569
|
+
DeviceType.Container,
|
|
54570
|
+
DeviceType.Thermostat
|
|
53905
54571
|
],
|
|
53906
54572
|
methods: {
|
|
53907
54573
|
/**
|
|
@@ -54522,6 +55188,12 @@ var ClimateControlStatusSchema = object({
|
|
|
54522
55188
|
/** Horizontal louver swing on/off. Null when the device has no
|
|
54523
55189
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
54524
55190
|
swingHorizontal: boolean().nullable(),
|
|
55191
|
+
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
55192
|
+
minTemp: number().nullable().optional(),
|
|
55193
|
+
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
55194
|
+
maxTemp: number().nullable().optional(),
|
|
55195
|
+
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
55196
|
+
tempStep: number().positive().nullable().optional(),
|
|
54525
55197
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
54526
55198
|
lastFetchedAt: number()
|
|
54527
55199
|
});
|
|
@@ -55376,6 +56048,125 @@ onStatusChanged: { data: object({
|
|
|
55376
56048
|
* whether persisting is worth a SQLite commit. */
|
|
55377
56049
|
volatileStateFields: ["lastChangedAt"]
|
|
55378
56050
|
};
|
|
56051
|
+
/**
|
|
56052
|
+
* Smart-dishwasher cap — program lifecycle, door / remote-start gates,
|
|
56053
|
+
* consumable levels (salt, rinse aid) and common program options.
|
|
56054
|
+
*
|
|
56055
|
+
* Sources: native Bosch Home Connect (`dishwasher` device type). Reusable
|
|
56056
|
+
* by any integration that exposes the same program / option surface.
|
|
56057
|
+
*
|
|
56058
|
+
* `kind: 'poll'` — cloud REST APIs typically have no push channel; the
|
|
56059
|
+
* provider refreshes the slice on an interval and eagerly after commands.
|
|
56060
|
+
*/
|
|
56061
|
+
var DishwasherStateSchema = _enum([
|
|
56062
|
+
"idle",
|
|
56063
|
+
"running",
|
|
56064
|
+
"paused",
|
|
56065
|
+
"finished",
|
|
56066
|
+
"error",
|
|
56067
|
+
"unavailable"
|
|
56068
|
+
]);
|
|
56069
|
+
var DishwasherConsumableLevelSchema = _enum([
|
|
56070
|
+
"ok",
|
|
56071
|
+
"low",
|
|
56072
|
+
"empty"
|
|
56073
|
+
]);
|
|
56074
|
+
var DishwasherStatusSchema = object({
|
|
56075
|
+
state: DishwasherStateSchema,
|
|
56076
|
+
/** Active program id/name, or null when none selected / unknown. */
|
|
56077
|
+
program: string().nullable(),
|
|
56078
|
+
/** Program tokens the hardware accepts — drives the UI selector. */
|
|
56079
|
+
availablePrograms: array(string()),
|
|
56080
|
+
/** Minutes remaining on the active cycle, or null when not running. */
|
|
56081
|
+
remainingMinutes: number().int().nonnegative().nullable(),
|
|
56082
|
+
/** Cycle completion 0..100, or null when not applicable. */
|
|
56083
|
+
progressPercent: number().min(0).max(100).nullable(),
|
|
56084
|
+
/** True when the door is reported open. */
|
|
56085
|
+
doorOpen: boolean(),
|
|
56086
|
+
/** True when remote start is allowed (door closed, power on, etc.). */
|
|
56087
|
+
remoteStartAllowed: boolean(),
|
|
56088
|
+
/** Main power / switched-on state. */
|
|
56089
|
+
powerOn: boolean(),
|
|
56090
|
+
saltLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
56091
|
+
rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
56092
|
+
halfLoad: boolean(),
|
|
56093
|
+
hygienePlus: boolean(),
|
|
56094
|
+
errorCode: number().nullable(),
|
|
56095
|
+
errorLabel: string().nullable(),
|
|
56096
|
+
lastFetchedAt: number()
|
|
56097
|
+
});
|
|
56098
|
+
var dishwasherCapability = {
|
|
56099
|
+
name: "dishwasher",
|
|
56100
|
+
scope: "device",
|
|
56101
|
+
deviceNative: true,
|
|
56102
|
+
mode: "singleton",
|
|
56103
|
+
deviceTypes: [DeviceType.Dishwasher],
|
|
56104
|
+
methods: {
|
|
56105
|
+
start: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
56106
|
+
kind: "mutation",
|
|
56107
|
+
auth: "protected"
|
|
56108
|
+
}),
|
|
56109
|
+
pause: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
56110
|
+
kind: "mutation",
|
|
56111
|
+
auth: "protected"
|
|
56112
|
+
}),
|
|
56113
|
+
stop: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
56114
|
+
kind: "mutation",
|
|
56115
|
+
auth: "protected"
|
|
56116
|
+
}),
|
|
56117
|
+
setProgram: method(object({
|
|
56118
|
+
deviceId: number().int().nonnegative(),
|
|
56119
|
+
program: string().min(1)
|
|
56120
|
+
}), _void(), {
|
|
56121
|
+
kind: "mutation",
|
|
56122
|
+
auth: "protected"
|
|
56123
|
+
}),
|
|
56124
|
+
setPower: method(object({
|
|
56125
|
+
deviceId: number().int().nonnegative(),
|
|
56126
|
+
on: boolean()
|
|
56127
|
+
}), _void(), {
|
|
56128
|
+
kind: "mutation",
|
|
56129
|
+
auth: "protected"
|
|
56130
|
+
}),
|
|
56131
|
+
setHalfLoad: method(object({
|
|
56132
|
+
deviceId: number().int().nonnegative(),
|
|
56133
|
+
on: boolean()
|
|
56134
|
+
}), _void(), {
|
|
56135
|
+
kind: "mutation",
|
|
56136
|
+
auth: "protected"
|
|
56137
|
+
}),
|
|
56138
|
+
setHygienePlus: method(object({
|
|
56139
|
+
deviceId: number().int().nonnegative(),
|
|
56140
|
+
on: boolean()
|
|
56141
|
+
}), _void(), {
|
|
56142
|
+
kind: "mutation",
|
|
56143
|
+
auth: "protected"
|
|
56144
|
+
}),
|
|
56145
|
+
setOption: method(object({
|
|
56146
|
+
deviceId: number().int().nonnegative(),
|
|
56147
|
+
key: string().min(1),
|
|
56148
|
+
value: union([
|
|
56149
|
+
boolean(),
|
|
56150
|
+
string(),
|
|
56151
|
+
number()
|
|
56152
|
+
])
|
|
56153
|
+
}), _void(), {
|
|
56154
|
+
kind: "mutation",
|
|
56155
|
+
auth: "protected"
|
|
56156
|
+
})
|
|
56157
|
+
},
|
|
56158
|
+
status: {
|
|
56159
|
+
schema: DishwasherStatusSchema,
|
|
56160
|
+
kind: "poll"
|
|
56161
|
+
},
|
|
56162
|
+
runtimeState: DishwasherStatusSchema,
|
|
56163
|
+
durability: "session",
|
|
56164
|
+
history: { fields: { progressPercent: {
|
|
56165
|
+
kind: "gauge",
|
|
56166
|
+
unit: "%",
|
|
56167
|
+
deadband: 2
|
|
56168
|
+
} } }
|
|
56169
|
+
};
|
|
55379
56170
|
var DoorbellStatusSchema = object({
|
|
55380
56171
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
55381
56172
|
lastPressedAt: number().nullable(),
|
|
@@ -56178,6 +56969,32 @@ var gasCapability = {
|
|
|
56178
56969
|
* whether persisting is worth a SQLite commit. */
|
|
56179
56970
|
volatileStateFields: ["lastChangedAt"]
|
|
56180
56971
|
};
|
|
56972
|
+
var HeatingValveStatusSchema = object({
|
|
56973
|
+
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
56974
|
+
position: number().min(0).max(100).nullable(),
|
|
56975
|
+
/** Ms epoch when the slice was last updated (push or poll). */
|
|
56976
|
+
lastFetchedAt: number()
|
|
56977
|
+
});
|
|
56978
|
+
var heatingValveCapability = {
|
|
56979
|
+
name: "heating-valve",
|
|
56980
|
+
scope: "device",
|
|
56981
|
+
deviceNative: true,
|
|
56982
|
+
mode: "singleton",
|
|
56983
|
+
deviceTypes: [DeviceType.Thermostat],
|
|
56984
|
+
methods: {},
|
|
56985
|
+
status: {
|
|
56986
|
+
schema: HeatingValveStatusSchema,
|
|
56987
|
+
kind: "push"
|
|
56988
|
+
},
|
|
56989
|
+
runtimeState: HeatingValveStatusSchema,
|
|
56990
|
+
durability: "session",
|
|
56991
|
+
history: { fields: { position: {
|
|
56992
|
+
kind: "gauge",
|
|
56993
|
+
unit: "%",
|
|
56994
|
+
deadband: 1
|
|
56995
|
+
} } },
|
|
56996
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
56997
|
+
};
|
|
56181
56998
|
/**
|
|
56182
56999
|
* Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
|
|
56183
57000
|
* an on/off actuator with an optional target-humidity setpoint and an
|
|
@@ -57310,7 +58127,19 @@ var TlsStatusSchema = object({
|
|
|
57310
58127
|
sans: array(string()),
|
|
57311
58128
|
caCertPem: string().nullable(),
|
|
57312
58129
|
reissueError: string().nullable(),
|
|
57313
|
-
restartRequired: boolean()
|
|
58130
|
+
restartRequired: boolean(),
|
|
58131
|
+
/** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
|
|
58132
|
+
caMissing: boolean(),
|
|
58133
|
+
/** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
|
|
58134
|
+
localCaConstrained: boolean().nullable(),
|
|
58135
|
+
/** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
|
|
58136
|
+
caReplacedAt: string().nullable(),
|
|
58137
|
+
/** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
|
|
58138
|
+
storedUpload: object({
|
|
58139
|
+
leafFingerprintSha256: string(),
|
|
58140
|
+
validTo: string(),
|
|
58141
|
+
hasCa: boolean()
|
|
58142
|
+
}).nullable()
|
|
57314
58143
|
});
|
|
57315
58144
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
57316
58145
|
/**
|
|
@@ -57366,6 +58195,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
57366
58195
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
57367
58196
|
kind: "mutation",
|
|
57368
58197
|
auth: "admin"
|
|
58198
|
+
}), method(_void(), TlsStatusSchema, {
|
|
58199
|
+
kind: "mutation",
|
|
58200
|
+
auth: "admin"
|
|
57369
58201
|
});
|
|
57370
58202
|
var LockControlStatusSchema = object({
|
|
57371
58203
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
@@ -63859,7 +64691,11 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
63859
64691
|
kind: "mutation",
|
|
63860
64692
|
auth: "admin",
|
|
63861
64693
|
access: "delete"
|
|
63862
|
-
}), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(
|
|
64694
|
+
}), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
64695
|
+
kind: "mutation",
|
|
64696
|
+
auth: "admin",
|
|
64697
|
+
access: "create"
|
|
64698
|
+
}), method(object({
|
|
63863
64699
|
userId: string(),
|
|
63864
64700
|
code: string()
|
|
63865
64701
|
}), object({ valid: boolean() }), {
|
|
@@ -65579,6 +66415,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
65579
66415
|
dayNight: dayNightCapability,
|
|
65580
66416
|
deviceDiscovery: deviceDiscoveryCapability,
|
|
65581
66417
|
deviceStatus: deviceStatusCapability,
|
|
66418
|
+
dishwasher: dishwasherCapability,
|
|
65582
66419
|
doorbell: doorbellCapability,
|
|
65583
66420
|
enumSensor: enumSensorCapability,
|
|
65584
66421
|
eventEmitter: eventEmitterCapability,
|
|
@@ -65586,6 +66423,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
65586
66423
|
featureProbe: featureProbeCapability,
|
|
65587
66424
|
flood: floodCapability,
|
|
65588
66425
|
gas: gasCapability,
|
|
66426
|
+
heatingValve: heatingValveCapability,
|
|
65589
66427
|
humidifier: humidifierCapability,
|
|
65590
66428
|
humiditySensor: humiditySensorCapability,
|
|
65591
66429
|
image: imageCapability,
|
|
@@ -65671,13 +66509,14 @@ var BaseDevice = class {
|
|
|
65671
66509
|
get state() {
|
|
65672
66510
|
if (!this._stateProxyCache) {
|
|
65673
66511
|
const cache = {};
|
|
65674
|
-
const handler = { get: (
|
|
66512
|
+
const handler = { get: (target, key) => {
|
|
65675
66513
|
const k = key;
|
|
65676
|
-
|
|
66514
|
+
const existing = Reflect.get(target, k);
|
|
66515
|
+
if (existing !== void 0) return existing;
|
|
65677
66516
|
const cap = DEVICE_LOCAL_STATE_CAPS[k];
|
|
65678
66517
|
if (!cap) return void 0;
|
|
65679
66518
|
const proxy = this.sliceProxy(cap);
|
|
65680
|
-
|
|
66519
|
+
Reflect.set(target, k, proxy);
|
|
65681
66520
|
return proxy;
|
|
65682
66521
|
} };
|
|
65683
66522
|
this._stateProxyCache = new Proxy(cache, handler);
|
|
@@ -65876,7 +66715,7 @@ var BaseDevice = class {
|
|
|
65876
66715
|
async updateSourceInfo(patch) {
|
|
65877
66716
|
const next = mergeSourceInfo(this.sourceInfo, patch);
|
|
65878
66717
|
this._sourceInfoCache = Object.freeze({ ...next });
|
|
65879
|
-
const action = this.ctx.api
|
|
66718
|
+
const action = this.ctx.api.deviceManager?.setMetadata;
|
|
65880
66719
|
if (action) try {
|
|
65881
66720
|
await action.mutate({
|
|
65882
66721
|
deviceId: this.id,
|
|
@@ -65911,7 +66750,7 @@ var BaseDevice = class {
|
|
|
65911
66750
|
* device boot on a meta refresh.
|
|
65912
66751
|
*/
|
|
65913
66752
|
async refreshFeatures() {
|
|
65914
|
-
const action = this.ctx.api
|
|
66753
|
+
const action = this.ctx.api.deviceManager?.registerDevice;
|
|
65915
66754
|
if (!action) return;
|
|
65916
66755
|
try {
|
|
65917
66756
|
await action.mutate({
|
|
@@ -66310,6 +67149,12 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
66310
67149
|
provider: this
|
|
66311
67150
|
}];
|
|
66312
67151
|
}
|
|
67152
|
+
/** Flatten `super.onInitialize()` whether it returned an array or an envelope. */
|
|
67153
|
+
providerRegistrationsFromSuper(result) {
|
|
67154
|
+
if (Array.isArray(result)) return result;
|
|
67155
|
+
if (result !== void 0 && result.providers !== void 0) return [...result.providers];
|
|
67156
|
+
return [];
|
|
67157
|
+
}
|
|
66313
67158
|
async onShutdown() {
|
|
66314
67159
|
this._restoreShuttingDown = true;
|
|
66315
67160
|
this.cancelRestoreRetries();
|
|
@@ -66836,7 +67681,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
66836
67681
|
return toDeviceSummary(device, this.addonId);
|
|
66837
67682
|
}
|
|
66838
67683
|
};
|
|
66839
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
|
|
67684
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
|
|
66840
67685
|
new Set(Object.values(DeviceType));
|
|
66841
67686
|
Object.freeze({
|
|
66842
67687
|
"accessories.getStatus": {
|
|
@@ -67985,6 +68830,12 @@ Object.freeze({
|
|
|
67985
68830
|
addonId: null,
|
|
67986
68831
|
access: "view"
|
|
67987
68832
|
},
|
|
68833
|
+
"coreBlocks.migrateRawSecrets": {
|
|
68834
|
+
capName: "core-blocks",
|
|
68835
|
+
capScope: "system",
|
|
68836
|
+
addonId: null,
|
|
68837
|
+
access: "create"
|
|
68838
|
+
},
|
|
67988
68839
|
"coreBlocks.restart": {
|
|
67989
68840
|
capName: "core-blocks",
|
|
67990
68841
|
capScope: "system",
|
|
@@ -69251,6 +70102,60 @@ Object.freeze({
|
|
|
69251
70102
|
addonId: null,
|
|
69252
70103
|
access: "view"
|
|
69253
70104
|
},
|
|
70105
|
+
"dishwasher.getStatus": {
|
|
70106
|
+
capName: "dishwasher",
|
|
70107
|
+
capScope: "device",
|
|
70108
|
+
addonId: null,
|
|
70109
|
+
access: "view"
|
|
70110
|
+
},
|
|
70111
|
+
"dishwasher.pause": {
|
|
70112
|
+
capName: "dishwasher",
|
|
70113
|
+
capScope: "device",
|
|
70114
|
+
addonId: null,
|
|
70115
|
+
access: "create"
|
|
70116
|
+
},
|
|
70117
|
+
"dishwasher.setHalfLoad": {
|
|
70118
|
+
capName: "dishwasher",
|
|
70119
|
+
capScope: "device",
|
|
70120
|
+
addonId: null,
|
|
70121
|
+
access: "create"
|
|
70122
|
+
},
|
|
70123
|
+
"dishwasher.setHygienePlus": {
|
|
70124
|
+
capName: "dishwasher",
|
|
70125
|
+
capScope: "device",
|
|
70126
|
+
addonId: null,
|
|
70127
|
+
access: "create"
|
|
70128
|
+
},
|
|
70129
|
+
"dishwasher.setOption": {
|
|
70130
|
+
capName: "dishwasher",
|
|
70131
|
+
capScope: "device",
|
|
70132
|
+
addonId: null,
|
|
70133
|
+
access: "create"
|
|
70134
|
+
},
|
|
70135
|
+
"dishwasher.setPower": {
|
|
70136
|
+
capName: "dishwasher",
|
|
70137
|
+
capScope: "device",
|
|
70138
|
+
addonId: null,
|
|
70139
|
+
access: "create"
|
|
70140
|
+
},
|
|
70141
|
+
"dishwasher.setProgram": {
|
|
70142
|
+
capName: "dishwasher",
|
|
70143
|
+
capScope: "device",
|
|
70144
|
+
addonId: null,
|
|
70145
|
+
access: "create"
|
|
70146
|
+
},
|
|
70147
|
+
"dishwasher.start": {
|
|
70148
|
+
capName: "dishwasher",
|
|
70149
|
+
capScope: "device",
|
|
70150
|
+
addonId: null,
|
|
70151
|
+
access: "create"
|
|
70152
|
+
},
|
|
70153
|
+
"dishwasher.stop": {
|
|
70154
|
+
capName: "dishwasher",
|
|
70155
|
+
capScope: "device",
|
|
70156
|
+
addonId: null,
|
|
70157
|
+
access: "create"
|
|
70158
|
+
},
|
|
69254
70159
|
"doorbell.getStatus": {
|
|
69255
70160
|
capName: "doorbell",
|
|
69256
70161
|
capScope: "device",
|
|
@@ -69461,6 +70366,12 @@ Object.freeze({
|
|
|
69461
70366
|
addonId: null,
|
|
69462
70367
|
access: "view"
|
|
69463
70368
|
},
|
|
70369
|
+
"heatingValve.getStatus": {
|
|
70370
|
+
capName: "heating-valve",
|
|
70371
|
+
capScope: "device",
|
|
70372
|
+
addonId: null,
|
|
70373
|
+
access: "view"
|
|
70374
|
+
},
|
|
69464
70375
|
"humidifier.getStatus": {
|
|
69465
70376
|
capName: "humidifier",
|
|
69466
70377
|
capScope: "device",
|
|
@@ -69671,6 +70582,12 @@ Object.freeze({
|
|
|
69671
70582
|
addonId: null,
|
|
69672
70583
|
access: "create"
|
|
69673
70584
|
},
|
|
70585
|
+
"keychainAdmin.importSecret": {
|
|
70586
|
+
capName: "keychain-admin",
|
|
70587
|
+
capScope: "system",
|
|
70588
|
+
addonId: null,
|
|
70589
|
+
access: "create"
|
|
70590
|
+
},
|
|
69674
70591
|
"keychainAdmin.listAudit": {
|
|
69675
70592
|
capName: "keychain-admin",
|
|
69676
70593
|
capScope: "system",
|
|
@@ -69839,6 +70756,12 @@ Object.freeze({
|
|
|
69839
70756
|
addonId: null,
|
|
69840
70757
|
access: "view"
|
|
69841
70758
|
},
|
|
70759
|
+
"llm.migrateRawSecrets": {
|
|
70760
|
+
capName: "llm",
|
|
70761
|
+
capScope: "system",
|
|
70762
|
+
addonId: null,
|
|
70763
|
+
access: "create"
|
|
70764
|
+
},
|
|
69842
70765
|
"llm.resolveModelRef": {
|
|
69843
70766
|
capName: "llm",
|
|
69844
70767
|
capScope: "system",
|
|
@@ -70067,6 +70990,12 @@ Object.freeze({
|
|
|
70067
70990
|
addonId: null,
|
|
70068
70991
|
access: "create"
|
|
70069
70992
|
},
|
|
70993
|
+
"localNetwork.useStoredUploadedCertificate": {
|
|
70994
|
+
capName: "local-network",
|
|
70995
|
+
capScope: "system",
|
|
70996
|
+
addonId: null,
|
|
70997
|
+
access: "create"
|
|
70998
|
+
},
|
|
70070
70999
|
"lockControl.getStatus": {
|
|
70071
71000
|
capName: "lock-control",
|
|
70072
71001
|
capScope: "device",
|
|
@@ -72719,6 +73648,30 @@ Object.freeze({
|
|
|
72719
73648
|
addonId: null,
|
|
72720
73649
|
access: "create"
|
|
72721
73650
|
},
|
|
73651
|
+
"recordEntries.deleteEntry": {
|
|
73652
|
+
capName: "record-entries",
|
|
73653
|
+
capScope: "system",
|
|
73654
|
+
addonId: null,
|
|
73655
|
+
access: "delete"
|
|
73656
|
+
},
|
|
73657
|
+
"recordEntries.listEntries": {
|
|
73658
|
+
capName: "record-entries",
|
|
73659
|
+
capScope: "system",
|
|
73660
|
+
addonId: null,
|
|
73661
|
+
access: "view"
|
|
73662
|
+
},
|
|
73663
|
+
"recordEntries.listEntryKinds": {
|
|
73664
|
+
capName: "record-entries",
|
|
73665
|
+
capScope: "system",
|
|
73666
|
+
addonId: null,
|
|
73667
|
+
access: "view"
|
|
73668
|
+
},
|
|
73669
|
+
"recordEntries.putEntry": {
|
|
73670
|
+
capName: "record-entries",
|
|
73671
|
+
capScope: "system",
|
|
73672
|
+
addonId: null,
|
|
73673
|
+
access: "create"
|
|
73674
|
+
},
|
|
72722
73675
|
"recording.getAvailability": {
|
|
72723
73676
|
capName: "recording",
|
|
72724
73677
|
capScope: "device",
|
|
@@ -73019,6 +73972,102 @@ Object.freeze({
|
|
|
73019
73972
|
addonId: null,
|
|
73020
73973
|
access: "view"
|
|
73021
73974
|
},
|
|
73975
|
+
"records.aggregate": {
|
|
73976
|
+
capName: "records",
|
|
73977
|
+
capScope: "system",
|
|
73978
|
+
addonId: null,
|
|
73979
|
+
access: "view"
|
|
73980
|
+
},
|
|
73981
|
+
"records.allocateId": {
|
|
73982
|
+
capName: "records",
|
|
73983
|
+
capScope: "system",
|
|
73984
|
+
addonId: null,
|
|
73985
|
+
access: "create"
|
|
73986
|
+
},
|
|
73987
|
+
"records.count": {
|
|
73988
|
+
capName: "records",
|
|
73989
|
+
capScope: "system",
|
|
73990
|
+
addonId: null,
|
|
73991
|
+
access: "view"
|
|
73992
|
+
},
|
|
73993
|
+
"records.declareKinds": {
|
|
73994
|
+
capName: "records",
|
|
73995
|
+
capScope: "system",
|
|
73996
|
+
addonId: null,
|
|
73997
|
+
access: "create"
|
|
73998
|
+
},
|
|
73999
|
+
"records.delete": {
|
|
74000
|
+
capName: "records",
|
|
74001
|
+
capScope: "system",
|
|
74002
|
+
addonId: null,
|
|
74003
|
+
access: "delete"
|
|
74004
|
+
},
|
|
74005
|
+
"records.deleteDevice": {
|
|
74006
|
+
capName: "records",
|
|
74007
|
+
capScope: "system",
|
|
74008
|
+
addonId: null,
|
|
74009
|
+
access: "delete"
|
|
74010
|
+
},
|
|
74011
|
+
"records.deleteMany": {
|
|
74012
|
+
capName: "records",
|
|
74013
|
+
capScope: "system",
|
|
74014
|
+
addonId: null,
|
|
74015
|
+
access: "delete"
|
|
74016
|
+
},
|
|
74017
|
+
"records.deleteWhere": {
|
|
74018
|
+
capName: "records",
|
|
74019
|
+
capScope: "system",
|
|
74020
|
+
addonId: null,
|
|
74021
|
+
access: "delete"
|
|
74022
|
+
},
|
|
74023
|
+
"records.get": {
|
|
74024
|
+
capName: "records",
|
|
74025
|
+
capScope: "system",
|
|
74026
|
+
addonId: null,
|
|
74027
|
+
access: "view"
|
|
74028
|
+
},
|
|
74029
|
+
"records.getMany": {
|
|
74030
|
+
capName: "records",
|
|
74031
|
+
capScope: "system",
|
|
74032
|
+
addonId: null,
|
|
74033
|
+
access: "view"
|
|
74034
|
+
},
|
|
74035
|
+
"records.histogram": {
|
|
74036
|
+
capName: "records",
|
|
74037
|
+
capScope: "system",
|
|
74038
|
+
addonId: null,
|
|
74039
|
+
access: "view"
|
|
74040
|
+
},
|
|
74041
|
+
"records.patch": {
|
|
74042
|
+
capName: "records",
|
|
74043
|
+
capScope: "system",
|
|
74044
|
+
addonId: null,
|
|
74045
|
+
access: "create"
|
|
74046
|
+
},
|
|
74047
|
+
"records.put": {
|
|
74048
|
+
capName: "records",
|
|
74049
|
+
capScope: "system",
|
|
74050
|
+
addonId: null,
|
|
74051
|
+
access: "create"
|
|
74052
|
+
},
|
|
74053
|
+
"records.putMany": {
|
|
74054
|
+
capName: "records",
|
|
74055
|
+
capScope: "system",
|
|
74056
|
+
addonId: null,
|
|
74057
|
+
access: "create"
|
|
74058
|
+
},
|
|
74059
|
+
"records.query": {
|
|
74060
|
+
capName: "records",
|
|
74061
|
+
capScope: "system",
|
|
74062
|
+
addonId: null,
|
|
74063
|
+
access: "view"
|
|
74064
|
+
},
|
|
74065
|
+
"records.secretRefs": {
|
|
74066
|
+
capName: "records",
|
|
74067
|
+
capScope: "system",
|
|
74068
|
+
addonId: null,
|
|
74069
|
+
access: "view"
|
|
74070
|
+
},
|
|
73022
74071
|
"sceneMonitor.captureReference": {
|
|
73023
74072
|
capName: "scene-monitor",
|
|
73024
74073
|
capScope: "device",
|
|
@@ -73451,6 +74500,48 @@ Object.freeze({
|
|
|
73451
74500
|
addonId: null,
|
|
73452
74501
|
access: "create"
|
|
73453
74502
|
},
|
|
74503
|
+
"storageAdmin.adoptRecords": {
|
|
74504
|
+
capName: "storage-admin",
|
|
74505
|
+
capScope: "system",
|
|
74506
|
+
addonId: null,
|
|
74507
|
+
access: "create"
|
|
74508
|
+
},
|
|
74509
|
+
"storageAdmin.dropCollection": {
|
|
74510
|
+
capName: "storage-admin",
|
|
74511
|
+
capScope: "system",
|
|
74512
|
+
addonId: null,
|
|
74513
|
+
access: "delete"
|
|
74514
|
+
},
|
|
74515
|
+
"storageAdmin.isAdoptedLegacyTable": {
|
|
74516
|
+
capName: "storage-admin",
|
|
74517
|
+
capScope: "system",
|
|
74518
|
+
addonId: null,
|
|
74519
|
+
access: "view"
|
|
74520
|
+
},
|
|
74521
|
+
"storageAdmin.listKinds": {
|
|
74522
|
+
capName: "storage-admin",
|
|
74523
|
+
capScope: "system",
|
|
74524
|
+
addonId: null,
|
|
74525
|
+
access: "view"
|
|
74526
|
+
},
|
|
74527
|
+
"storageAdmin.listTables": {
|
|
74528
|
+
capName: "storage-admin",
|
|
74529
|
+
capScope: "system",
|
|
74530
|
+
addonId: null,
|
|
74531
|
+
access: "view"
|
|
74532
|
+
},
|
|
74533
|
+
"storageAdmin.readLegacyCollection": {
|
|
74534
|
+
capName: "storage-admin",
|
|
74535
|
+
capScope: "system",
|
|
74536
|
+
addonId: null,
|
|
74537
|
+
access: "view"
|
|
74538
|
+
},
|
|
74539
|
+
"storageAdmin.readRecords": {
|
|
74540
|
+
capName: "storage-admin",
|
|
74541
|
+
capScope: "system",
|
|
74542
|
+
addonId: null,
|
|
74543
|
+
access: "view"
|
|
74544
|
+
},
|
|
73454
74545
|
"storageEvictable.evict": {
|
|
73455
74546
|
capName: "storage-evictable",
|
|
73456
74547
|
capScope: "system",
|
|
@@ -74363,6 +75454,12 @@ Object.freeze({
|
|
|
74363
75454
|
addonId: null,
|
|
74364
75455
|
access: "view"
|
|
74365
75456
|
},
|
|
75457
|
+
"userManagement.migrateRawSecrets": {
|
|
75458
|
+
capName: "user-management",
|
|
75459
|
+
capScope: "system",
|
|
75460
|
+
addonId: null,
|
|
75461
|
+
access: "create"
|
|
75462
|
+
},
|
|
74366
75463
|
"userManagement.oauthExchangeCode": {
|
|
74367
75464
|
capName: "user-management",
|
|
74368
75465
|
capScope: "system",
|
|
@@ -74862,7 +75959,7 @@ Object.freeze({
|
|
|
74862
75959
|
access: "create"
|
|
74863
75960
|
}
|
|
74864
75961
|
});
|
|
74865
|
-
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;
|
|
75962
|
+
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.Dishwasher;
|
|
74866
75963
|
DeviceFeature.BatteryOperated;
|
|
74867
75964
|
Object.freeze({
|
|
74868
75965
|
"accessories.getStatus": [{
|
|
@@ -75686,6 +76783,51 @@ Object.freeze({
|
|
|
75686
76783
|
form: "single",
|
|
75687
76784
|
optional: false
|
|
75688
76785
|
}],
|
|
76786
|
+
"dishwasher.getStatus": [{
|
|
76787
|
+
name: "deviceId",
|
|
76788
|
+
form: "single",
|
|
76789
|
+
optional: false
|
|
76790
|
+
}],
|
|
76791
|
+
"dishwasher.pause": [{
|
|
76792
|
+
name: "deviceId",
|
|
76793
|
+
form: "single",
|
|
76794
|
+
optional: false
|
|
76795
|
+
}],
|
|
76796
|
+
"dishwasher.setHalfLoad": [{
|
|
76797
|
+
name: "deviceId",
|
|
76798
|
+
form: "single",
|
|
76799
|
+
optional: false
|
|
76800
|
+
}],
|
|
76801
|
+
"dishwasher.setHygienePlus": [{
|
|
76802
|
+
name: "deviceId",
|
|
76803
|
+
form: "single",
|
|
76804
|
+
optional: false
|
|
76805
|
+
}],
|
|
76806
|
+
"dishwasher.setOption": [{
|
|
76807
|
+
name: "deviceId",
|
|
76808
|
+
form: "single",
|
|
76809
|
+
optional: false
|
|
76810
|
+
}],
|
|
76811
|
+
"dishwasher.setPower": [{
|
|
76812
|
+
name: "deviceId",
|
|
76813
|
+
form: "single",
|
|
76814
|
+
optional: false
|
|
76815
|
+
}],
|
|
76816
|
+
"dishwasher.setProgram": [{
|
|
76817
|
+
name: "deviceId",
|
|
76818
|
+
form: "single",
|
|
76819
|
+
optional: false
|
|
76820
|
+
}],
|
|
76821
|
+
"dishwasher.start": [{
|
|
76822
|
+
name: "deviceId",
|
|
76823
|
+
form: "single",
|
|
76824
|
+
optional: false
|
|
76825
|
+
}],
|
|
76826
|
+
"dishwasher.stop": [{
|
|
76827
|
+
name: "deviceId",
|
|
76828
|
+
form: "single",
|
|
76829
|
+
optional: false
|
|
76830
|
+
}],
|
|
75689
76831
|
"doorbell.getStatus": [{
|
|
75690
76832
|
name: "deviceId",
|
|
75691
76833
|
form: "single",
|
|
@@ -75755,6 +76897,11 @@ Object.freeze({
|
|
|
75755
76897
|
form: "single",
|
|
75756
76898
|
optional: false
|
|
75757
76899
|
}],
|
|
76900
|
+
"heatingValve.getStatus": [{
|
|
76901
|
+
name: "deviceId",
|
|
76902
|
+
form: "single",
|
|
76903
|
+
optional: false
|
|
76904
|
+
}],
|
|
75758
76905
|
"humidifier.getStatus": [{
|
|
75759
76906
|
name: "deviceId",
|
|
75760
76907
|
form: "single",
|
|
@@ -77039,6 +78186,37 @@ Object.freeze({
|
|
|
77039
78186
|
form: "single",
|
|
77040
78187
|
optional: false
|
|
77041
78188
|
}],
|
|
78189
|
+
"records.count": [{
|
|
78190
|
+
name: "deviceId",
|
|
78191
|
+
form: "single",
|
|
78192
|
+
optional: true
|
|
78193
|
+
}],
|
|
78194
|
+
"records.deleteDevice": [{
|
|
78195
|
+
name: "deviceId",
|
|
78196
|
+
form: "single",
|
|
78197
|
+
optional: false
|
|
78198
|
+
}],
|
|
78199
|
+
"records.deleteWhere": [{
|
|
78200
|
+
name: "deviceId",
|
|
78201
|
+
form: "single",
|
|
78202
|
+
optional: true
|
|
78203
|
+
}],
|
|
78204
|
+
"records.histogram": [{
|
|
78205
|
+
name: "deviceId",
|
|
78206
|
+
form: "single",
|
|
78207
|
+
optional: true
|
|
78208
|
+
}],
|
|
78209
|
+
"records.putMany": [{
|
|
78210
|
+
name: "records",
|
|
78211
|
+
form: "object-array",
|
|
78212
|
+
optional: false,
|
|
78213
|
+
itemField: "deviceId"
|
|
78214
|
+
}],
|
|
78215
|
+
"records.query": [{
|
|
78216
|
+
name: "deviceId",
|
|
78217
|
+
form: "single",
|
|
78218
|
+
optional: true
|
|
78219
|
+
}],
|
|
77042
78220
|
"sceneMonitor.captureReference": [{
|
|
77043
78221
|
name: "deviceId",
|
|
77044
78222
|
form: "single",
|
|
@@ -77155,6 +78333,17 @@ Object.freeze({
|
|
|
77155
78333
|
form: "single",
|
|
77156
78334
|
optional: false
|
|
77157
78335
|
}],
|
|
78336
|
+
"storageAdmin.adoptRecords": [{
|
|
78337
|
+
name: "records",
|
|
78338
|
+
form: "object-array",
|
|
78339
|
+
optional: false,
|
|
78340
|
+
itemField: "deviceId"
|
|
78341
|
+
}],
|
|
78342
|
+
"storageAdmin.readRecords": [{
|
|
78343
|
+
name: "deviceId",
|
|
78344
|
+
form: "single",
|
|
78345
|
+
optional: true
|
|
78346
|
+
}],
|
|
77158
78347
|
"streamBroker.acquireEgressTranscode": [{
|
|
77159
78348
|
name: "deviceId",
|
|
77160
78349
|
form: "single",
|
|
@@ -80021,7 +81210,7 @@ var TuyaProviderAddon = class extends BaseDeviceProvider {
|
|
|
80021
81210
|
super({});
|
|
80022
81211
|
}
|
|
80023
81212
|
async onInitialize() {
|
|
80024
|
-
const regs = await super.onInitialize();
|
|
81213
|
+
const regs = this.providerRegistrationsFromSuper(await super.onInitialize());
|
|
80025
81214
|
this.clients = new TuyaClientRegistry(this.ctx.logger, tuyaConnections);
|
|
80026
81215
|
this.clients.setOnConnected((integrationId) => {
|
|
80027
81216
|
this.ctx.logger.info("Tuya: integration connected", { meta: { integrationId } });
|