@camstack/addon-mqtt-broker 1.2.125 → 1.2.127
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/mqtt-broker.addon.js +1975 -81
- package/dist/mqtt-broker.addon.mjs +1975 -81
- package/package.json +1 -1
|
@@ -39,7 +39,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
40
40
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
41
41
|
//#endregion
|
|
42
|
-
//#region ../types/dist/event-category-
|
|
42
|
+
//#region ../types/dist/event-category-BvOFi6PY.mjs
|
|
43
43
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
44
44
|
EventCategory["SystemBoot"] = "system.boot";
|
|
45
45
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -103,6 +103,24 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
103
103
|
* consumes this category and maps it to the `addon-crash-loop` kind.
|
|
104
104
|
*/
|
|
105
105
|
EventCategory["AddonRunnerFailed"] = "addon.runner-failed";
|
|
106
|
+
/**
|
|
107
|
+
* A runner whose addon refused to start with a typed reason (D877) - parked
|
|
108
|
+
* in `refused`, never counted by the crash breaker, retried on operator
|
|
109
|
+
* action. One event per refusal.
|
|
110
|
+
*/
|
|
111
|
+
EventCategory["AddonRunnerRefused"] = "addon.runner-refused";
|
|
112
|
+
/**
|
|
113
|
+
* A runner settled on a state (D877): `running` (READY), `crashed`, `failed` or
|
|
114
|
+
* `refused`. In-process telemetry for the boot check (re-evaluate while any
|
|
115
|
+
* runner is still pending) and the alert centre (close a recovered runner).
|
|
116
|
+
*/
|
|
117
|
+
EventCategory["ProcessStateChanged"] = "process.state-changed";
|
|
118
|
+
/**
|
|
119
|
+
* The node's one-shot runner boot check (D877): planned runners against the
|
|
120
|
+
* process service, `degraded` when any is failed, refused or missing. The
|
|
121
|
+
* alert centre turns it into one alert and a direct critical push.
|
|
122
|
+
*/
|
|
123
|
+
EventCategory["SystemRunnerCheck"] = "system.runner-check";
|
|
106
124
|
EventCategory["AddonError"] = "addon.error";
|
|
107
125
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
108
126
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -4145,6 +4163,12 @@ var numberProcessor = (schema, ctx, _json, _params) => {
|
|
|
4145
4163
|
var booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
4146
4164
|
json.type = "boolean";
|
|
4147
4165
|
};
|
|
4166
|
+
var bigintProcessor = (_schema, ctx, _json, _params) => {
|
|
4167
|
+
if (ctx.unrepresentable === "throw") throw new Error("BigInt cannot be represented in JSON Schema");
|
|
4168
|
+
};
|
|
4169
|
+
var symbolProcessor = (_schema, ctx, _json, _params) => {
|
|
4170
|
+
if (ctx.unrepresentable === "throw") throw new Error("Symbols cannot be represented in JSON Schema");
|
|
4171
|
+
};
|
|
4148
4172
|
var nullProcessor = (_schema, ctx, json, _params) => {
|
|
4149
4173
|
if (ctx.target === "openapi-3.0") {
|
|
4150
4174
|
json.type = "string";
|
|
@@ -4152,12 +4176,17 @@ var nullProcessor = (_schema, ctx, json, _params) => {
|
|
|
4152
4176
|
json.enum = [null];
|
|
4153
4177
|
} else json.type = "null";
|
|
4154
4178
|
};
|
|
4179
|
+
var undefinedProcessor = (_schema, ctx, _json, _params) => {
|
|
4180
|
+
if (ctx.unrepresentable === "throw") throw new Error("Undefined cannot be represented in JSON Schema");
|
|
4181
|
+
};
|
|
4155
4182
|
var voidProcessor = (_schema, ctx, _json, _params) => {
|
|
4156
4183
|
if (ctx.unrepresentable === "throw") throw new Error("Void cannot be represented in JSON Schema");
|
|
4157
4184
|
};
|
|
4158
4185
|
var neverProcessor = (_schema, _ctx, json, _params) => {
|
|
4159
4186
|
json.not = {};
|
|
4160
4187
|
};
|
|
4188
|
+
var anyProcessor = (_schema, _ctx, _json, _params) => {};
|
|
4189
|
+
var unknownProcessor = (_schema, _ctx, _json, _params) => {};
|
|
4161
4190
|
var dateProcessor = (_schema, ctx, _json, _params) => {
|
|
4162
4191
|
if (ctx.unrepresentable === "throw") throw new Error("Date cannot be represented in JSON Schema");
|
|
4163
4192
|
};
|
|
@@ -4189,12 +4218,53 @@ var literalProcessor = (schema, ctx, json, _params) => {
|
|
|
4189
4218
|
json.enum = vals;
|
|
4190
4219
|
}
|
|
4191
4220
|
};
|
|
4221
|
+
var nanProcessor = (_schema, ctx, _json, _params) => {
|
|
4222
|
+
if (ctx.unrepresentable === "throw") throw new Error("NaN cannot be represented in JSON Schema");
|
|
4223
|
+
};
|
|
4224
|
+
var templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
4225
|
+
const _json = json;
|
|
4226
|
+
const pattern = schema._zod.pattern;
|
|
4227
|
+
if (!pattern) throw new Error("Pattern not found in template literal");
|
|
4228
|
+
_json.type = "string";
|
|
4229
|
+
_json.pattern = pattern.source;
|
|
4230
|
+
};
|
|
4231
|
+
var fileProcessor = (schema, _ctx, json, _params) => {
|
|
4232
|
+
const _json = json;
|
|
4233
|
+
const file = {
|
|
4234
|
+
type: "string",
|
|
4235
|
+
format: "binary",
|
|
4236
|
+
contentEncoding: "binary"
|
|
4237
|
+
};
|
|
4238
|
+
const { minimum, maximum, mime } = schema._zod.bag;
|
|
4239
|
+
if (minimum !== void 0) file.minLength = minimum;
|
|
4240
|
+
if (maximum !== void 0) file.maxLength = maximum;
|
|
4241
|
+
if (mime) if (mime.length === 1) {
|
|
4242
|
+
file.contentMediaType = mime[0];
|
|
4243
|
+
Object.assign(_json, file);
|
|
4244
|
+
} else {
|
|
4245
|
+
Object.assign(_json, file);
|
|
4246
|
+
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
4247
|
+
}
|
|
4248
|
+
else Object.assign(_json, file);
|
|
4249
|
+
};
|
|
4250
|
+
var successProcessor = (_schema, _ctx, json, _params) => {
|
|
4251
|
+
json.type = "boolean";
|
|
4252
|
+
};
|
|
4192
4253
|
var customProcessor = (_schema, ctx, _json, _params) => {
|
|
4193
4254
|
if (ctx.unrepresentable === "throw") throw new Error("Custom types cannot be represented in JSON Schema");
|
|
4194
4255
|
};
|
|
4256
|
+
var functionProcessor = (_schema, ctx, _json, _params) => {
|
|
4257
|
+
if (ctx.unrepresentable === "throw") throw new Error("Function types cannot be represented in JSON Schema");
|
|
4258
|
+
};
|
|
4195
4259
|
var transformProcessor = (_schema, ctx, _json, _params) => {
|
|
4196
4260
|
if (ctx.unrepresentable === "throw") throw new Error("Transforms cannot be represented in JSON Schema");
|
|
4197
4261
|
};
|
|
4262
|
+
var mapProcessor = (_schema, ctx, _json, _params) => {
|
|
4263
|
+
if (ctx.unrepresentable === "throw") throw new Error("Map cannot be represented in JSON Schema");
|
|
4264
|
+
};
|
|
4265
|
+
var setProcessor = (_schema, ctx, _json, _params) => {
|
|
4266
|
+
if (ctx.unrepresentable === "throw") throw new Error("Set cannot be represented in JSON Schema");
|
|
4267
|
+
};
|
|
4198
4268
|
var arrayProcessor = (schema, ctx, _json, params) => {
|
|
4199
4269
|
const json = _json;
|
|
4200
4270
|
const def = schema._zod.def;
|
|
@@ -4399,6 +4469,12 @@ var readonlyProcessor = (schema, ctx, json, params) => {
|
|
|
4399
4469
|
seen.ref = def.innerType;
|
|
4400
4470
|
json.readOnly = true;
|
|
4401
4471
|
};
|
|
4472
|
+
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
4473
|
+
const def = schema._zod.def;
|
|
4474
|
+
process$1(def.innerType, ctx, params);
|
|
4475
|
+
const seen = ctx.seen.get(schema);
|
|
4476
|
+
seen.ref = def.innerType;
|
|
4477
|
+
};
|
|
4402
4478
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
4403
4479
|
const def = schema._zod.def;
|
|
4404
4480
|
process$1(def.innerType, ctx, params);
|
|
@@ -4411,6 +4487,81 @@ var lazyProcessor = (schema, ctx, _json, params) => {
|
|
|
4411
4487
|
const seen = ctx.seen.get(schema);
|
|
4412
4488
|
seen.ref = innerType;
|
|
4413
4489
|
};
|
|
4490
|
+
var allProcessors = {
|
|
4491
|
+
string: stringProcessor,
|
|
4492
|
+
number: numberProcessor,
|
|
4493
|
+
boolean: booleanProcessor,
|
|
4494
|
+
bigint: bigintProcessor,
|
|
4495
|
+
symbol: symbolProcessor,
|
|
4496
|
+
null: nullProcessor,
|
|
4497
|
+
undefined: undefinedProcessor,
|
|
4498
|
+
void: voidProcessor,
|
|
4499
|
+
never: neverProcessor,
|
|
4500
|
+
any: anyProcessor,
|
|
4501
|
+
unknown: unknownProcessor,
|
|
4502
|
+
date: dateProcessor,
|
|
4503
|
+
enum: enumProcessor,
|
|
4504
|
+
literal: literalProcessor,
|
|
4505
|
+
nan: nanProcessor,
|
|
4506
|
+
template_literal: templateLiteralProcessor,
|
|
4507
|
+
file: fileProcessor,
|
|
4508
|
+
success: successProcessor,
|
|
4509
|
+
custom: customProcessor,
|
|
4510
|
+
function: functionProcessor,
|
|
4511
|
+
transform: transformProcessor,
|
|
4512
|
+
map: mapProcessor,
|
|
4513
|
+
set: setProcessor,
|
|
4514
|
+
array: arrayProcessor,
|
|
4515
|
+
object: objectProcessor,
|
|
4516
|
+
union: unionProcessor,
|
|
4517
|
+
intersection: intersectionProcessor,
|
|
4518
|
+
tuple: tupleProcessor,
|
|
4519
|
+
record: recordProcessor,
|
|
4520
|
+
nullable: nullableProcessor,
|
|
4521
|
+
nonoptional: nonoptionalProcessor,
|
|
4522
|
+
default: defaultProcessor,
|
|
4523
|
+
prefault: prefaultProcessor,
|
|
4524
|
+
catch: catchProcessor,
|
|
4525
|
+
pipe: pipeProcessor,
|
|
4526
|
+
readonly: readonlyProcessor,
|
|
4527
|
+
promise: promiseProcessor,
|
|
4528
|
+
optional: optionalProcessor,
|
|
4529
|
+
lazy: lazyProcessor
|
|
4530
|
+
};
|
|
4531
|
+
function toJSONSchema(input, params) {
|
|
4532
|
+
if ("_idmap" in input) {
|
|
4533
|
+
const registry = input;
|
|
4534
|
+
const ctx = initializeContext({
|
|
4535
|
+
...params,
|
|
4536
|
+
processors: allProcessors
|
|
4537
|
+
});
|
|
4538
|
+
const defs = {};
|
|
4539
|
+
for (const entry of registry._idmap.entries()) {
|
|
4540
|
+
const [_, schema] = entry;
|
|
4541
|
+
process$1(schema, ctx);
|
|
4542
|
+
}
|
|
4543
|
+
const schemas = {};
|
|
4544
|
+
ctx.external = {
|
|
4545
|
+
registry,
|
|
4546
|
+
uri: params?.uri,
|
|
4547
|
+
defs
|
|
4548
|
+
};
|
|
4549
|
+
for (const entry of registry._idmap.entries()) {
|
|
4550
|
+
const [key, schema] = entry;
|
|
4551
|
+
extractDefs(ctx, schema);
|
|
4552
|
+
schemas[key] = finalize(ctx, schema);
|
|
4553
|
+
}
|
|
4554
|
+
if (Object.keys(defs).length > 0) schemas.__shared = { [ctx.target === "draft-2020-12" ? "$defs" : "definitions"]: defs };
|
|
4555
|
+
return { schemas };
|
|
4556
|
+
}
|
|
4557
|
+
const ctx = initializeContext({
|
|
4558
|
+
...params,
|
|
4559
|
+
processors: allProcessors
|
|
4560
|
+
});
|
|
4561
|
+
process$1(input, ctx);
|
|
4562
|
+
extractDefs(ctx, input);
|
|
4563
|
+
return finalize(ctx, input);
|
|
4564
|
+
}
|
|
4414
4565
|
//#endregion
|
|
4415
4566
|
//#region ../../node_modules/zod/v4/classic/iso.js
|
|
4416
4567
|
var ZodISODateTime = /*@__PURE__*/ $constructor("ZodISODateTime", (inst, def) => {
|
|
@@ -5427,7 +5578,7 @@ var ZodIssueCode = {
|
|
|
5427
5578
|
var ZodFirstPartyTypeKind;
|
|
5428
5579
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5429
5580
|
//#endregion
|
|
5430
|
-
//#region ../types/dist/sleep-
|
|
5581
|
+
//#region ../types/dist/sleep-BegKjwUu.mjs
|
|
5431
5582
|
/**
|
|
5432
5583
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5433
5584
|
* window to float samples (D455).
|
|
@@ -5544,7 +5695,7 @@ function isKeychainStoredField(field) {
|
|
|
5544
5695
|
var SECRET_CLEAR = { $secretClear: true };
|
|
5545
5696
|
/** The `'***'` token D634 redaction writes for non-admins. */
|
|
5546
5697
|
var LEGACY_DEVICE_REDACTION = "***";
|
|
5547
|
-
function isRecord$
|
|
5698
|
+
function isRecord$5(value) {
|
|
5548
5699
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5549
5700
|
}
|
|
5550
5701
|
function makeSecretMarker(ref) {
|
|
@@ -5555,12 +5706,12 @@ function makeSecretMarker(ref) {
|
|
|
5555
5706
|
}
|
|
5556
5707
|
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
5557
5708
|
function isSecretMarker(value) {
|
|
5558
|
-
if (!isRecord$
|
|
5709
|
+
if (!isRecord$5(value)) return false;
|
|
5559
5710
|
const ref = value["$secret"];
|
|
5560
5711
|
return typeof ref === "string" && ref.length > 0;
|
|
5561
5712
|
}
|
|
5562
5713
|
function isSecretClear(value) {
|
|
5563
|
-
return isRecord$
|
|
5714
|
+
return isRecord$5(value) && value["$secretClear"] === true;
|
|
5564
5715
|
}
|
|
5565
5716
|
/**
|
|
5566
5717
|
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
@@ -6170,7 +6321,7 @@ var KeychainWriteRefusedError = class extends Error {
|
|
|
6170
6321
|
}
|
|
6171
6322
|
};
|
|
6172
6323
|
var KEEP = { kind: "keep" };
|
|
6173
|
-
function isRecord$
|
|
6324
|
+
function isRecord$4(value) {
|
|
6174
6325
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6175
6326
|
}
|
|
6176
6327
|
function defaultMintId() {
|
|
@@ -6183,7 +6334,7 @@ function collectSecretMarkerRefs(value, out = []) {
|
|
|
6183
6334
|
return out;
|
|
6184
6335
|
}
|
|
6185
6336
|
if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
|
|
6186
|
-
else if (isRecord$
|
|
6337
|
+
else if (isRecord$4(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
6187
6338
|
return out;
|
|
6188
6339
|
}
|
|
6189
6340
|
async function storedValueEquals(secrets, marker, candidate) {
|
|
@@ -6238,7 +6389,7 @@ function rowId(row, idKey) {
|
|
|
6238
6389
|
return null;
|
|
6239
6390
|
}
|
|
6240
6391
|
async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
6241
|
-
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$
|
|
6392
|
+
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$4) : [];
|
|
6242
6393
|
if (!Array.isArray(incoming)) {
|
|
6243
6394
|
ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
|
|
6244
6395
|
return {
|
|
@@ -6254,7 +6405,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
|
6254
6405
|
const kept = /* @__PURE__ */ new Set();
|
|
6255
6406
|
const rows = [];
|
|
6256
6407
|
for (const row of incoming) {
|
|
6257
|
-
if (!isRecord$
|
|
6408
|
+
if (!isRecord$4(row)) {
|
|
6258
6409
|
rows.push(row);
|
|
6259
6410
|
continue;
|
|
6260
6411
|
}
|
|
@@ -6279,7 +6430,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
6279
6430
|
case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
|
|
6280
6431
|
case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
|
|
6281
6432
|
case "object": {
|
|
6282
|
-
if (!isRecord$
|
|
6433
|
+
if (!isRecord$4(incoming)) {
|
|
6283
6434
|
if (incoming === void 0) return KEEP;
|
|
6284
6435
|
ctx.deletes.push(...collectSecretMarkerRefs(stored));
|
|
6285
6436
|
return {
|
|
@@ -6287,7 +6438,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
6287
6438
|
value: incoming
|
|
6288
6439
|
};
|
|
6289
6440
|
}
|
|
6290
|
-
const storedObj = isRecord$
|
|
6441
|
+
const storedObj = isRecord$4(stored) ? stored : {};
|
|
6291
6442
|
return {
|
|
6292
6443
|
kind: "value",
|
|
6293
6444
|
value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
|
|
@@ -6410,7 +6561,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
|
|
|
6410
6561
|
value: items
|
|
6411
6562
|
};
|
|
6412
6563
|
}
|
|
6413
|
-
if (isRecord$
|
|
6564
|
+
if (isRecord$4(v)) {
|
|
6414
6565
|
const out = {};
|
|
6415
6566
|
for (const [k, child] of Object.entries(v)) {
|
|
6416
6567
|
const s = substitute(child);
|
|
@@ -6442,7 +6593,7 @@ var SecretOverwriteRefusedError = class extends Error {
|
|
|
6442
6593
|
this.name = "SecretOverwriteRefusedError";
|
|
6443
6594
|
}
|
|
6444
6595
|
};
|
|
6445
|
-
function isRecord(value) {
|
|
6596
|
+
function isRecord$3(value) {
|
|
6446
6597
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6447
6598
|
}
|
|
6448
6599
|
function resolvedValue(pass, marker) {
|
|
@@ -6471,7 +6622,7 @@ function inArray(pass, incoming, stored) {
|
|
|
6471
6622
|
return incoming.map((item, index) => {
|
|
6472
6623
|
const twin = stored[index];
|
|
6473
6624
|
if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
|
|
6474
|
-
if (isRecord(item) && isRecord(twin)) return inArrayRow(pass, item, twin);
|
|
6625
|
+
if (isRecord$3(item) && isRecord$3(twin)) return inArrayRow(pass, item, twin);
|
|
6475
6626
|
if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
|
|
6476
6627
|
return item;
|
|
6477
6628
|
});
|
|
@@ -6482,7 +6633,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
6482
6633
|
const twin = stored[key];
|
|
6483
6634
|
if (isSecretMarker(twin)) {
|
|
6484
6635
|
if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
|
|
6485
|
-
} else if (isRecord(value) && isRecord(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
6636
|
+
} else if (isRecord$3(value) && isRecord$3(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
6486
6637
|
else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
|
|
6487
6638
|
}
|
|
6488
6639
|
return out;
|
|
@@ -6490,7 +6641,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
6490
6641
|
async function walk(pass, incoming, stored) {
|
|
6491
6642
|
if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
|
|
6492
6643
|
if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
|
|
6493
|
-
if (!isRecord(incoming) || !isRecord(stored)) return incoming;
|
|
6644
|
+
if (!isRecord$3(incoming) || !isRecord$3(stored)) return incoming;
|
|
6494
6645
|
const out = { ...incoming };
|
|
6495
6646
|
for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
|
|
6496
6647
|
return out;
|
|
@@ -6521,7 +6672,7 @@ async function restoreUnchangedSecrets(input) {
|
|
|
6521
6672
|
function isJsonEqual(a, b) {
|
|
6522
6673
|
if (Object.is(a, b)) return true;
|
|
6523
6674
|
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
|
|
6524
|
-
if (isRecord(a) && isRecord(b)) {
|
|
6675
|
+
if (isRecord$3(a) && isRecord$3(b)) {
|
|
6525
6676
|
const keys = Object.keys(a);
|
|
6526
6677
|
if (keys.length !== Object.keys(b).length) return false;
|
|
6527
6678
|
return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
|
|
@@ -6545,7 +6696,7 @@ function markerKeysOf(blob, owner) {
|
|
|
6545
6696
|
}
|
|
6546
6697
|
function nodeFromStored(value, owner) {
|
|
6547
6698
|
if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
|
|
6548
|
-
if (!isRecord(value)) return null;
|
|
6699
|
+
if (!isRecord$3(value)) return null;
|
|
6549
6700
|
const children = /* @__PURE__ */ new Map();
|
|
6550
6701
|
for (const [key, child] of Object.entries(value)) {
|
|
6551
6702
|
const node = nodeFromStored(child, owner);
|
|
@@ -6678,7 +6829,6 @@ function createHandle(raw, opts) {
|
|
|
6678
6829
|
const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
|
|
6679
6830
|
const readAddonStoreResult = raw.readAddonStoreResult;
|
|
6680
6831
|
const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
|
|
6681
|
-
const readDeviceStoreBatch = raw.readDeviceStoreBatch;
|
|
6682
6832
|
const overrides = {
|
|
6683
6833
|
readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
|
|
6684
6834
|
...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
|
|
@@ -6702,12 +6852,12 @@ function createHandle(raw, opts) {
|
|
|
6702
6852
|
};
|
|
6703
6853
|
} } : {},
|
|
6704
6854
|
readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
|
|
6705
|
-
|
|
6706
|
-
const rows = await readDeviceStoreBatch
|
|
6855
|
+
readDeviceStoreBatch: async (deviceIds) => {
|
|
6856
|
+
const rows = await raw.readDeviceStoreBatch(deviceIds);
|
|
6707
6857
|
const out = /* @__PURE__ */ new Map();
|
|
6708
6858
|
for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
|
|
6709
6859
|
return out;
|
|
6710
|
-
}
|
|
6860
|
+
},
|
|
6711
6861
|
writeAddonStore: async (patch) => {
|
|
6712
6862
|
await writeAddon(patch);
|
|
6713
6863
|
},
|
|
@@ -7334,7 +7484,7 @@ var BaseAddon = class {
|
|
|
7334
7484
|
if (!ctx) return;
|
|
7335
7485
|
const addonId = ctx.id;
|
|
7336
7486
|
setImmediate(() => {
|
|
7337
|
-
ctx.api.addons
|
|
7487
|
+
ctx.api.addons.restartAddon.mutate({ addonId }).then(() => {
|
|
7338
7488
|
ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: patchKeys.filter((k) => restartKeys.has(k)) } });
|
|
7339
7489
|
}).catch((err) => {
|
|
7340
7490
|
ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
@@ -7534,7 +7684,7 @@ var BaseAddon = class {
|
|
|
7534
7684
|
return this._addonSecretTree;
|
|
7535
7685
|
}
|
|
7536
7686
|
deviceSecretTree() {
|
|
7537
|
-
this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
|
|
7687
|
+
this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
|
|
7538
7688
|
return this._deviceSecretTree;
|
|
7539
7689
|
}
|
|
7540
7690
|
/**
|
|
@@ -7610,42 +7760,135 @@ var BaseAddon = class {
|
|
|
7610
7760
|
this[timerOf] = timer;
|
|
7611
7761
|
}
|
|
7612
7762
|
/**
|
|
7613
|
-
* Wrap `ctx.settings.readAddonStore()` with a
|
|
7614
|
-
*
|
|
7615
|
-
* swap, or any race where the SqliteSettingsBackend is between shutdown and
|
|
7616
|
-
* re-initialize) doesn't propagate up into `initialize()` and leave the
|
|
7763
|
+
* Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
|
|
7764
|
+
* settings-store outage doesn't propagate up into `initialize()` and leave the
|
|
7617
7765
|
* addon permanently broken.
|
|
7618
7766
|
*
|
|
7619
|
-
*
|
|
7620
|
-
*
|
|
7621
|
-
* `
|
|
7767
|
+
* Two kinds of outage, two budgets:
|
|
7768
|
+
* - the store's own restart fingerprints (`SqliteSettingsBackend not
|
|
7769
|
+
* initialized`, `provider not available`): a short budget, then the addon
|
|
7770
|
+
* falls back to `{}` (defaults) - its first successful patch rehydrates;
|
|
7771
|
+
* - a TRANSPORT failure (the hub is restarting while this runner boots:
|
|
7772
|
+
* `discovery deadline exceeded`, `parent could not route`, a refused or
|
|
7773
|
+
* timed-out connection). Since D870 a runner's settings read THROWS instead
|
|
7774
|
+
* of answering `{}`, so without a budget a hub restart ends in a respawn
|
|
7775
|
+
* loop. A long, bounded budget; only after it the error propagates - the
|
|
7776
|
+
* addon never runs on defaults for a store it could not ask.
|
|
7777
|
+
* Anything else propagates immediately so real bugs surface.
|
|
7622
7778
|
*/
|
|
7623
7779
|
async readAddonStoreWithRetry() {
|
|
7624
7780
|
const settings = this.rawSettings();
|
|
7625
7781
|
if (!settings) return {};
|
|
7626
|
-
const delaysMs = [
|
|
7627
|
-
150,
|
|
7628
|
-
350,
|
|
7629
|
-
600,
|
|
7630
|
-
900
|
|
7631
|
-
];
|
|
7632
7782
|
let lastErr;
|
|
7633
|
-
for (let attempt = 0
|
|
7783
|
+
for (let attempt = 0;; attempt++) try {
|
|
7634
7784
|
const stored = await settings.readAddonStore() ?? {};
|
|
7635
7785
|
this.settingsStoreReady = true;
|
|
7636
7786
|
return stored;
|
|
7637
7787
|
} catch (err) {
|
|
7638
7788
|
lastErr = err;
|
|
7639
|
-
const
|
|
7640
|
-
if (
|
|
7641
|
-
|
|
7642
|
-
|
|
7789
|
+
const kind = classifyStoreError(err);
|
|
7790
|
+
if (kind === "permanent") throw err;
|
|
7791
|
+
const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
|
|
7792
|
+
if (delay === void 0) {
|
|
7793
|
+
if (kind === "transport") throw err;
|
|
7794
|
+
break;
|
|
7795
|
+
}
|
|
7796
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
7643
7797
|
}
|
|
7644
7798
|
this.settingsStoreReady = false;
|
|
7645
7799
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
7646
7800
|
return {};
|
|
7647
7801
|
}
|
|
7648
7802
|
};
|
|
7803
|
+
/** The settings store's own restart fingerprints. */
|
|
7804
|
+
var RESTART_RETRY_DELAYS_MS = [
|
|
7805
|
+
150,
|
|
7806
|
+
350,
|
|
7807
|
+
600,
|
|
7808
|
+
900
|
|
7809
|
+
];
|
|
7810
|
+
/** A hub restarting under a booting runner: ~100 s in total. */
|
|
7811
|
+
var TRANSPORT_RETRY_DELAYS_MS = [
|
|
7812
|
+
150,
|
|
7813
|
+
350,
|
|
7814
|
+
600,
|
|
7815
|
+
900,
|
|
7816
|
+
2e3,
|
|
7817
|
+
5e3,
|
|
7818
|
+
1e4,
|
|
7819
|
+
2e4,
|
|
7820
|
+
3e4,
|
|
7821
|
+
3e4
|
|
7822
|
+
];
|
|
7823
|
+
var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
|
|
7824
|
+
/** Node / socket error codes of a connection that did not happen or broke. */
|
|
7825
|
+
var TRANSPORT_CODES = new Set([
|
|
7826
|
+
"ECONNREFUSED",
|
|
7827
|
+
"ECONNRESET",
|
|
7828
|
+
"ETIMEDOUT",
|
|
7829
|
+
"EPIPE",
|
|
7830
|
+
"EHOSTUNREACH",
|
|
7831
|
+
"ENETUNREACH"
|
|
7832
|
+
]);
|
|
7833
|
+
/** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
|
|
7834
|
+
var UNAVAILABLE_TRPC_CODES = new Set([
|
|
7835
|
+
"TIMEOUT",
|
|
7836
|
+
"SERVICE_UNAVAILABLE",
|
|
7837
|
+
"BAD_GATEWAY",
|
|
7838
|
+
"GATEWAY_TIMEOUT"
|
|
7839
|
+
]);
|
|
7840
|
+
/** The exact phrases OUR transport raises when it cannot reach the hub. */
|
|
7841
|
+
var TRANSPORT_PHRASES = [
|
|
7842
|
+
"discovery deadline exceeded",
|
|
7843
|
+
"parent could not route child unowned cap call",
|
|
7844
|
+
"UDS request timed out after",
|
|
7845
|
+
"channel closed"
|
|
7846
|
+
];
|
|
7847
|
+
/** Moleculer error `type`s of a call that could not be served right now. */
|
|
7848
|
+
var MOLECULER_TRANSIENT_TYPES = new Set([
|
|
7849
|
+
"REQUEST_TIMEOUT",
|
|
7850
|
+
"SERVICE_NOT_FOUND",
|
|
7851
|
+
"SERVICE_NOT_AVAILABLE",
|
|
7852
|
+
"REQUEST_REJECTED",
|
|
7853
|
+
"BROKER_DISCONNECTED"
|
|
7854
|
+
]);
|
|
7855
|
+
function stringProp(value, key) {
|
|
7856
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
7857
|
+
const found = Reflect.get(value, key);
|
|
7858
|
+
return typeof found === "string" ? found : void 0;
|
|
7859
|
+
}
|
|
7860
|
+
/** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
|
|
7861
|
+
function errorCode(err) {
|
|
7862
|
+
const out = {};
|
|
7863
|
+
const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
|
|
7864
|
+
const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
|
|
7865
|
+
const node = stringProp(err, "code") ?? stringProp(cause, "code");
|
|
7866
|
+
if (node !== void 0) out.node = node;
|
|
7867
|
+
const trpc = stringProp(data, "code");
|
|
7868
|
+
if (trpc !== void 0) out.trpc = trpc;
|
|
7869
|
+
return out;
|
|
7870
|
+
}
|
|
7871
|
+
/**
|
|
7872
|
+
* Is a settings read failure worth retrying, and with which budget? Only
|
|
7873
|
+
* EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
|
|
7874
|
+
* one of the exact phrases our own transport raises. A message that merely
|
|
7875
|
+
* contains "timed out" or "unreachable" is not evidence of anything, and
|
|
7876
|
+
* `settings store not wired` is a wiring bug that retrying cannot fix.
|
|
7877
|
+
*/
|
|
7878
|
+
function classifyStoreError(err) {
|
|
7879
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7880
|
+
if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
|
|
7881
|
+
const code = errorCode(err);
|
|
7882
|
+
if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
|
|
7883
|
+
if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
|
|
7884
|
+
if (typeof err === "object" && err !== null) {
|
|
7885
|
+
if (Reflect.get(err, "retryable") === true) return "transport";
|
|
7886
|
+
const type = stringProp(err, "type");
|
|
7887
|
+
if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
|
|
7888
|
+
}
|
|
7889
|
+
if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
|
|
7890
|
+
return "permanent";
|
|
7891
|
+
}
|
|
7649
7892
|
/**
|
|
7650
7893
|
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
7651
7894
|
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
@@ -7735,6 +7978,365 @@ var PeerBytesTicketSchema = object({
|
|
|
7735
7978
|
*/
|
|
7736
7979
|
declaredBytes: number().int().nonnegative().nullable()
|
|
7737
7980
|
});
|
|
7981
|
+
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
7982
|
+
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
7983
|
+
var RECORD_DATABASES = ["settings", "events"];
|
|
7984
|
+
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
7985
|
+
var RECORD_CLASSES = [
|
|
7986
|
+
"config",
|
|
7987
|
+
"ledger",
|
|
7988
|
+
"audit",
|
|
7989
|
+
"mirror",
|
|
7990
|
+
"ephemeral"
|
|
7991
|
+
];
|
|
7992
|
+
var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
|
|
7993
|
+
var RECORD_MAX_DATA_BYTES = 1024 * 1024;
|
|
7994
|
+
var RECORD_BATCH_MAX = 1e3;
|
|
7995
|
+
var RECORD_QUERY_MAX = 1e3;
|
|
7996
|
+
/** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
|
|
7997
|
+
var RECORD_SETTINGS_MAX_ROWS = 5e4;
|
|
7998
|
+
var PREFIX = "records/";
|
|
7999
|
+
/** The base of every records failure. `detail` is the human part of the message. */
|
|
8000
|
+
var RecordsError = class extends Error {
|
|
8001
|
+
code;
|
|
8002
|
+
detail;
|
|
8003
|
+
constructor(code, detail) {
|
|
8004
|
+
super(`${PREFIX}${code}: ${detail}`);
|
|
8005
|
+
this.name = "RecordsError";
|
|
8006
|
+
this.code = code;
|
|
8007
|
+
this.detail = detail;
|
|
8008
|
+
}
|
|
8009
|
+
};
|
|
8010
|
+
/** The keychain could not store or serve a value: explicit, never an empty string. */
|
|
8011
|
+
var RecordSecretUnavailableError = class extends RecordsError {
|
|
8012
|
+
constructor(detail) {
|
|
8013
|
+
super("secret-unavailable", detail);
|
|
8014
|
+
this.name = "RecordSecretUnavailableError";
|
|
8015
|
+
}
|
|
8016
|
+
};
|
|
8017
|
+
var InvalidRecordError = class extends RecordsError {
|
|
8018
|
+
constructor(detail) {
|
|
8019
|
+
super("invalid-record", detail);
|
|
8020
|
+
this.name = "InvalidRecordError";
|
|
8021
|
+
}
|
|
8022
|
+
};
|
|
8023
|
+
/** A kind declaration the engine refuses (non-additive change, bad shape, wrong file). */
|
|
8024
|
+
var DeclarationRefusedError = class extends RecordsError {
|
|
8025
|
+
constructor(detail) {
|
|
8026
|
+
super("declaration-refused", detail);
|
|
8027
|
+
this.name = "DeclarationRefusedError";
|
|
8028
|
+
}
|
|
8029
|
+
};
|
|
8030
|
+
function defOf(schema) {
|
|
8031
|
+
const zodBag = Reflect.get(schema, "_zod");
|
|
8032
|
+
if (typeof zodBag !== "object" || zodBag === null) return {};
|
|
8033
|
+
const def = Reflect.get(zodBag, "def");
|
|
8034
|
+
if (typeof def !== "object" || def === null) return {};
|
|
8035
|
+
return def;
|
|
8036
|
+
}
|
|
8037
|
+
function declaredHere(schema) {
|
|
8038
|
+
return globalRegistry.get(schema)?.secret === true;
|
|
8039
|
+
}
|
|
8040
|
+
/** Wrapper kinds whose `innerType` is the field itself. */
|
|
8041
|
+
var WRAPPERS = new Set([
|
|
8042
|
+
"optional",
|
|
8043
|
+
"nullable",
|
|
8044
|
+
"default",
|
|
8045
|
+
"prefault",
|
|
8046
|
+
"catch",
|
|
8047
|
+
"readonly",
|
|
8048
|
+
"nonoptional"
|
|
8049
|
+
]);
|
|
8050
|
+
/**
|
|
8051
|
+
* The schema layers that make up ONE field: the schema itself and every
|
|
8052
|
+
* wrapper below it (optional / default / nullable / readonly / catch), plus
|
|
8053
|
+
* both sides of a pipe (`z.preprocess` puts the real schema on `out`,
|
|
8054
|
+
* `.transform` on `in`).
|
|
8055
|
+
*/
|
|
8056
|
+
function layersOf(schema) {
|
|
8057
|
+
const out = [];
|
|
8058
|
+
const queue = [schema];
|
|
8059
|
+
while (queue.length > 0) {
|
|
8060
|
+
const s = queue.shift();
|
|
8061
|
+
if (!s || out.includes(s)) continue;
|
|
8062
|
+
out.push(s);
|
|
8063
|
+
const def = defOf(s);
|
|
8064
|
+
if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
|
|
8065
|
+
if (def.type === "pipe") {
|
|
8066
|
+
if (def.in) queue.push(def.in);
|
|
8067
|
+
if (def.out) queue.push(def.out);
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
return out;
|
|
8071
|
+
}
|
|
8072
|
+
/** True when any layer of this field declares `.meta({ secret: true })`. */
|
|
8073
|
+
function isZodSecret(schema) {
|
|
8074
|
+
return layersOf(schema).some(declaredHere);
|
|
8075
|
+
}
|
|
8076
|
+
/**
|
|
8077
|
+
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
8078
|
+
* element of this array" (`connection.password`, `rows[].token`). A declared
|
|
8079
|
+
* object or array is reported as one path and not descended into — everything
|
|
8080
|
+
* under it is secret.
|
|
8081
|
+
*/
|
|
8082
|
+
function collectZodSecretPaths(schema, prefix = "") {
|
|
8083
|
+
if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
|
|
8084
|
+
const paths = [];
|
|
8085
|
+
for (const layer of layersOf(schema)) {
|
|
8086
|
+
const def = defOf(layer);
|
|
8087
|
+
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
8088
|
+
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
8089
|
+
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
8090
|
+
else if (def.type === "intersection") {
|
|
8091
|
+
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
8092
|
+
if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
8095
|
+
return [...new Set(paths)];
|
|
8096
|
+
}
|
|
8097
|
+
/**
|
|
8098
|
+
* Secret fields of a record, addressed the way a Zod schema declares them
|
|
8099
|
+
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8100
|
+
* (`auth.accessToken`, `headers[].value`).
|
|
8101
|
+
*
|
|
8102
|
+
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8103
|
+
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8104
|
+
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
8105
|
+
* leaf names its element: `headers[X-Api].value`.
|
|
8106
|
+
*
|
|
8107
|
+
* Pure and browser-safe. Nothing here mutates: every transform returns a new value.
|
|
8108
|
+
*/
|
|
8109
|
+
/** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
|
|
8110
|
+
function parseSecretPath(path) {
|
|
8111
|
+
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8112
|
+
return path.split(".").map((raw) => {
|
|
8113
|
+
const each = raw.endsWith("[]");
|
|
8114
|
+
const key = each ? raw.slice(0, -2) : raw;
|
|
8115
|
+
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8116
|
+
return {
|
|
8117
|
+
key,
|
|
8118
|
+
each
|
|
8119
|
+
};
|
|
8120
|
+
});
|
|
8121
|
+
}
|
|
8122
|
+
/** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
|
|
8123
|
+
function secretArrayPrefixes(path) {
|
|
8124
|
+
const segments = parseSecretPath(path);
|
|
8125
|
+
const prefixes = [];
|
|
8126
|
+
const walked = [];
|
|
8127
|
+
for (const segment of segments) {
|
|
8128
|
+
walked.push(segment.key);
|
|
8129
|
+
if (segment.each) prefixes.push(walked.join("."));
|
|
8130
|
+
}
|
|
8131
|
+
return prefixes;
|
|
8132
|
+
}
|
|
8133
|
+
/**
|
|
8134
|
+
* Record kinds (D881): the declaration an addon makes of one kind of fact, the
|
|
8135
|
+
* Zod row schema that describes it, and the helper that turns the two into the
|
|
8136
|
+
* JSON the engine stores.
|
|
8137
|
+
*
|
|
8138
|
+
* A kind is declared ONCE, next to the code that owns it, and the engine's
|
|
8139
|
+
* reconciler creates its indexes - nothing creates DDL at runtime from a write.
|
|
8140
|
+
* Everything the generic machinery needs (secret fields, the entry descriptor)
|
|
8141
|
+
* is DERIVED from the Zod schema: `.meta({ secret: true })` marks a credential,
|
|
8142
|
+
* never a field name (D879).
|
|
8143
|
+
*/
|
|
8144
|
+
/** A JSON field path: `trackId`, `auth.accessToken`. Charset-restricted: it is rendered into DDL. */
|
|
8145
|
+
var RecordFieldPathSchema = string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}(\.[A-Za-z_][A-Za-z0-9_]{0,63}){0,3}$/);
|
|
8146
|
+
var RecordIndexSchema = object({
|
|
8147
|
+
name: string().regex(/^[a-z][a-z0-9_]{0,31}$/),
|
|
8148
|
+
fields: array(RecordFieldPathSchema).min(1).max(4),
|
|
8149
|
+
unique: boolean().optional()
|
|
8150
|
+
}).strict();
|
|
8151
|
+
var RecordRetentionSchema = union([
|
|
8152
|
+
object({
|
|
8153
|
+
ttlMs: number().int().positive(),
|
|
8154
|
+
from: _enum(["at", "write"])
|
|
8155
|
+
}).strict(),
|
|
8156
|
+
object({
|
|
8157
|
+
maxRows: number().int().positive().optional(),
|
|
8158
|
+
maxRowsPerDevice: number().int().positive().optional()
|
|
8159
|
+
}).strict().refine((r) => r.maxRows !== void 0 || r.maxRowsPerDevice !== void 0, { message: "name maxRows, maxRowsPerDevice or both" }),
|
|
8160
|
+
object({
|
|
8161
|
+
owner: string().min(1),
|
|
8162
|
+
reason: string().min(1)
|
|
8163
|
+
}).strict(),
|
|
8164
|
+
object({ none: string().min(1) }).strict()
|
|
8165
|
+
]);
|
|
8166
|
+
/** The descriptor a generic entries UI reads (D881 "generic entries"). */
|
|
8167
|
+
var EntryDescriptorSchema = object({
|
|
8168
|
+
title: string().min(1),
|
|
8169
|
+
/** The record body field shown as the entry's name. */
|
|
8170
|
+
nameField: string().min(1),
|
|
8171
|
+
/** JSON Schema of the entry body; `secret: true` marks a password field. */
|
|
8172
|
+
fields: record(string(), unknown())
|
|
8173
|
+
}).strict();
|
|
8174
|
+
/** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
|
|
8175
|
+
var RecordKindDeclarationSchema = object({
|
|
8176
|
+
database: _enum(RECORD_DATABASES).default("settings"),
|
|
8177
|
+
class: _enum(RECORD_CLASSES),
|
|
8178
|
+
writeTrigger: string().min(1),
|
|
8179
|
+
device: _enum([
|
|
8180
|
+
"required",
|
|
8181
|
+
"optional",
|
|
8182
|
+
"none"
|
|
8183
|
+
]).default("none"),
|
|
8184
|
+
at: _enum(["required", "none"]).default("none"),
|
|
8185
|
+
indexes: array(RecordIndexSchema).max(8).default([]),
|
|
8186
|
+
retention: RecordRetentionSchema,
|
|
8187
|
+
deviceRemoval: _enum(["delete", "none"]).default("none"),
|
|
8188
|
+
maxDataBytes: number().int().positive().max(RECORD_MAX_DATA_BYTES).default(RECORD_DEFAULT_MAX_DATA_BYTES),
|
|
8189
|
+
/** Derived from the Zod schema; never written by hand. */
|
|
8190
|
+
secretPaths: array(string().min(1)).default([]),
|
|
8191
|
+
/** The id key of each array that carries a secret (`{ headers: 'name' }`). */
|
|
8192
|
+
secretArrayKeys: record(string(), string()).default({}),
|
|
8193
|
+
/** The legacy table this kind adopts from; the boot guard reads it (D853: no migration code). */
|
|
8194
|
+
adoptsFrom: string().min(1).optional(),
|
|
8195
|
+
/** Kinds that must agree through `transact` sit in one file under one family name. */
|
|
8196
|
+
transactFamily: string().min(1).optional(),
|
|
8197
|
+
entry: EntryDescriptorSchema.optional()
|
|
8198
|
+
}).strict();
|
|
8199
|
+
/** The kind names: lower-case, dash separated, short enough for an index name. */
|
|
8200
|
+
var RecordKindNameSchema = string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
8201
|
+
/** Cross-field rules a declaration must satisfy. Throws {@link DeclarationRefusedError}. */
|
|
8202
|
+
function assertValidDeclaration(name, decl) {
|
|
8203
|
+
const refuse = (why) => {
|
|
8204
|
+
throw new DeclarationRefusedError(`kind "${name}": ${why}`);
|
|
8205
|
+
};
|
|
8206
|
+
if (!RecordKindNameSchema.safeParse(name).success) refuse("the name must be lower-case-dashed");
|
|
8207
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8208
|
+
for (const index of decl.indexes) {
|
|
8209
|
+
if (seen.has(index.name)) refuse(`index name "${index.name}" is declared twice`);
|
|
8210
|
+
seen.add(index.name);
|
|
8211
|
+
}
|
|
8212
|
+
const { retention } = decl;
|
|
8213
|
+
const hasSecrets = decl.secretPaths.length > 0;
|
|
8214
|
+
if ("ttlMs" in retention) {
|
|
8215
|
+
if (retention.from === "at" && decl.at !== "required") refuse("a ttl from \"at\" needs at: \"required\"");
|
|
8216
|
+
if (hasSecrets) refuse("a kind with secret fields cannot use engine TTL (its keychain values would be orphaned)");
|
|
8217
|
+
}
|
|
8218
|
+
if ("maxRows" in retention || "maxRowsPerDevice" in retention) {
|
|
8219
|
+
if (retention.maxRowsPerDevice !== void 0 && decl.device !== "required") refuse("maxRowsPerDevice needs device: \"required\"");
|
|
8220
|
+
if (decl.at !== "required") refuse("a row bound trims the oldest \"at\": it needs at: \"required\"");
|
|
8221
|
+
if (hasSecrets) refuse("a kind with secret fields cannot use an engine row bound");
|
|
8222
|
+
const bound = Math.max(retention.maxRows ?? 0, retention.maxRowsPerDevice ?? 0);
|
|
8223
|
+
if (decl.database === "settings" && bound > 5e4) refuse(`a row bound above ${RECORD_SETTINGS_MAX_ROWS} belongs in database: "events" (a big delete on the settings thread stalls every configuration read)`);
|
|
8224
|
+
}
|
|
8225
|
+
if (decl.deviceRemoval === "delete" && decl.device === "none") refuse("deviceRemoval: \"delete\" needs a device: \"required\" or \"optional\"");
|
|
8226
|
+
for (const path of decl.secretPaths) for (const prefix of secretArrayPrefixes(path)) if (decl.secretArrayKeys[prefix] === void 0) refuse(`secret path "${path}" crosses the array "${prefix}": declare its id key in secretArrayKeys`);
|
|
8227
|
+
}
|
|
8228
|
+
function bodyFieldsOf(schema) {
|
|
8229
|
+
return { ...toJSONSchema(schema, {
|
|
8230
|
+
unrepresentable: "any",
|
|
8231
|
+
io: "input"
|
|
8232
|
+
}) };
|
|
8233
|
+
}
|
|
8234
|
+
function isRecordLike(value) {
|
|
8235
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8236
|
+
}
|
|
8237
|
+
/** Does every branch of the body schema carry `field`? (A discriminated union has several.) */
|
|
8238
|
+
function hasNameField(json, field) {
|
|
8239
|
+
const branches = json["oneOf"] ?? json["anyOf"];
|
|
8240
|
+
if (Array.isArray(branches)) return branches.length > 0 && branches.every((b) => isRecordLike(b) && hasNameField(b, field));
|
|
8241
|
+
const properties = json["properties"];
|
|
8242
|
+
return isRecordLike(properties) && Object.prototype.hasOwnProperty.call(properties, field);
|
|
8243
|
+
}
|
|
8244
|
+
/**
|
|
8245
|
+
* Declare the kinds of a package. The keys are the kind names. The result is
|
|
8246
|
+
* what `ctx.records.declare` sends to the engine and what `ctx.records.kind`
|
|
8247
|
+
* types its handle from.
|
|
8248
|
+
*/
|
|
8249
|
+
function defineRecordKinds(specs) {
|
|
8250
|
+
const out = {};
|
|
8251
|
+
for (const [name, spec] of Object.entries(specs)) {
|
|
8252
|
+
const secretPaths = collectZodSecretPaths(spec.schema);
|
|
8253
|
+
const nameField = spec.entry?.nameField ?? "name";
|
|
8254
|
+
const fields = bodyFieldsOf(spec.schema);
|
|
8255
|
+
if (spec.entry !== void 0 && !hasNameField(fields, nameField)) throw new DeclarationRefusedError(`kind "${name}": an entry kind's schema needs a "${nameField}" field`);
|
|
8256
|
+
const parsed = RecordKindDeclarationSchema.parse({
|
|
8257
|
+
database: spec.database,
|
|
8258
|
+
class: spec.class,
|
|
8259
|
+
writeTrigger: spec.writeTrigger,
|
|
8260
|
+
device: spec.device,
|
|
8261
|
+
at: spec.at,
|
|
8262
|
+
indexes: spec.indexes === void 0 ? void 0 : [...spec.indexes],
|
|
8263
|
+
retention: spec.retention,
|
|
8264
|
+
deviceRemoval: spec.deviceRemoval,
|
|
8265
|
+
maxDataBytes: spec.maxDataBytes,
|
|
8266
|
+
secretPaths: [...secretPaths],
|
|
8267
|
+
secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
|
|
8268
|
+
adoptsFrom: spec.adoptsFrom,
|
|
8269
|
+
transactFamily: spec.transactFamily,
|
|
8270
|
+
entry: spec.entry === void 0 ? void 0 : {
|
|
8271
|
+
title: spec.entry.title,
|
|
8272
|
+
nameField,
|
|
8273
|
+
fields
|
|
8274
|
+
}
|
|
8275
|
+
});
|
|
8276
|
+
assertValidDeclaration(name, parsed);
|
|
8277
|
+
out[name] = {
|
|
8278
|
+
name,
|
|
8279
|
+
schema: spec.schema,
|
|
8280
|
+
declaration: parsed
|
|
8281
|
+
};
|
|
8282
|
+
}
|
|
8283
|
+
return out;
|
|
8284
|
+
}
|
|
8285
|
+
/**
|
|
8286
|
+
* Entry kinds (D881): every list of user-created entries (notification
|
|
8287
|
+
* targets, brokers, LLM profiles) is ONE record kind with an id, a name and a
|
|
8288
|
+
* kind-specific body, and the admin UI lists/adds/edits/deletes all of them
|
|
8289
|
+
* through the `record-entries` capability with password fields derived from
|
|
8290
|
+
* the schema (`.meta({ secret: true })`).
|
|
8291
|
+
*/
|
|
8292
|
+
/** The body of a plain entry: a name and the fields the kind adds. */
|
|
8293
|
+
function entryBody(shape) {
|
|
8294
|
+
return object({
|
|
8295
|
+
name: string().min(1),
|
|
8296
|
+
...shape
|
|
8297
|
+
});
|
|
8298
|
+
}
|
|
8299
|
+
/**
|
|
8300
|
+
* Declare one entry kind. An entry is configuration: never trimmed by the
|
|
8301
|
+
* engine, never aged, belonging to no device.
|
|
8302
|
+
*/
|
|
8303
|
+
function defineEntryKind(name, spec) {
|
|
8304
|
+
const defined = defineRecordKinds({ [name]: {
|
|
8305
|
+
schema: spec.schema,
|
|
8306
|
+
class: "config",
|
|
8307
|
+
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
8308
|
+
retention: { none: "an entry goes only when the operator deletes it" },
|
|
8309
|
+
...spec.database === void 0 ? {} : { database: spec.database },
|
|
8310
|
+
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
8311
|
+
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
8312
|
+
...spec.adoptsFrom === void 0 ? {} : { adoptsFrom: spec.adoptsFrom },
|
|
8313
|
+
entry: {
|
|
8314
|
+
title: spec.title,
|
|
8315
|
+
...spec.nameField === void 0 ? {} : { nameField: spec.nameField }
|
|
8316
|
+
}
|
|
8317
|
+
} })[name];
|
|
8318
|
+
if (defined === void 0) throw new DeclarationRefusedError(`entry kind "${name}" was not built`);
|
|
8319
|
+
return defined;
|
|
8320
|
+
}
|
|
8321
|
+
/**
|
|
8322
|
+
* Legacy source for broker entry adoption (B2): brokers live in addon-settings
|
|
8323
|
+
* as one row `<addonId>.brokers` whose value is an array until records-adopt
|
|
8324
|
+
* copies them into the entry kind.
|
|
8325
|
+
*/
|
|
8326
|
+
var SETTINGS_BROKERS_ADOPTS_PREFIX = "@settings-brokers/";
|
|
8327
|
+
/** The `adoptsFrom` value for an addon's broker entry kind. */
|
|
8328
|
+
function settingsBrokersAdoptsFrom(addonId) {
|
|
8329
|
+
return `${SETTINGS_BROKERS_ADOPTS_PREFIX}${addonId}`;
|
|
8330
|
+
}
|
|
8331
|
+
/**
|
|
8332
|
+
* Legacy source for entry adoption (B3): a list stored under addon-settings
|
|
8333
|
+
* `<addonId>.<key>` until records-adopt copies rows into the entry kind.
|
|
8334
|
+
*/
|
|
8335
|
+
var SETTINGS_ARRAY_ADOPTS_PREFIX = "@settings-array/";
|
|
8336
|
+
/** The `adoptsFrom` value for an addon-settings array field. */
|
|
8337
|
+
function settingsArrayAdoptsFrom(addonId, settingsKey) {
|
|
8338
|
+
return `${SETTINGS_ARRAY_ADOPTS_PREFIX}${addonId}/${settingsKey}`;
|
|
8339
|
+
}
|
|
7738
8340
|
/** Shared Zod schemas used across streaming capabilities. */
|
|
7739
8341
|
var CamProfileSchema = _enum([
|
|
7740
8342
|
"high",
|
|
@@ -8200,7 +8802,7 @@ object({
|
|
|
8200
8802
|
params: record(string(), union([string(), number()])).optional()
|
|
8201
8803
|
});
|
|
8202
8804
|
//#endregion
|
|
8203
|
-
//#region ../types/dist/err-msg-
|
|
8805
|
+
//#region ../types/dist/err-msg-BXKzRBwg.mjs
|
|
8204
8806
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
8205
8807
|
DeviceType["Camera"] = "camera";
|
|
8206
8808
|
DeviceType["Hub"] = "hub";
|
|
@@ -8292,6 +8894,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
8292
8894
|
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
8293
8895
|
* integrations sharing the same food/desiccant/hopper surface. */
|
|
8294
8896
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
8897
|
+
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
8898
|
+
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
8899
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations. */
|
|
8900
|
+
DeviceType["Dishwasher"] = "dishwasher";
|
|
8295
8901
|
return DeviceType;
|
|
8296
8902
|
}({});
|
|
8297
8903
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -8469,6 +9075,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
8469
9075
|
/** Virtual HA toggle (input_boolean.*) — distinguishable from a
|
|
8470
9076
|
* real Switch device for UI rendering / export adapters. */
|
|
8471
9077
|
DeviceRole["BinaryHelper"] = "binary-helper";
|
|
9078
|
+
/** A smart plug: a `switch` whose source says it is an outlet (HA
|
|
9079
|
+
* `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
|
|
9080
|
+
DeviceRole["Outlet"] = "outlet";
|
|
8472
9081
|
/** Generic motion / occupancy / moving event source. Distinct from
|
|
8473
9082
|
* the camera accessory PirSensor role: that one is a camera child;
|
|
8474
9083
|
* this is a standalone HA / 3rd-party motion sensor. */
|
|
@@ -8536,7 +9145,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
8536
9145
|
return DeviceRole;
|
|
8537
9146
|
}({});
|
|
8538
9147
|
//#endregion
|
|
8539
|
-
//#region ../types/dist/composition-
|
|
9148
|
+
//#region ../types/dist/composition-CB6PMT9p.mjs
|
|
8540
9149
|
/**
|
|
8541
9150
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
8542
9151
|
*
|
|
@@ -11740,11 +12349,10 @@ var ClipModelMetaSchema = object({
|
|
|
11740
12349
|
/**
|
|
11741
12350
|
* The `vector-store` index this model's vectors live in. ONE feature space
|
|
11742
12351
|
* per index: two models share an index only when their vectors are ranked
|
|
11743
|
-
* against each other, which for CLIP means never across families.
|
|
11744
|
-
*
|
|
11745
|
-
* `modelId`); any other model gets its own.
|
|
12352
|
+
* against each other, which for CLIP means never across families. Every
|
|
12353
|
+
* model gets its own.
|
|
11746
12354
|
*/
|
|
11747
|
-
vectorIndex: string().regex(/^object-clip
|
|
12355
|
+
vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
|
|
11748
12356
|
/**
|
|
11749
12357
|
* The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
|
|
11750
12358
|
* must be unique per tokenizer: every model's siblings land in ONE flat
|
|
@@ -12923,8 +13531,13 @@ var AlertSeveritySchema = _enum([
|
|
|
12923
13531
|
"info",
|
|
12924
13532
|
"success",
|
|
12925
13533
|
"warning",
|
|
12926
|
-
"error"
|
|
13534
|
+
"error",
|
|
13535
|
+
"critical"
|
|
12927
13536
|
]);
|
|
13537
|
+
var AlertActionSchema = object({
|
|
13538
|
+
label: string(),
|
|
13539
|
+
href: string().optional()
|
|
13540
|
+
});
|
|
12928
13541
|
var AlertStatusSchema = _enum([
|
|
12929
13542
|
"active",
|
|
12930
13543
|
"in-progress",
|
|
@@ -12942,6 +13555,7 @@ var AlertSchema = object({
|
|
|
12942
13555
|
severity: AlertSeveritySchema,
|
|
12943
13556
|
title: string(),
|
|
12944
13557
|
message: string(),
|
|
13558
|
+
action: AlertActionSchema.optional(),
|
|
12945
13559
|
status: AlertStatusSchema,
|
|
12946
13560
|
progress: number().optional(),
|
|
12947
13561
|
read: boolean(),
|
|
@@ -15420,6 +16034,41 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
|
15420
16034
|
keyField: string()
|
|
15421
16035
|
}).optional()
|
|
15422
16036
|
})) });
|
|
16037
|
+
/**
|
|
16038
|
+
* The report of a one-shot "move this owner's plaintext secrets into the
|
|
16039
|
+
* keychain" run (D878). COUNTS only - never a value, never a ref.
|
|
16040
|
+
*
|
|
16041
|
+
* The owner runs it on its own rows, through its own write path; an operator
|
|
16042
|
+
* script (`scripts/hub/migrate-raw-secrets.ts`) calls the owner's cap method.
|
|
16043
|
+
* Dry run (`apply: false`) reports what WOULD move and changes nothing.
|
|
16044
|
+
*/
|
|
16045
|
+
var RawSecretMigrationInputSchema = object({
|
|
16046
|
+
/** `false` (the default of the script) = report only. */
|
|
16047
|
+
apply: boolean() });
|
|
16048
|
+
var RawSecretMigrationReportSchema = object({
|
|
16049
|
+
apply: boolean(),
|
|
16050
|
+
/** Rows that can carry a secret (a profile, an enrolment). */
|
|
16051
|
+
rows: number().int(),
|
|
16052
|
+
/** Rows whose secret was plaintext when the run started. */
|
|
16053
|
+
raw: number().int(),
|
|
16054
|
+
/** Rows that already held a keychain marker when the run started. */
|
|
16055
|
+
markers: number().int(),
|
|
16056
|
+
/** Plaintext rows moved by THIS run (always 0 on a dry run). */
|
|
16057
|
+
migrated: number().int(),
|
|
16058
|
+
/** Plaintext rows left in place because the move or its read-back failed. */
|
|
16059
|
+
failed: number().int(),
|
|
16060
|
+
/** After the run: rows still holding plaintext. */
|
|
16061
|
+
remainingRaw: number().int(),
|
|
16062
|
+
/** After the run: markers the keychain could not resolve right now. */
|
|
16063
|
+
unresolvedMarkers: number().int(),
|
|
16064
|
+
/**
|
|
16065
|
+
* A second, redundant copy of the secrets' rows that the run found (and, on
|
|
16066
|
+
* `apply`, deleted after verifying the first). Owners with a single copy omit it.
|
|
16067
|
+
*/
|
|
16068
|
+
redundantCopies: number().int().optional(),
|
|
16069
|
+
/** True when nothing is raw and every marker resolves. */
|
|
16070
|
+
verified: boolean()
|
|
16071
|
+
});
|
|
15423
16072
|
/** Where a block runs. The operator chooses — a block driving a device on an
|
|
15424
16073
|
* agent is the reason placement is not fixed to the hub. */
|
|
15425
16074
|
var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
|
|
@@ -15646,6 +16295,9 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
15646
16295
|
}), SettingsUpdateResultSchema, {
|
|
15647
16296
|
kind: "mutation",
|
|
15648
16297
|
auth: "admin"
|
|
16298
|
+
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
16299
|
+
kind: "mutation",
|
|
16300
|
+
auth: "admin"
|
|
15649
16301
|
}), method(object({}), object({ libs: array(object({
|
|
15650
16302
|
filePath: string(),
|
|
15651
16303
|
content: string()
|
|
@@ -15909,6 +16561,347 @@ id: union([string(), number()]) }), {
|
|
|
15909
16561
|
auth: "admin"
|
|
15910
16562
|
});
|
|
15911
16563
|
/**
|
|
16564
|
+
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
16565
|
+
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
16566
|
+
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
16567
|
+
*
|
|
16568
|
+
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
16569
|
+
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
16570
|
+
* that with the addon id closed over). It is deliberately NOT in any input
|
|
16571
|
+
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
16572
|
+
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
16573
|
+
* addons go through the owner's capability.
|
|
16574
|
+
*
|
|
16575
|
+
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
16576
|
+
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
16577
|
+
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
16578
|
+
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
16579
|
+
* row's own ref.
|
|
16580
|
+
*
|
|
16581
|
+
* Internal: addons reach it through `ctx.records`, never directly.
|
|
16582
|
+
*/
|
|
16583
|
+
var RecordIdSchema = string().min(1).max(256);
|
|
16584
|
+
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
16585
|
+
var RecordBodySchema = record(string(), unknown());
|
|
16586
|
+
var RecordInputSchema = object({
|
|
16587
|
+
id: RecordIdSchema,
|
|
16588
|
+
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
16589
|
+
deviceId: number().int().optional(),
|
|
16590
|
+
/** The domain clock, ms: observed / created / opened. */
|
|
16591
|
+
at: number().int().optional(),
|
|
16592
|
+
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
16593
|
+
expiresAt: number().int().optional(),
|
|
16594
|
+
data: RecordBodySchema
|
|
16595
|
+
});
|
|
16596
|
+
var RecordViewSchema = object({
|
|
16597
|
+
id: string(),
|
|
16598
|
+
deviceId: number().int().nullable(),
|
|
16599
|
+
at: number().nullable(),
|
|
16600
|
+
expiresAt: number().nullable(),
|
|
16601
|
+
rev: number().int(),
|
|
16602
|
+
updatedAt: number(),
|
|
16603
|
+
data: RecordBodySchema
|
|
16604
|
+
});
|
|
16605
|
+
/** Declared index fields only. Anything else does not compile, by design. */
|
|
16606
|
+
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
16607
|
+
string(),
|
|
16608
|
+
number(),
|
|
16609
|
+
boolean(),
|
|
16610
|
+
object({ in: array(union([
|
|
16611
|
+
string(),
|
|
16612
|
+
number(),
|
|
16613
|
+
boolean()
|
|
16614
|
+
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
16615
|
+
object({
|
|
16616
|
+
gt: union([string(), number()]).optional(),
|
|
16617
|
+
gte: union([string(), number()]).optional(),
|
|
16618
|
+
lt: union([string(), number()]).optional(),
|
|
16619
|
+
lte: union([string(), number()]).optional()
|
|
16620
|
+
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
16621
|
+
]));
|
|
16622
|
+
var KindShape = { kind: RecordKindNameSchema };
|
|
16623
|
+
var TimeWindowShape = {
|
|
16624
|
+
deviceId: number().int().optional(),
|
|
16625
|
+
atFrom: number().optional(),
|
|
16626
|
+
atTo: number().optional()
|
|
16627
|
+
};
|
|
16628
|
+
var RecordQueryInputSchema = object({
|
|
16629
|
+
...KindShape,
|
|
16630
|
+
...TimeWindowShape,
|
|
16631
|
+
where: RecordWhereSchema.optional(),
|
|
16632
|
+
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
16633
|
+
order: _enum(["at", "id"]).optional(),
|
|
16634
|
+
desc: boolean().optional(),
|
|
16635
|
+
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
16636
|
+
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
16637
|
+
cursor: string().min(1).optional()
|
|
16638
|
+
});
|
|
16639
|
+
var RecordQueryResultSchema = object({
|
|
16640
|
+
records: array(RecordViewSchema),
|
|
16641
|
+
nextCursor: string().nullable()
|
|
16642
|
+
});
|
|
16643
|
+
var RecordKindStateSchema = _enum([
|
|
16644
|
+
"created",
|
|
16645
|
+
"unchanged",
|
|
16646
|
+
"updated"
|
|
16647
|
+
]);
|
|
16648
|
+
var DeclareKindResultSchema = object({
|
|
16649
|
+
kindId: number().int(),
|
|
16650
|
+
state: RecordKindStateSchema,
|
|
16651
|
+
createdIndexes: array(string()),
|
|
16652
|
+
/** The boot guard: the kind is empty while its legacy table still holds rows. */
|
|
16653
|
+
adoptionPending: object({
|
|
16654
|
+
legacyTable: string(),
|
|
16655
|
+
legacyRows: number().int()
|
|
16656
|
+
}).nullable()
|
|
16657
|
+
});
|
|
16658
|
+
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
16659
|
+
var SecretRefsList = array(string());
|
|
16660
|
+
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
16661
|
+
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
16662
|
+
kind: "mutation",
|
|
16663
|
+
auth: "admin"
|
|
16664
|
+
}), method(object({
|
|
16665
|
+
...KindShape,
|
|
16666
|
+
id: RecordIdSchema
|
|
16667
|
+
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
16668
|
+
...KindShape,
|
|
16669
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
16670
|
+
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
16671
|
+
...KindShape,
|
|
16672
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
16673
|
+
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
16674
|
+
...KindShape,
|
|
16675
|
+
record: RecordInputSchema,
|
|
16676
|
+
/** Compare-and-set. `0` means "the row must not exist". */
|
|
16677
|
+
ifRev: number().int().min(0).optional()
|
|
16678
|
+
}), object({
|
|
16679
|
+
rev: number().int(),
|
|
16680
|
+
releasedRefs: SecretRefsList
|
|
16681
|
+
}), {
|
|
16682
|
+
kind: "mutation",
|
|
16683
|
+
auth: "admin"
|
|
16684
|
+
}), method(object({
|
|
16685
|
+
...KindShape,
|
|
16686
|
+
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
16687
|
+
}), object({
|
|
16688
|
+
count: number().int(),
|
|
16689
|
+
releasedRefs: SecretRefsList
|
|
16690
|
+
}), {
|
|
16691
|
+
kind: "mutation",
|
|
16692
|
+
auth: "admin"
|
|
16693
|
+
}), method(object({
|
|
16694
|
+
...KindShape,
|
|
16695
|
+
id: RecordIdSchema,
|
|
16696
|
+
data: RecordBodySchema,
|
|
16697
|
+
at: number().int().nullable().optional(),
|
|
16698
|
+
expiresAt: number().int().nullable().optional(),
|
|
16699
|
+
ifRev: number().int().min(1).optional()
|
|
16700
|
+
}), object({
|
|
16701
|
+
rev: number().int(),
|
|
16702
|
+
releasedRefs: SecretRefsList
|
|
16703
|
+
}), {
|
|
16704
|
+
kind: "mutation",
|
|
16705
|
+
auth: "admin"
|
|
16706
|
+
}), method(object({
|
|
16707
|
+
...KindShape,
|
|
16708
|
+
id: RecordIdSchema
|
|
16709
|
+
}), object({
|
|
16710
|
+
deleted: boolean(),
|
|
16711
|
+
secretRefs: SecretRefsList
|
|
16712
|
+
}), {
|
|
16713
|
+
kind: "mutation",
|
|
16714
|
+
auth: "admin"
|
|
16715
|
+
}), method(object({
|
|
16716
|
+
...KindShape,
|
|
16717
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
16718
|
+
}), object({
|
|
16719
|
+
deleted: number().int(),
|
|
16720
|
+
secretRefs: SecretRefsList
|
|
16721
|
+
}), {
|
|
16722
|
+
kind: "mutation",
|
|
16723
|
+
auth: "admin"
|
|
16724
|
+
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
16725
|
+
...KindShape,
|
|
16726
|
+
...TimeWindowShape,
|
|
16727
|
+
where: RecordWhereSchema.optional()
|
|
16728
|
+
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
16729
|
+
...KindShape,
|
|
16730
|
+
deviceId: number().int().optional(),
|
|
16731
|
+
from: number(),
|
|
16732
|
+
to: number(),
|
|
16733
|
+
bucketMs: number().int().positive()
|
|
16734
|
+
}), object({ buckets: array(object({
|
|
16735
|
+
bucketStart: number(),
|
|
16736
|
+
count: number().int()
|
|
16737
|
+
})) }), { auth: "admin" }), method(object({
|
|
16738
|
+
...KindShape,
|
|
16739
|
+
op: _enum([
|
|
16740
|
+
"sum",
|
|
16741
|
+
"min",
|
|
16742
|
+
"max",
|
|
16743
|
+
"count"
|
|
16744
|
+
]),
|
|
16745
|
+
field: RecordFieldPathSchema.optional(),
|
|
16746
|
+
groupBy: RecordFieldPathSchema.optional(),
|
|
16747
|
+
where: RecordWhereSchema.optional()
|
|
16748
|
+
}), object({ rows: array(object({
|
|
16749
|
+
group: union([
|
|
16750
|
+
string(),
|
|
16751
|
+
number(),
|
|
16752
|
+
_null()
|
|
16753
|
+
]),
|
|
16754
|
+
value: number().nullable()
|
|
16755
|
+
})) }), { auth: "admin" }), method(object({
|
|
16756
|
+
...KindShape,
|
|
16757
|
+
deviceId: number().int().optional(),
|
|
16758
|
+
atBefore: number().optional(),
|
|
16759
|
+
where: RecordWhereSchema.optional()
|
|
16760
|
+
}), object({
|
|
16761
|
+
deleted: number().int(),
|
|
16762
|
+
secretRefs: SecretRefsList
|
|
16763
|
+
}), {
|
|
16764
|
+
kind: "mutation",
|
|
16765
|
+
auth: "admin"
|
|
16766
|
+
}), method(object({
|
|
16767
|
+
deviceId: number().int(),
|
|
16768
|
+
kinds: array(RecordKindNameSchema).optional()
|
|
16769
|
+
}), object({
|
|
16770
|
+
deleted: record(string(), number().int()),
|
|
16771
|
+
secretRefs: SecretRefsList
|
|
16772
|
+
}), {
|
|
16773
|
+
kind: "mutation",
|
|
16774
|
+
auth: "admin"
|
|
16775
|
+
}), method(object(KindShape), object({ id: number().int() }), {
|
|
16776
|
+
kind: "mutation",
|
|
16777
|
+
auth: "admin"
|
|
16778
|
+
});
|
|
16779
|
+
/** One table of the settings database. */
|
|
16780
|
+
var StorageTableSchema = object({
|
|
16781
|
+
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
16782
|
+
name: string(),
|
|
16783
|
+
/** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
|
|
16784
|
+
owner: string().nullable(),
|
|
16785
|
+
rows: number().int().nonnegative(),
|
|
16786
|
+
/** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
|
|
16787
|
+
bytes: number().int().nonnegative().nullable(),
|
|
16788
|
+
/** Declared by an addon since this engine process started (a running addon claims it). */
|
|
16789
|
+
declaredThisBoot: boolean()
|
|
16790
|
+
});
|
|
16791
|
+
/** One declared record kind (D881) as System -> Data shows it. */
|
|
16792
|
+
var StorageKindSchema = object({
|
|
16793
|
+
owner: string(),
|
|
16794
|
+
kind: string(),
|
|
16795
|
+
database: _enum(["settings", "events"]),
|
|
16796
|
+
class: string(),
|
|
16797
|
+
state: _enum([
|
|
16798
|
+
"declared",
|
|
16799
|
+
"orphaned",
|
|
16800
|
+
"dropped"
|
|
16801
|
+
]),
|
|
16802
|
+
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
16803
|
+
rows: number().int().nonnegative().nullable(),
|
|
16804
|
+
/** Rows x the average size of a sample; null for an empty kind. */
|
|
16805
|
+
bytesEstimate: number().int().nonnegative().nullable(),
|
|
16806
|
+
writeTrigger: string(),
|
|
16807
|
+
retention: RecordRetentionSchema,
|
|
16808
|
+
/** `true` when the kind is an entry kind (the generic entries UI lists it). */
|
|
16809
|
+
entry: boolean(),
|
|
16810
|
+
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
16811
|
+
declaration: RecordKindDeclarationSchema
|
|
16812
|
+
});
|
|
16813
|
+
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
16814
|
+
collection: string().min(1),
|
|
16815
|
+
expectRows: number().int().nonnegative()
|
|
16816
|
+
}), object({
|
|
16817
|
+
dropped: literal(true),
|
|
16818
|
+
rows: number().int().nonnegative()
|
|
16819
|
+
}), {
|
|
16820
|
+
kind: "mutation",
|
|
16821
|
+
auth: "admin"
|
|
16822
|
+
}), method(object({
|
|
16823
|
+
owner: string().min(1).optional(),
|
|
16824
|
+
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
16825
|
+
counts: boolean().optional()
|
|
16826
|
+
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
16827
|
+
owner: string().min(1),
|
|
16828
|
+
kind: RecordKindNameSchema
|
|
16829
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
16830
|
+
owner: string().min(1),
|
|
16831
|
+
kind: RecordKindNameSchema,
|
|
16832
|
+
records: array(RecordInputSchema).max(RECORD_BATCH_MAX),
|
|
16833
|
+
/** The LAST batch: the copy is complete, the boot guard stops asking. */
|
|
16834
|
+
complete: boolean().optional()
|
|
16835
|
+
}).refine((input) => input.records.length > 0 || input.complete === true, { message: "records must be non-empty unless complete is true" }), object({ adopted: number().int() }), {
|
|
16836
|
+
kind: "mutation",
|
|
16837
|
+
auth: "admin"
|
|
16838
|
+
}), method(object({
|
|
16839
|
+
collection: string().min(1),
|
|
16840
|
+
cursor: string().optional(),
|
|
16841
|
+
limit: number().int().min(1).max(1e3).optional()
|
|
16842
|
+
}), object({
|
|
16843
|
+
rows: array(object({
|
|
16844
|
+
id: string(),
|
|
16845
|
+
data: record(string(), unknown())
|
|
16846
|
+
})),
|
|
16847
|
+
nextCursor: string().nullable()
|
|
16848
|
+
}), { auth: "admin" }), method(object({ collection: string().min(1) }), object({ adopted: boolean() }), { auth: "admin" });
|
|
16849
|
+
/**
|
|
16850
|
+
* record-entries - the ONE admin surface for every list of user-created entries
|
|
16851
|
+
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
16852
|
+
* declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
|
|
16853
|
+
*
|
|
16854
|
+
* The UI is generic. `listEntryKinds` hands it, per kind, the JSON Schema of
|
|
16855
|
+
* the entry body with `secret: true` on every credential field, so a password
|
|
16856
|
+
* field is derived from the schema and never from a name. `putEntry` takes the
|
|
16857
|
+
* redaction token for an untouched secret (the stored value is kept), a string
|
|
16858
|
+
* for a new one, and the clear instruction to remove one. A read never carries
|
|
16859
|
+
* a secret value, and a locked keychain refuses the write with
|
|
16860
|
+
* `records/secret-unavailable` instead of storing plaintext.
|
|
16861
|
+
*
|
|
16862
|
+
* Hub-only, served in-process (it acts for each entry's owner on the keychain).
|
|
16863
|
+
*/
|
|
16864
|
+
var EntryKindInfoSchema = object({
|
|
16865
|
+
owner: string(),
|
|
16866
|
+
kind: RecordKindNameSchema,
|
|
16867
|
+
title: string(),
|
|
16868
|
+
nameField: string(),
|
|
16869
|
+
/** JSON Schema of the body; `secret: true` marks a password field. */
|
|
16870
|
+
fields: record(string(), unknown()),
|
|
16871
|
+
secretPaths: array(string()),
|
|
16872
|
+
rows: number().int().nonnegative()
|
|
16873
|
+
});
|
|
16874
|
+
var EntryViewSchema = object({
|
|
16875
|
+
id: RecordIdSchema,
|
|
16876
|
+
name: string(),
|
|
16877
|
+
rev: number().int(),
|
|
16878
|
+
updatedAt: number(),
|
|
16879
|
+
/** Secret fields hold the redaction token. */
|
|
16880
|
+
data: RecordBodySchema
|
|
16881
|
+
});
|
|
16882
|
+
var EntryAddress = {
|
|
16883
|
+
owner: string().min(1),
|
|
16884
|
+
kind: RecordKindNameSchema
|
|
16885
|
+
};
|
|
16886
|
+
method(object({}), object({ kinds: array(EntryKindInfoSchema) }), { auth: "admin" }), method(object({ ...EntryAddress }), object({ entries: array(EntryViewSchema) }), { auth: "admin" }), method(object({
|
|
16887
|
+
...EntryAddress,
|
|
16888
|
+
id: RecordIdSchema.optional(),
|
|
16889
|
+
data: RecordBodySchema,
|
|
16890
|
+
ifRev: number().int().min(1).optional()
|
|
16891
|
+
}), object({
|
|
16892
|
+
id: string(),
|
|
16893
|
+
rev: number().int()
|
|
16894
|
+
}), {
|
|
16895
|
+
kind: "mutation",
|
|
16896
|
+
auth: "admin"
|
|
16897
|
+
}), method(object({
|
|
16898
|
+
...EntryAddress,
|
|
16899
|
+
id: RecordIdSchema
|
|
16900
|
+
}), object({ deleted: boolean() }), {
|
|
16901
|
+
kind: "mutation",
|
|
16902
|
+
auth: "admin"
|
|
16903
|
+
});
|
|
16904
|
+
/**
|
|
15912
16905
|
* `keychain` — the hub's encrypted store of secret values (D787).
|
|
15913
16906
|
*
|
|
15914
16907
|
* Settings blobs never hold a secret: they hold a marker
|
|
@@ -16449,6 +17442,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
|
|
|
16449
17442
|
kind: "mutation",
|
|
16450
17443
|
auth: "admin",
|
|
16451
17444
|
caller: "required"
|
|
17445
|
+
}), method(object({
|
|
17446
|
+
ref: string().min(1),
|
|
17447
|
+
value: string().min(1)
|
|
17448
|
+
}), object({
|
|
17449
|
+
ref: string(),
|
|
17450
|
+
version: number().int()
|
|
17451
|
+
}), {
|
|
17452
|
+
kind: "mutation",
|
|
17453
|
+
auth: "admin",
|
|
17454
|
+
caller: "required"
|
|
16452
17455
|
}), method(object({ refs: array(string().min(1)).max(1e3) }), object({
|
|
16453
17456
|
deleted: array(string()).readonly(),
|
|
16454
17457
|
kept: array(object({
|
|
@@ -16746,7 +17749,11 @@ DeviceType.Hub, DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number().int
|
|
|
16746
17749
|
});
|
|
16747
17750
|
/**
|
|
16748
17751
|
* `device-adoption` — generic discovery + adoption surface,
|
|
16749
|
-
* system-scoped
|
|
17752
|
+
* system-scoped collection (one provider per integration addon), routed
|
|
17753
|
+
* by explicit `{ addonId }` on direct `deviceAdoption.*` calls — the same
|
|
17754
|
+
* pin model as `connection-test`. The admin UI keeps using
|
|
17755
|
+
* `deviceManager.adoption*` (which already threads `addonId`). The de-HA-ified
|
|
17756
|
+
* successor to `ha-discovery`:
|
|
16750
17757
|
* keyed by `integrationId` (the addon resolves integrationId → broker
|
|
16751
17758
|
* internally) rather than a connection-specific `brokerId`, and it
|
|
16752
17759
|
* REUSES the shared `DiscoveredChildDevice` candidate type from
|
|
@@ -38918,6 +39925,9 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
38918
39925
|
}), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
38919
39926
|
kind: "mutation",
|
|
38920
39927
|
auth: "admin"
|
|
39928
|
+
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
39929
|
+
kind: "mutation",
|
|
39930
|
+
auth: "admin"
|
|
38921
39931
|
}), method(ProfileRefInputSchema, _void(), {
|
|
38922
39932
|
kind: "mutation",
|
|
38923
39933
|
auth: "admin"
|
|
@@ -49360,6 +50370,41 @@ var ServerRollbackInfoSchema = object({
|
|
|
49360
50370
|
atMs: number(),
|
|
49361
50371
|
reason: string()
|
|
49362
50372
|
});
|
|
50373
|
+
/**
|
|
50374
|
+
* The runner boot check (D877): the runners this node PLANNED (hub: one per
|
|
50375
|
+
* `resolveRunnerId` over its forked addons; agent: the runners its process
|
|
50376
|
+
* service holds, because only the hub plans) against the states the process
|
|
50377
|
+
* service reports. Kept in memory, recomputed by the same one-shot check after
|
|
50378
|
+
* every boot, served on `getServerPackageStatus`.
|
|
50379
|
+
*
|
|
50380
|
+
* `failed` is the D6 breaker's terminal state, `refused` a deterministic Init
|
|
50381
|
+
* refusal (an addon that said "I will not start, and here is why"), `missing` a
|
|
50382
|
+
* planned runner the process service has no entry for.
|
|
50383
|
+
*/
|
|
50384
|
+
var RunnerRefusalSchema = object({
|
|
50385
|
+
runnerId: string(),
|
|
50386
|
+
addonIds: array(string()),
|
|
50387
|
+
reason: string()
|
|
50388
|
+
});
|
|
50389
|
+
var RunnerBootCheckSchema = object({
|
|
50390
|
+
checkedAtMs: number(),
|
|
50391
|
+
/** Runners planned for this node. */
|
|
50392
|
+
expected: number(),
|
|
50393
|
+
/** Planned runners whose process is `running`. */
|
|
50394
|
+
running: number(),
|
|
50395
|
+
failed: array(string()),
|
|
50396
|
+
refused: array(RunnerRefusalSchema),
|
|
50397
|
+
missing: array(string()),
|
|
50398
|
+
/**
|
|
50399
|
+
* Runners still on their way: `starting`, or `crashed` waiting on a backoff
|
|
50400
|
+
* respawn. The check is FINAL only when this is empty; until then nothing is
|
|
50401
|
+
* pushed and the verdict is re-evaluated on the next runner state change.
|
|
50402
|
+
* Optional for version skew (absent = settled).
|
|
50403
|
+
*/
|
|
50404
|
+
pending: array(string()).optional(),
|
|
50405
|
+
/** True when the plan is the hub's (`missing` is then meaningful); false on an agent. */
|
|
50406
|
+
planKnown: boolean()
|
|
50407
|
+
});
|
|
49363
50408
|
var ServerPackageStatusSchema = object({
|
|
49364
50409
|
/** Root package name (`@camstack/server` on the hub). */
|
|
49365
50410
|
packageName: string(),
|
|
@@ -49422,7 +50467,14 @@ var ServerPackageStatusSchema = object({
|
|
|
49422
50467
|
* for version skew: an older provider's payload simply omits it, and an
|
|
49423
50468
|
* absent wrapper is unknown — never `native`.
|
|
49424
50469
|
*/
|
|
49425
|
-
wrapper: WrapperIdentitySchema.optional()
|
|
50470
|
+
wrapper: WrapperIdentitySchema.optional(),
|
|
50471
|
+
/**
|
|
50472
|
+
* What this node PLANNED to run against what its process service reports
|
|
50473
|
+
* (D877). Absent until the one-shot boot check has run (~60 s after boot) and
|
|
50474
|
+
* on a provider that predates it. Absent is "not checked yet", never "all
|
|
50475
|
+
* well": a consumer that needs the answer treats it as pending.
|
|
50476
|
+
*/
|
|
50477
|
+
runners: RunnerBootCheckSchema.optional()
|
|
49426
50478
|
});
|
|
49427
50479
|
var ServerUpdateCheckResultSchema = object({
|
|
49428
50480
|
packageName: string(),
|
|
@@ -49473,6 +50525,13 @@ var ServerUpdateOutcomeSchema = _enum([
|
|
|
49473
50525
|
"refused",
|
|
49474
50526
|
"failed"
|
|
49475
50527
|
]);
|
|
50528
|
+
/** One addon the pre-flight found the target version would refuse (D877). */
|
|
50529
|
+
var UpdateBlockerSchema = object({
|
|
50530
|
+
addonId: string(),
|
|
50531
|
+
reason: string(),
|
|
50532
|
+
/** What the operator does about it. */
|
|
50533
|
+
remedy: string()
|
|
50534
|
+
});
|
|
49476
50535
|
var ServerUpdateActionResultSchema = object({
|
|
49477
50536
|
accepted: boolean(),
|
|
49478
50537
|
targetVersion: string().nullable(),
|
|
@@ -49490,14 +50549,27 @@ var ServerUpdateActionResultSchema = object({
|
|
|
49490
50549
|
* The failure itself, verbatim enough to act on (the npm output for a
|
|
49491
50550
|
* staging install). Non-null only for `failed`.
|
|
49492
50551
|
*/
|
|
49493
|
-
error: string().nullable().optional()
|
|
50552
|
+
error: string().nullable().optional(),
|
|
50553
|
+
/**
|
|
50554
|
+
* The addons the target version would refuse to start on this node's live
|
|
50555
|
+
* store (D877), present when the pre-flight refused the update
|
|
50556
|
+
* (`reason: 'addon-would-refuse'`). Optional for version skew.
|
|
50557
|
+
*/
|
|
50558
|
+
blockers: array(UpdateBlockerSchema).optional()
|
|
49494
50559
|
});
|
|
49495
50560
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
49496
50561
|
kind: "mutation",
|
|
49497
50562
|
auth: "admin"
|
|
49498
|
-
}), method(object({
|
|
49499
|
-
/** Explicit target version; omitted = latest from the registry. */
|
|
49500
|
-
version: string().optional()
|
|
50563
|
+
}), method(object({
|
|
50564
|
+
/** Explicit target version; omitted = latest from the registry. */
|
|
50565
|
+
version: string().optional(),
|
|
50566
|
+
/**
|
|
50567
|
+
* Apply even though the pre-flight (D877) found addons the target would
|
|
50568
|
+
* refuse to start. Off by default: those addons stay down until the
|
|
50569
|
+
* remedy is run, which is the incident this gate exists to prevent.
|
|
50570
|
+
*/
|
|
50571
|
+
acknowledgeRefusals: boolean().optional()
|
|
50572
|
+
}), ServerUpdateActionResultSchema, {
|
|
49501
50573
|
kind: "mutation",
|
|
49502
50574
|
auth: "admin",
|
|
49503
50575
|
timeoutMs: 16 * 6e4
|
|
@@ -51910,7 +52982,9 @@ var ManifestShapeSchema = object({
|
|
|
51910
52982
|
var AddonProcessSchema = object({
|
|
51911
52983
|
pid: number().optional(),
|
|
51912
52984
|
mode: string(),
|
|
51913
|
-
state: string()
|
|
52985
|
+
state: string(),
|
|
52986
|
+
/** Why the runner is `refused` (an addon's own words), when it is (D877). */
|
|
52987
|
+
reason: string().optional()
|
|
51914
52988
|
});
|
|
51915
52989
|
/**
|
|
51916
52990
|
* Per-addon health snapshot from the kernel's AddonHealthMonitor.
|
|
@@ -52488,7 +53562,7 @@ var BatteryStatusSchema = object({
|
|
|
52488
53562
|
*/
|
|
52489
53563
|
binary: boolean().optional()
|
|
52490
53564
|
});
|
|
52491
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, method(object({
|
|
53565
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, method(object({
|
|
52492
53566
|
deviceId: number(),
|
|
52493
53567
|
/** Bound on the wait. Sensible range 3000–10000ms. */
|
|
52494
53568
|
timeoutMs: number().int().min(500).max(3e4).default(8e3)
|
|
@@ -52913,6 +53987,12 @@ object({
|
|
|
52913
53987
|
/** Horizontal louver swing on/off. Null when the device has no
|
|
52914
53988
|
* horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
|
|
52915
53989
|
swingHorizontal: boolean().nullable(),
|
|
53990
|
+
/** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
53991
|
+
minTemp: number().nullable().optional(),
|
|
53992
|
+
/** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
|
|
53993
|
+
maxTemp: number().nullable().optional(),
|
|
53994
|
+
/** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
|
|
53995
|
+
tempStep: number().positive().nullable().optional(),
|
|
52916
53996
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
52917
53997
|
lastFetchedAt: number()
|
|
52918
53998
|
});
|
|
@@ -53358,6 +54438,130 @@ object({
|
|
|
53358
54438
|
deviceId: number(),
|
|
53359
54439
|
status: DeviceStatusSchema
|
|
53360
54440
|
});
|
|
54441
|
+
/**
|
|
54442
|
+
* Smart-dishwasher cap — program lifecycle, door / remote-start gates,
|
|
54443
|
+
* consumable levels (salt, rinse aid) and common program options.
|
|
54444
|
+
*
|
|
54445
|
+
* Sources: native Bosch Home Connect (`dishwasher` device type). Reusable
|
|
54446
|
+
* by any integration that exposes the same program / option surface.
|
|
54447
|
+
*
|
|
54448
|
+
* `kind: 'poll'` — cloud REST APIs typically have no push channel; the
|
|
54449
|
+
* provider refreshes the slice on an interval and eagerly after commands.
|
|
54450
|
+
*/
|
|
54451
|
+
var DishwasherStateSchema = _enum([
|
|
54452
|
+
"idle",
|
|
54453
|
+
"running",
|
|
54454
|
+
"paused",
|
|
54455
|
+
"finished",
|
|
54456
|
+
"error",
|
|
54457
|
+
"unavailable"
|
|
54458
|
+
]);
|
|
54459
|
+
var DishwasherConsumableLevelSchema = _enum([
|
|
54460
|
+
"ok",
|
|
54461
|
+
"low",
|
|
54462
|
+
"empty"
|
|
54463
|
+
]);
|
|
54464
|
+
object({
|
|
54465
|
+
state: DishwasherStateSchema,
|
|
54466
|
+
/** Active program id/name, or null when none selected / unknown. */
|
|
54467
|
+
program: string().nullable(),
|
|
54468
|
+
/** Program tokens the hardware accepts — drives the UI selector. */
|
|
54469
|
+
availablePrograms: array(string()),
|
|
54470
|
+
/** Minutes remaining on the active cycle, or null when not running. */
|
|
54471
|
+
remainingMinutes: number().int().nonnegative().nullable(),
|
|
54472
|
+
/** Cycle completion 0..100, or null when not applicable. */
|
|
54473
|
+
progressPercent: number().min(0).max(100).nullable(),
|
|
54474
|
+
/** True when the door is reported open. */
|
|
54475
|
+
doorOpen: boolean(),
|
|
54476
|
+
/** True when remote start is allowed (door closed, power on, etc.). */
|
|
54477
|
+
remoteStartAllowed: boolean(),
|
|
54478
|
+
/** Main power / switched-on state. */
|
|
54479
|
+
powerOn: boolean(),
|
|
54480
|
+
saltLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
54481
|
+
rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
54482
|
+
halfLoad: boolean(),
|
|
54483
|
+
hygienePlus: boolean(),
|
|
54484
|
+
/** Extra dry option when the appliance exposes it. */
|
|
54485
|
+
extraDry: boolean(),
|
|
54486
|
+
/** Intensive zone (lower basket) when the appliance exposes it. */
|
|
54487
|
+
intensiveZone: boolean(),
|
|
54488
|
+
/** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
|
|
54489
|
+
varioSpeedPlus: boolean(),
|
|
54490
|
+
/** Child lock (controls disabled on the appliance). */
|
|
54491
|
+
childLock: boolean(),
|
|
54492
|
+
errorCode: number().nullable(),
|
|
54493
|
+
errorLabel: string().nullable(),
|
|
54494
|
+
lastFetchedAt: number()
|
|
54495
|
+
});
|
|
54496
|
+
DeviceType.Dishwasher, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
54497
|
+
kind: "mutation",
|
|
54498
|
+
auth: "protected"
|
|
54499
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
54500
|
+
kind: "mutation",
|
|
54501
|
+
auth: "protected"
|
|
54502
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
54503
|
+
kind: "mutation",
|
|
54504
|
+
auth: "protected"
|
|
54505
|
+
}), method(object({
|
|
54506
|
+
deviceId: number().int().nonnegative(),
|
|
54507
|
+
program: string().min(1)
|
|
54508
|
+
}), _void(), {
|
|
54509
|
+
kind: "mutation",
|
|
54510
|
+
auth: "protected"
|
|
54511
|
+
}), method(object({
|
|
54512
|
+
deviceId: number().int().nonnegative(),
|
|
54513
|
+
on: boolean()
|
|
54514
|
+
}), _void(), {
|
|
54515
|
+
kind: "mutation",
|
|
54516
|
+
auth: "protected"
|
|
54517
|
+
}), method(object({
|
|
54518
|
+
deviceId: number().int().nonnegative(),
|
|
54519
|
+
on: boolean()
|
|
54520
|
+
}), _void(), {
|
|
54521
|
+
kind: "mutation",
|
|
54522
|
+
auth: "protected"
|
|
54523
|
+
}), method(object({
|
|
54524
|
+
deviceId: number().int().nonnegative(),
|
|
54525
|
+
on: boolean()
|
|
54526
|
+
}), _void(), {
|
|
54527
|
+
kind: "mutation",
|
|
54528
|
+
auth: "protected"
|
|
54529
|
+
}), method(object({
|
|
54530
|
+
deviceId: number().int().nonnegative(),
|
|
54531
|
+
on: boolean()
|
|
54532
|
+
}), _void(), {
|
|
54533
|
+
kind: "mutation",
|
|
54534
|
+
auth: "protected"
|
|
54535
|
+
}), method(object({
|
|
54536
|
+
deviceId: number().int().nonnegative(),
|
|
54537
|
+
on: boolean()
|
|
54538
|
+
}), _void(), {
|
|
54539
|
+
kind: "mutation",
|
|
54540
|
+
auth: "protected"
|
|
54541
|
+
}), method(object({
|
|
54542
|
+
deviceId: number().int().nonnegative(),
|
|
54543
|
+
on: boolean()
|
|
54544
|
+
}), _void(), {
|
|
54545
|
+
kind: "mutation",
|
|
54546
|
+
auth: "protected"
|
|
54547
|
+
}), method(object({
|
|
54548
|
+
deviceId: number().int().nonnegative(),
|
|
54549
|
+
on: boolean()
|
|
54550
|
+
}), _void(), {
|
|
54551
|
+
kind: "mutation",
|
|
54552
|
+
auth: "protected"
|
|
54553
|
+
}), method(object({
|
|
54554
|
+
deviceId: number().int().nonnegative(),
|
|
54555
|
+
key: string().min(1),
|
|
54556
|
+
value: union([
|
|
54557
|
+
boolean(),
|
|
54558
|
+
string(),
|
|
54559
|
+
number()
|
|
54560
|
+
])
|
|
54561
|
+
}), _void(), {
|
|
54562
|
+
kind: "mutation",
|
|
54563
|
+
auth: "protected"
|
|
54564
|
+
});
|
|
53361
54565
|
object({
|
|
53362
54566
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
53363
54567
|
lastPressedAt: number().nullable(),
|
|
@@ -53923,6 +55127,13 @@ object({
|
|
|
53923
55127
|
lastChangedAt: number()
|
|
53924
55128
|
});
|
|
53925
55129
|
DeviceType.Sensor;
|
|
55130
|
+
object({
|
|
55131
|
+
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
55132
|
+
position: number().min(0).max(100).nullable(),
|
|
55133
|
+
/** Ms epoch when the slice was last updated (push or poll). */
|
|
55134
|
+
lastFetchedAt: number()
|
|
55135
|
+
});
|
|
55136
|
+
DeviceType.Thermostat;
|
|
53926
55137
|
object({
|
|
53927
55138
|
/** Whether the humidifier is currently on. */
|
|
53928
55139
|
on: boolean(),
|
|
@@ -54733,7 +55944,19 @@ var TlsStatusSchema = object({
|
|
|
54733
55944
|
sans: array(string()),
|
|
54734
55945
|
caCertPem: string().nullable(),
|
|
54735
55946
|
reissueError: string().nullable(),
|
|
54736
|
-
restartRequired: boolean()
|
|
55947
|
+
restartRequired: boolean(),
|
|
55948
|
+
/** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
|
|
55949
|
+
caMissing: boolean(),
|
|
55950
|
+
/** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
|
|
55951
|
+
localCaConstrained: boolean().nullable(),
|
|
55952
|
+
/** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
|
|
55953
|
+
caReplacedAt: string().nullable(),
|
|
55954
|
+
/** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
|
|
55955
|
+
storedUpload: object({
|
|
55956
|
+
leafFingerprintSha256: string(),
|
|
55957
|
+
validTo: string(),
|
|
55958
|
+
hasCa: boolean()
|
|
55959
|
+
}).nullable()
|
|
54737
55960
|
});
|
|
54738
55961
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
54739
55962
|
/**
|
|
@@ -54789,6 +56012,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
54789
56012
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
54790
56013
|
kind: "mutation",
|
|
54791
56014
|
auth: "admin"
|
|
56015
|
+
}), method(_void(), TlsStatusSchema, {
|
|
56016
|
+
kind: "mutation",
|
|
56017
|
+
auth: "admin"
|
|
54792
56018
|
});
|
|
54793
56019
|
object({
|
|
54794
56020
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
@@ -59918,7 +61144,11 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
59918
61144
|
kind: "mutation",
|
|
59919
61145
|
auth: "admin",
|
|
59920
61146
|
access: "delete"
|
|
59921
|
-
}), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(
|
|
61147
|
+
}), method(object({ userId: string() }), TotpStatusSchema, { auth: "admin" }), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
61148
|
+
kind: "mutation",
|
|
61149
|
+
auth: "admin",
|
|
61150
|
+
access: "create"
|
|
61151
|
+
}), method(object({
|
|
59922
61152
|
userId: string(),
|
|
59923
61153
|
code: string()
|
|
59924
61154
|
}), object({ valid: boolean() }), {
|
|
@@ -60970,7 +62200,7 @@ var AccessoryKind = {
|
|
|
60970
62200
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
60971
62201
|
};
|
|
60972
62202
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
60973
|
-
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;
|
|
62203
|
+
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;
|
|
60974
62204
|
new Set(Object.values(DeviceType));
|
|
60975
62205
|
Object.freeze({
|
|
60976
62206
|
"accessories.getStatus": {
|
|
@@ -62119,6 +63349,12 @@ Object.freeze({
|
|
|
62119
63349
|
addonId: null,
|
|
62120
63350
|
access: "view"
|
|
62121
63351
|
},
|
|
63352
|
+
"coreBlocks.migrateRawSecrets": {
|
|
63353
|
+
capName: "core-blocks",
|
|
63354
|
+
capScope: "system",
|
|
63355
|
+
addonId: null,
|
|
63356
|
+
access: "create"
|
|
63357
|
+
},
|
|
62122
63358
|
"coreBlocks.restart": {
|
|
62123
63359
|
capName: "core-blocks",
|
|
62124
63360
|
capScope: "system",
|
|
@@ -63385,6 +64621,84 @@ Object.freeze({
|
|
|
63385
64621
|
addonId: null,
|
|
63386
64622
|
access: "view"
|
|
63387
64623
|
},
|
|
64624
|
+
"dishwasher.getStatus": {
|
|
64625
|
+
capName: "dishwasher",
|
|
64626
|
+
capScope: "device",
|
|
64627
|
+
addonId: null,
|
|
64628
|
+
access: "view"
|
|
64629
|
+
},
|
|
64630
|
+
"dishwasher.pause": {
|
|
64631
|
+
capName: "dishwasher",
|
|
64632
|
+
capScope: "device",
|
|
64633
|
+
addonId: null,
|
|
64634
|
+
access: "create"
|
|
64635
|
+
},
|
|
64636
|
+
"dishwasher.setChildLock": {
|
|
64637
|
+
capName: "dishwasher",
|
|
64638
|
+
capScope: "device",
|
|
64639
|
+
addonId: null,
|
|
64640
|
+
access: "create"
|
|
64641
|
+
},
|
|
64642
|
+
"dishwasher.setExtraDry": {
|
|
64643
|
+
capName: "dishwasher",
|
|
64644
|
+
capScope: "device",
|
|
64645
|
+
addonId: null,
|
|
64646
|
+
access: "create"
|
|
64647
|
+
},
|
|
64648
|
+
"dishwasher.setHalfLoad": {
|
|
64649
|
+
capName: "dishwasher",
|
|
64650
|
+
capScope: "device",
|
|
64651
|
+
addonId: null,
|
|
64652
|
+
access: "create"
|
|
64653
|
+
},
|
|
64654
|
+
"dishwasher.setHygienePlus": {
|
|
64655
|
+
capName: "dishwasher",
|
|
64656
|
+
capScope: "device",
|
|
64657
|
+
addonId: null,
|
|
64658
|
+
access: "create"
|
|
64659
|
+
},
|
|
64660
|
+
"dishwasher.setIntensiveZone": {
|
|
64661
|
+
capName: "dishwasher",
|
|
64662
|
+
capScope: "device",
|
|
64663
|
+
addonId: null,
|
|
64664
|
+
access: "create"
|
|
64665
|
+
},
|
|
64666
|
+
"dishwasher.setOption": {
|
|
64667
|
+
capName: "dishwasher",
|
|
64668
|
+
capScope: "device",
|
|
64669
|
+
addonId: null,
|
|
64670
|
+
access: "create"
|
|
64671
|
+
},
|
|
64672
|
+
"dishwasher.setPower": {
|
|
64673
|
+
capName: "dishwasher",
|
|
64674
|
+
capScope: "device",
|
|
64675
|
+
addonId: null,
|
|
64676
|
+
access: "create"
|
|
64677
|
+
},
|
|
64678
|
+
"dishwasher.setProgram": {
|
|
64679
|
+
capName: "dishwasher",
|
|
64680
|
+
capScope: "device",
|
|
64681
|
+
addonId: null,
|
|
64682
|
+
access: "create"
|
|
64683
|
+
},
|
|
64684
|
+
"dishwasher.setVarioSpeedPlus": {
|
|
64685
|
+
capName: "dishwasher",
|
|
64686
|
+
capScope: "device",
|
|
64687
|
+
addonId: null,
|
|
64688
|
+
access: "create"
|
|
64689
|
+
},
|
|
64690
|
+
"dishwasher.start": {
|
|
64691
|
+
capName: "dishwasher",
|
|
64692
|
+
capScope: "device",
|
|
64693
|
+
addonId: null,
|
|
64694
|
+
access: "create"
|
|
64695
|
+
},
|
|
64696
|
+
"dishwasher.stop": {
|
|
64697
|
+
capName: "dishwasher",
|
|
64698
|
+
capScope: "device",
|
|
64699
|
+
addonId: null,
|
|
64700
|
+
access: "create"
|
|
64701
|
+
},
|
|
63388
64702
|
"doorbell.getStatus": {
|
|
63389
64703
|
capName: "doorbell",
|
|
63390
64704
|
capScope: "device",
|
|
@@ -63595,6 +64909,12 @@ Object.freeze({
|
|
|
63595
64909
|
addonId: null,
|
|
63596
64910
|
access: "view"
|
|
63597
64911
|
},
|
|
64912
|
+
"heatingValve.getStatus": {
|
|
64913
|
+
capName: "heating-valve",
|
|
64914
|
+
capScope: "device",
|
|
64915
|
+
addonId: null,
|
|
64916
|
+
access: "view"
|
|
64917
|
+
},
|
|
63598
64918
|
"humidifier.getStatus": {
|
|
63599
64919
|
capName: "humidifier",
|
|
63600
64920
|
capScope: "device",
|
|
@@ -63805,6 +65125,12 @@ Object.freeze({
|
|
|
63805
65125
|
addonId: null,
|
|
63806
65126
|
access: "create"
|
|
63807
65127
|
},
|
|
65128
|
+
"keychainAdmin.importSecret": {
|
|
65129
|
+
capName: "keychain-admin",
|
|
65130
|
+
capScope: "system",
|
|
65131
|
+
addonId: null,
|
|
65132
|
+
access: "create"
|
|
65133
|
+
},
|
|
63808
65134
|
"keychainAdmin.listAudit": {
|
|
63809
65135
|
capName: "keychain-admin",
|
|
63810
65136
|
capScope: "system",
|
|
@@ -63973,6 +65299,12 @@ Object.freeze({
|
|
|
63973
65299
|
addonId: null,
|
|
63974
65300
|
access: "view"
|
|
63975
65301
|
},
|
|
65302
|
+
"llm.migrateRawSecrets": {
|
|
65303
|
+
capName: "llm",
|
|
65304
|
+
capScope: "system",
|
|
65305
|
+
addonId: null,
|
|
65306
|
+
access: "create"
|
|
65307
|
+
},
|
|
63976
65308
|
"llm.resolveModelRef": {
|
|
63977
65309
|
capName: "llm",
|
|
63978
65310
|
capScope: "system",
|
|
@@ -64201,6 +65533,12 @@ Object.freeze({
|
|
|
64201
65533
|
addonId: null,
|
|
64202
65534
|
access: "create"
|
|
64203
65535
|
},
|
|
65536
|
+
"localNetwork.useStoredUploadedCertificate": {
|
|
65537
|
+
capName: "local-network",
|
|
65538
|
+
capScope: "system",
|
|
65539
|
+
addonId: null,
|
|
65540
|
+
access: "create"
|
|
65541
|
+
},
|
|
64204
65542
|
"lockControl.getStatus": {
|
|
64205
65543
|
capName: "lock-control",
|
|
64206
65544
|
capScope: "device",
|
|
@@ -66853,6 +68191,30 @@ Object.freeze({
|
|
|
66853
68191
|
addonId: null,
|
|
66854
68192
|
access: "create"
|
|
66855
68193
|
},
|
|
68194
|
+
"recordEntries.deleteEntry": {
|
|
68195
|
+
capName: "record-entries",
|
|
68196
|
+
capScope: "system",
|
|
68197
|
+
addonId: null,
|
|
68198
|
+
access: "delete"
|
|
68199
|
+
},
|
|
68200
|
+
"recordEntries.listEntries": {
|
|
68201
|
+
capName: "record-entries",
|
|
68202
|
+
capScope: "system",
|
|
68203
|
+
addonId: null,
|
|
68204
|
+
access: "view"
|
|
68205
|
+
},
|
|
68206
|
+
"recordEntries.listEntryKinds": {
|
|
68207
|
+
capName: "record-entries",
|
|
68208
|
+
capScope: "system",
|
|
68209
|
+
addonId: null,
|
|
68210
|
+
access: "view"
|
|
68211
|
+
},
|
|
68212
|
+
"recordEntries.putEntry": {
|
|
68213
|
+
capName: "record-entries",
|
|
68214
|
+
capScope: "system",
|
|
68215
|
+
addonId: null,
|
|
68216
|
+
access: "create"
|
|
68217
|
+
},
|
|
66856
68218
|
"recording.getAvailability": {
|
|
66857
68219
|
capName: "recording",
|
|
66858
68220
|
capScope: "device",
|
|
@@ -67153,6 +68515,102 @@ Object.freeze({
|
|
|
67153
68515
|
addonId: null,
|
|
67154
68516
|
access: "view"
|
|
67155
68517
|
},
|
|
68518
|
+
"records.aggregate": {
|
|
68519
|
+
capName: "records",
|
|
68520
|
+
capScope: "system",
|
|
68521
|
+
addonId: null,
|
|
68522
|
+
access: "view"
|
|
68523
|
+
},
|
|
68524
|
+
"records.allocateId": {
|
|
68525
|
+
capName: "records",
|
|
68526
|
+
capScope: "system",
|
|
68527
|
+
addonId: null,
|
|
68528
|
+
access: "create"
|
|
68529
|
+
},
|
|
68530
|
+
"records.count": {
|
|
68531
|
+
capName: "records",
|
|
68532
|
+
capScope: "system",
|
|
68533
|
+
addonId: null,
|
|
68534
|
+
access: "view"
|
|
68535
|
+
},
|
|
68536
|
+
"records.declareKinds": {
|
|
68537
|
+
capName: "records",
|
|
68538
|
+
capScope: "system",
|
|
68539
|
+
addonId: null,
|
|
68540
|
+
access: "create"
|
|
68541
|
+
},
|
|
68542
|
+
"records.delete": {
|
|
68543
|
+
capName: "records",
|
|
68544
|
+
capScope: "system",
|
|
68545
|
+
addonId: null,
|
|
68546
|
+
access: "delete"
|
|
68547
|
+
},
|
|
68548
|
+
"records.deleteDevice": {
|
|
68549
|
+
capName: "records",
|
|
68550
|
+
capScope: "system",
|
|
68551
|
+
addonId: null,
|
|
68552
|
+
access: "delete"
|
|
68553
|
+
},
|
|
68554
|
+
"records.deleteMany": {
|
|
68555
|
+
capName: "records",
|
|
68556
|
+
capScope: "system",
|
|
68557
|
+
addonId: null,
|
|
68558
|
+
access: "delete"
|
|
68559
|
+
},
|
|
68560
|
+
"records.deleteWhere": {
|
|
68561
|
+
capName: "records",
|
|
68562
|
+
capScope: "system",
|
|
68563
|
+
addonId: null,
|
|
68564
|
+
access: "delete"
|
|
68565
|
+
},
|
|
68566
|
+
"records.get": {
|
|
68567
|
+
capName: "records",
|
|
68568
|
+
capScope: "system",
|
|
68569
|
+
addonId: null,
|
|
68570
|
+
access: "view"
|
|
68571
|
+
},
|
|
68572
|
+
"records.getMany": {
|
|
68573
|
+
capName: "records",
|
|
68574
|
+
capScope: "system",
|
|
68575
|
+
addonId: null,
|
|
68576
|
+
access: "view"
|
|
68577
|
+
},
|
|
68578
|
+
"records.histogram": {
|
|
68579
|
+
capName: "records",
|
|
68580
|
+
capScope: "system",
|
|
68581
|
+
addonId: null,
|
|
68582
|
+
access: "view"
|
|
68583
|
+
},
|
|
68584
|
+
"records.patch": {
|
|
68585
|
+
capName: "records",
|
|
68586
|
+
capScope: "system",
|
|
68587
|
+
addonId: null,
|
|
68588
|
+
access: "create"
|
|
68589
|
+
},
|
|
68590
|
+
"records.put": {
|
|
68591
|
+
capName: "records",
|
|
68592
|
+
capScope: "system",
|
|
68593
|
+
addonId: null,
|
|
68594
|
+
access: "create"
|
|
68595
|
+
},
|
|
68596
|
+
"records.putMany": {
|
|
68597
|
+
capName: "records",
|
|
68598
|
+
capScope: "system",
|
|
68599
|
+
addonId: null,
|
|
68600
|
+
access: "create"
|
|
68601
|
+
},
|
|
68602
|
+
"records.query": {
|
|
68603
|
+
capName: "records",
|
|
68604
|
+
capScope: "system",
|
|
68605
|
+
addonId: null,
|
|
68606
|
+
access: "view"
|
|
68607
|
+
},
|
|
68608
|
+
"records.secretRefs": {
|
|
68609
|
+
capName: "records",
|
|
68610
|
+
capScope: "system",
|
|
68611
|
+
addonId: null,
|
|
68612
|
+
access: "view"
|
|
68613
|
+
},
|
|
67156
68614
|
"sceneMonitor.captureReference": {
|
|
67157
68615
|
capName: "scene-monitor",
|
|
67158
68616
|
capScope: "device",
|
|
@@ -67585,6 +69043,48 @@ Object.freeze({
|
|
|
67585
69043
|
addonId: null,
|
|
67586
69044
|
access: "create"
|
|
67587
69045
|
},
|
|
69046
|
+
"storageAdmin.adoptRecords": {
|
|
69047
|
+
capName: "storage-admin",
|
|
69048
|
+
capScope: "system",
|
|
69049
|
+
addonId: null,
|
|
69050
|
+
access: "create"
|
|
69051
|
+
},
|
|
69052
|
+
"storageAdmin.dropCollection": {
|
|
69053
|
+
capName: "storage-admin",
|
|
69054
|
+
capScope: "system",
|
|
69055
|
+
addonId: null,
|
|
69056
|
+
access: "delete"
|
|
69057
|
+
},
|
|
69058
|
+
"storageAdmin.isAdoptedLegacyTable": {
|
|
69059
|
+
capName: "storage-admin",
|
|
69060
|
+
capScope: "system",
|
|
69061
|
+
addonId: null,
|
|
69062
|
+
access: "view"
|
|
69063
|
+
},
|
|
69064
|
+
"storageAdmin.listKinds": {
|
|
69065
|
+
capName: "storage-admin",
|
|
69066
|
+
capScope: "system",
|
|
69067
|
+
addonId: null,
|
|
69068
|
+
access: "view"
|
|
69069
|
+
},
|
|
69070
|
+
"storageAdmin.listTables": {
|
|
69071
|
+
capName: "storage-admin",
|
|
69072
|
+
capScope: "system",
|
|
69073
|
+
addonId: null,
|
|
69074
|
+
access: "view"
|
|
69075
|
+
},
|
|
69076
|
+
"storageAdmin.readLegacyCollection": {
|
|
69077
|
+
capName: "storage-admin",
|
|
69078
|
+
capScope: "system",
|
|
69079
|
+
addonId: null,
|
|
69080
|
+
access: "view"
|
|
69081
|
+
},
|
|
69082
|
+
"storageAdmin.readRecords": {
|
|
69083
|
+
capName: "storage-admin",
|
|
69084
|
+
capScope: "system",
|
|
69085
|
+
addonId: null,
|
|
69086
|
+
access: "view"
|
|
69087
|
+
},
|
|
67588
69088
|
"storageEvictable.evict": {
|
|
67589
69089
|
capName: "storage-evictable",
|
|
67590
69090
|
capScope: "system",
|
|
@@ -68497,6 +69997,12 @@ Object.freeze({
|
|
|
68497
69997
|
addonId: null,
|
|
68498
69998
|
access: "view"
|
|
68499
69999
|
},
|
|
70000
|
+
"userManagement.migrateRawSecrets": {
|
|
70001
|
+
capName: "user-management",
|
|
70002
|
+
capScope: "system",
|
|
70003
|
+
addonId: null,
|
|
70004
|
+
access: "create"
|
|
70005
|
+
},
|
|
68500
70006
|
"userManagement.oauthExchangeCode": {
|
|
68501
70007
|
capName: "user-management",
|
|
68502
70008
|
capScope: "system",
|
|
@@ -68996,7 +70502,7 @@ Object.freeze({
|
|
|
68996
70502
|
access: "create"
|
|
68997
70503
|
}
|
|
68998
70504
|
});
|
|
68999
|
-
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;
|
|
70505
|
+
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;
|
|
69000
70506
|
DeviceFeature.BatteryOperated;
|
|
69001
70507
|
Object.freeze({
|
|
69002
70508
|
"accessories.getStatus": [{
|
|
@@ -69820,6 +71326,71 @@ Object.freeze({
|
|
|
69820
71326
|
form: "single",
|
|
69821
71327
|
optional: false
|
|
69822
71328
|
}],
|
|
71329
|
+
"dishwasher.getStatus": [{
|
|
71330
|
+
name: "deviceId",
|
|
71331
|
+
form: "single",
|
|
71332
|
+
optional: false
|
|
71333
|
+
}],
|
|
71334
|
+
"dishwasher.pause": [{
|
|
71335
|
+
name: "deviceId",
|
|
71336
|
+
form: "single",
|
|
71337
|
+
optional: false
|
|
71338
|
+
}],
|
|
71339
|
+
"dishwasher.setChildLock": [{
|
|
71340
|
+
name: "deviceId",
|
|
71341
|
+
form: "single",
|
|
71342
|
+
optional: false
|
|
71343
|
+
}],
|
|
71344
|
+
"dishwasher.setExtraDry": [{
|
|
71345
|
+
name: "deviceId",
|
|
71346
|
+
form: "single",
|
|
71347
|
+
optional: false
|
|
71348
|
+
}],
|
|
71349
|
+
"dishwasher.setHalfLoad": [{
|
|
71350
|
+
name: "deviceId",
|
|
71351
|
+
form: "single",
|
|
71352
|
+
optional: false
|
|
71353
|
+
}],
|
|
71354
|
+
"dishwasher.setHygienePlus": [{
|
|
71355
|
+
name: "deviceId",
|
|
71356
|
+
form: "single",
|
|
71357
|
+
optional: false
|
|
71358
|
+
}],
|
|
71359
|
+
"dishwasher.setIntensiveZone": [{
|
|
71360
|
+
name: "deviceId",
|
|
71361
|
+
form: "single",
|
|
71362
|
+
optional: false
|
|
71363
|
+
}],
|
|
71364
|
+
"dishwasher.setOption": [{
|
|
71365
|
+
name: "deviceId",
|
|
71366
|
+
form: "single",
|
|
71367
|
+
optional: false
|
|
71368
|
+
}],
|
|
71369
|
+
"dishwasher.setPower": [{
|
|
71370
|
+
name: "deviceId",
|
|
71371
|
+
form: "single",
|
|
71372
|
+
optional: false
|
|
71373
|
+
}],
|
|
71374
|
+
"dishwasher.setProgram": [{
|
|
71375
|
+
name: "deviceId",
|
|
71376
|
+
form: "single",
|
|
71377
|
+
optional: false
|
|
71378
|
+
}],
|
|
71379
|
+
"dishwasher.setVarioSpeedPlus": [{
|
|
71380
|
+
name: "deviceId",
|
|
71381
|
+
form: "single",
|
|
71382
|
+
optional: false
|
|
71383
|
+
}],
|
|
71384
|
+
"dishwasher.start": [{
|
|
71385
|
+
name: "deviceId",
|
|
71386
|
+
form: "single",
|
|
71387
|
+
optional: false
|
|
71388
|
+
}],
|
|
71389
|
+
"dishwasher.stop": [{
|
|
71390
|
+
name: "deviceId",
|
|
71391
|
+
form: "single",
|
|
71392
|
+
optional: false
|
|
71393
|
+
}],
|
|
69823
71394
|
"doorbell.getStatus": [{
|
|
69824
71395
|
name: "deviceId",
|
|
69825
71396
|
form: "single",
|
|
@@ -69889,6 +71460,11 @@ Object.freeze({
|
|
|
69889
71460
|
form: "single",
|
|
69890
71461
|
optional: false
|
|
69891
71462
|
}],
|
|
71463
|
+
"heatingValve.getStatus": [{
|
|
71464
|
+
name: "deviceId",
|
|
71465
|
+
form: "single",
|
|
71466
|
+
optional: false
|
|
71467
|
+
}],
|
|
69892
71468
|
"humidifier.getStatus": [{
|
|
69893
71469
|
name: "deviceId",
|
|
69894
71470
|
form: "single",
|
|
@@ -71173,6 +72749,37 @@ Object.freeze({
|
|
|
71173
72749
|
form: "single",
|
|
71174
72750
|
optional: false
|
|
71175
72751
|
}],
|
|
72752
|
+
"records.count": [{
|
|
72753
|
+
name: "deviceId",
|
|
72754
|
+
form: "single",
|
|
72755
|
+
optional: true
|
|
72756
|
+
}],
|
|
72757
|
+
"records.deleteDevice": [{
|
|
72758
|
+
name: "deviceId",
|
|
72759
|
+
form: "single",
|
|
72760
|
+
optional: false
|
|
72761
|
+
}],
|
|
72762
|
+
"records.deleteWhere": [{
|
|
72763
|
+
name: "deviceId",
|
|
72764
|
+
form: "single",
|
|
72765
|
+
optional: true
|
|
72766
|
+
}],
|
|
72767
|
+
"records.histogram": [{
|
|
72768
|
+
name: "deviceId",
|
|
72769
|
+
form: "single",
|
|
72770
|
+
optional: true
|
|
72771
|
+
}],
|
|
72772
|
+
"records.putMany": [{
|
|
72773
|
+
name: "records",
|
|
72774
|
+
form: "object-array",
|
|
72775
|
+
optional: false,
|
|
72776
|
+
itemField: "deviceId"
|
|
72777
|
+
}],
|
|
72778
|
+
"records.query": [{
|
|
72779
|
+
name: "deviceId",
|
|
72780
|
+
form: "single",
|
|
72781
|
+
optional: true
|
|
72782
|
+
}],
|
|
71176
72783
|
"sceneMonitor.captureReference": [{
|
|
71177
72784
|
name: "deviceId",
|
|
71178
72785
|
form: "single",
|
|
@@ -71289,6 +72896,17 @@ Object.freeze({
|
|
|
71289
72896
|
form: "single",
|
|
71290
72897
|
optional: false
|
|
71291
72898
|
}],
|
|
72899
|
+
"storageAdmin.adoptRecords": [{
|
|
72900
|
+
name: "records",
|
|
72901
|
+
form: "object-array",
|
|
72902
|
+
optional: false,
|
|
72903
|
+
itemField: "deviceId"
|
|
72904
|
+
}],
|
|
72905
|
+
"storageAdmin.readRecords": [{
|
|
72906
|
+
name: "deviceId",
|
|
72907
|
+
form: "single",
|
|
72908
|
+
optional: true
|
|
72909
|
+
}],
|
|
71292
72910
|
"streamBroker.acquireEgressTranscode": [{
|
|
71293
72911
|
name: "deviceId",
|
|
71294
72912
|
form: "single",
|
|
@@ -104872,6 +106490,205 @@ function retiredCiphertextPaths(config) {
|
|
|
104872
106490
|
return out;
|
|
104873
106491
|
}
|
|
104874
106492
|
//#endregion
|
|
106493
|
+
//#region src/mqtt-external-broker-entry.ts
|
|
106494
|
+
var mqttOnboardDescriptorSchema = object({
|
|
106495
|
+
port: number().int().min(1).max(65535),
|
|
106496
|
+
allowAnonymous: boolean()
|
|
106497
|
+
});
|
|
106498
|
+
var MqttExternalBrokerEntrySchema = object({
|
|
106499
|
+
id: string().min(1),
|
|
106500
|
+
name: string().min(1),
|
|
106501
|
+
url: string().min(1),
|
|
106502
|
+
username: string().optional(),
|
|
106503
|
+
password: string().optional().meta({ secret: true }),
|
|
106504
|
+
clientIdPrefix: string().optional(),
|
|
106505
|
+
onboard: mqttOnboardDescriptorSchema.optional()
|
|
106506
|
+
});
|
|
106507
|
+
var mqttBrokersEntryKind = defineEntryKind("brokers", {
|
|
106508
|
+
title: "MQTT external brokers",
|
|
106509
|
+
schema: entryBody({
|
|
106510
|
+
url: string().min(1),
|
|
106511
|
+
username: string().optional(),
|
|
106512
|
+
password: string().optional().meta({ secret: true }),
|
|
106513
|
+
clientIdPrefix: string().optional(),
|
|
106514
|
+
onboard: mqttOnboardDescriptorSchema.optional()
|
|
106515
|
+
}),
|
|
106516
|
+
writeTrigger: "an operator adds or removes an external MQTT broker entry",
|
|
106517
|
+
adoptsFrom: settingsBrokersAdoptsFrom("mqtt-broker")
|
|
106518
|
+
});
|
|
106519
|
+
//#endregion
|
|
106520
|
+
//#region src/mqtt-entry-record-kinds.ts
|
|
106521
|
+
var aclAccessSchema = _enum([
|
|
106522
|
+
"sub",
|
|
106523
|
+
"pub",
|
|
106524
|
+
"both"
|
|
106525
|
+
]);
|
|
106526
|
+
var MqttClientBodySchema = entryBody({ passwordHash: string() });
|
|
106527
|
+
var mqttClientEntryKind = defineEntryKind("mqtt-client", {
|
|
106528
|
+
title: "MQTT embedded clients",
|
|
106529
|
+
schema: MqttClientBodySchema,
|
|
106530
|
+
writeTrigger: "an operator adds or removes an embedded MQTT client credential",
|
|
106531
|
+
adoptsFrom: settingsArrayAdoptsFrom("mqtt-broker", "embeddedClients")
|
|
106532
|
+
});
|
|
106533
|
+
var MqttAclBodySchema = entryBody({
|
|
106534
|
+
username: string().min(1),
|
|
106535
|
+
topicPattern: string().min(1),
|
|
106536
|
+
access: aclAccessSchema
|
|
106537
|
+
});
|
|
106538
|
+
var mqttAclEntryKind = defineEntryKind("mqtt-acl-rule", {
|
|
106539
|
+
title: "MQTT ACL rules",
|
|
106540
|
+
schema: MqttAclBodySchema,
|
|
106541
|
+
writeTrigger: "an operator edits embedded broker ACL rules",
|
|
106542
|
+
adoptsFrom: settingsArrayAdoptsFrom("mqtt-broker", "embeddedAcl")
|
|
106543
|
+
});
|
|
106544
|
+
//#endregion
|
|
106545
|
+
//#region src/mqtt-broker-store.ts
|
|
106546
|
+
function rowToEntry(id, data) {
|
|
106547
|
+
const parsed = MqttExternalBrokerEntrySchema.safeParse({
|
|
106548
|
+
id,
|
|
106549
|
+
name: data.name,
|
|
106550
|
+
url: data.url,
|
|
106551
|
+
username: data.username,
|
|
106552
|
+
password: data.password,
|
|
106553
|
+
clientIdPrefix: data.clientIdPrefix,
|
|
106554
|
+
onboard: data.onboard
|
|
106555
|
+
});
|
|
106556
|
+
return parsed.success ? parsed.data : null;
|
|
106557
|
+
}
|
|
106558
|
+
var MqttBrokerStore = class MqttBrokerStore {
|
|
106559
|
+
kind;
|
|
106560
|
+
constructor(kind) {
|
|
106561
|
+
this.kind = kind;
|
|
106562
|
+
}
|
|
106563
|
+
static of(records) {
|
|
106564
|
+
return new MqttBrokerStore(records.kind(mqttBrokersEntryKind));
|
|
106565
|
+
}
|
|
106566
|
+
async listResolved() {
|
|
106567
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
106568
|
+
const out = [];
|
|
106569
|
+
for (const record of page.records) {
|
|
106570
|
+
let data = record.data;
|
|
106571
|
+
try {
|
|
106572
|
+
const resolved = await this.kind.getResolved(record.id);
|
|
106573
|
+
if (resolved !== null) data = resolved.data;
|
|
106574
|
+
} catch (err) {
|
|
106575
|
+
if (!(err instanceof RecordSecretUnavailableError)) throw err;
|
|
106576
|
+
}
|
|
106577
|
+
const entry = rowToEntry(record.id, data);
|
|
106578
|
+
if (entry !== null) out.push(entry);
|
|
106579
|
+
}
|
|
106580
|
+
return out;
|
|
106581
|
+
}
|
|
106582
|
+
async replaceAll(entries) {
|
|
106583
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
106584
|
+
const nextIds = new Set(entries.map((e) => e.id));
|
|
106585
|
+
for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
|
|
106586
|
+
for (const entry of entries) {
|
|
106587
|
+
const { id, name, url, username, password, clientIdPrefix, onboard } = entry;
|
|
106588
|
+
const body = {
|
|
106589
|
+
name,
|
|
106590
|
+
url,
|
|
106591
|
+
...username === void 0 ? {} : { username },
|
|
106592
|
+
...password === void 0 ? {} : { password },
|
|
106593
|
+
...clientIdPrefix === void 0 ? {} : { clientIdPrefix },
|
|
106594
|
+
...onboard === void 0 ? {} : { onboard }
|
|
106595
|
+
};
|
|
106596
|
+
const parsed = mqttBrokersEntryKind.schema.safeParse(body);
|
|
106597
|
+
if (!parsed.success) throw new Error(`invalid MQTT broker '${id}': ${parsed.error.message}`);
|
|
106598
|
+
await this.kind.put({
|
|
106599
|
+
id,
|
|
106600
|
+
data: parsed.data
|
|
106601
|
+
});
|
|
106602
|
+
}
|
|
106603
|
+
}
|
|
106604
|
+
};
|
|
106605
|
+
//#endregion
|
|
106606
|
+
//#region src/mqtt-entry-store.ts
|
|
106607
|
+
function mqttAclRecordId(rule) {
|
|
106608
|
+
return `${rule.username}\u0000${rule.topicPattern}`;
|
|
106609
|
+
}
|
|
106610
|
+
var MqttClientStore = class MqttClientStore {
|
|
106611
|
+
kind;
|
|
106612
|
+
constructor(kind) {
|
|
106613
|
+
this.kind = kind;
|
|
106614
|
+
}
|
|
106615
|
+
static of(records) {
|
|
106616
|
+
return new MqttClientStore(records.kind(mqttClientEntryKind));
|
|
106617
|
+
}
|
|
106618
|
+
async listResolved() {
|
|
106619
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
106620
|
+
const out = [];
|
|
106621
|
+
for (const record of page.records) {
|
|
106622
|
+
const parsed = MqttClientBodySchema.safeParse(record.data);
|
|
106623
|
+
if (!parsed.success) continue;
|
|
106624
|
+
out.push({
|
|
106625
|
+
username: record.id,
|
|
106626
|
+
passwordHash: parsed.data.passwordHash
|
|
106627
|
+
});
|
|
106628
|
+
}
|
|
106629
|
+
return out;
|
|
106630
|
+
}
|
|
106631
|
+
async replaceAll(entries) {
|
|
106632
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
106633
|
+
const nextIds = new Set(entries.map((e) => e.username));
|
|
106634
|
+
for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
|
|
106635
|
+
for (const entry of entries) {
|
|
106636
|
+
const body = {
|
|
106637
|
+
name: entry.username,
|
|
106638
|
+
passwordHash: entry.passwordHash
|
|
106639
|
+
};
|
|
106640
|
+
const parsed = mqttClientEntryKind.schema.safeParse(body);
|
|
106641
|
+
if (!parsed.success) throw new Error(`invalid MQTT client '${entry.username}': ${parsed.error.message}`);
|
|
106642
|
+
await this.kind.put({
|
|
106643
|
+
id: entry.username,
|
|
106644
|
+
data: parsed.data
|
|
106645
|
+
});
|
|
106646
|
+
}
|
|
106647
|
+
}
|
|
106648
|
+
};
|
|
106649
|
+
var MqttAclStore = class MqttAclStore {
|
|
106650
|
+
kind;
|
|
106651
|
+
constructor(kind) {
|
|
106652
|
+
this.kind = kind;
|
|
106653
|
+
}
|
|
106654
|
+
static of(records) {
|
|
106655
|
+
return new MqttAclStore(records.kind(mqttAclEntryKind));
|
|
106656
|
+
}
|
|
106657
|
+
async listResolved() {
|
|
106658
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
106659
|
+
const out = [];
|
|
106660
|
+
for (const record of page.records) {
|
|
106661
|
+
const parsed = MqttAclBodySchema.safeParse(record.data);
|
|
106662
|
+
if (!parsed.success) continue;
|
|
106663
|
+
out.push({
|
|
106664
|
+
username: parsed.data.username,
|
|
106665
|
+
topicPattern: parsed.data.topicPattern,
|
|
106666
|
+
access: parsed.data.access
|
|
106667
|
+
});
|
|
106668
|
+
}
|
|
106669
|
+
return out;
|
|
106670
|
+
}
|
|
106671
|
+
async replaceAll(entries) {
|
|
106672
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
106673
|
+
const nextIds = new Set(entries.map((e) => mqttAclRecordId(e)));
|
|
106674
|
+
for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
|
|
106675
|
+
for (const entry of entries) {
|
|
106676
|
+
const body = {
|
|
106677
|
+
name: `${entry.username} · ${entry.topicPattern}`,
|
|
106678
|
+
username: entry.username,
|
|
106679
|
+
topicPattern: entry.topicPattern,
|
|
106680
|
+
access: entry.access
|
|
106681
|
+
};
|
|
106682
|
+
const parsed = mqttAclEntryKind.schema.safeParse(body);
|
|
106683
|
+
if (!parsed.success) throw new Error(`invalid MQTT ACL '${entry.username}/${entry.topicPattern}': ${parsed.error.message}`);
|
|
106684
|
+
await this.kind.put({
|
|
106685
|
+
id: mqttAclRecordId(entry),
|
|
106686
|
+
data: parsed.data
|
|
106687
|
+
});
|
|
106688
|
+
}
|
|
106689
|
+
}
|
|
106690
|
+
};
|
|
106691
|
+
//#endregion
|
|
104875
106692
|
//#region src/mqtt-broker.addon.ts
|
|
104876
106693
|
/**
|
|
104877
106694
|
* MQTT Broker addon — broker REGISTRY (not a pub/sub proxy).
|
|
@@ -104905,7 +106722,7 @@ function retiredCiphertextPaths(config) {
|
|
|
104905
106722
|
* integration-type addon.
|
|
104906
106723
|
*
|
|
104907
106724
|
* Lifecycle:
|
|
104908
|
-
* - External broker entries are persisted in `this.
|
|
106725
|
+
* - External broker entries are persisted in `this.activeExternalBrokers`.
|
|
104909
106726
|
* - The embedded broker is persisted as flat `embedded*` config keys
|
|
104910
106727
|
* (`embeddedEnabled`, `embeddedPort`, …) so the standard settings
|
|
104911
106728
|
* form can drive it; {@link MqttBrokerAddon.deriveEmbeddedEntry}
|
|
@@ -105024,11 +106841,86 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105024
106841
|
/** Per-entry onboard `aedes` runtimes, keyed by `ExternalBrokerEntry.id`. */
|
|
105025
106842
|
onboardRuntimes = /* @__PURE__ */ new Map();
|
|
105026
106843
|
probeCache = null;
|
|
106844
|
+
brokerStore = null;
|
|
106845
|
+
clientStore = null;
|
|
106846
|
+
aclStore = null;
|
|
106847
|
+
/** External broker rows (records when B2 is live, else settings mirror). */
|
|
106848
|
+
activeExternalBrokers = [];
|
|
106849
|
+
activeEmbeddedClients = [];
|
|
106850
|
+
activeEmbeddedAcl = [];
|
|
105027
106851
|
constructor() {
|
|
105028
106852
|
super({ ...DEFAULT_CONFIG });
|
|
105029
106853
|
}
|
|
106854
|
+
async loadPersistedExternalBrokers() {
|
|
106855
|
+
const store = this.brokerStore;
|
|
106856
|
+
if (store !== null) {
|
|
106857
|
+
const fromRecords = await store.listResolved();
|
|
106858
|
+
if (fromRecords.length > 0) return fromRecords;
|
|
106859
|
+
}
|
|
106860
|
+
return this.config.brokers;
|
|
106861
|
+
}
|
|
106862
|
+
async persistExternalBrokers(entries) {
|
|
106863
|
+
this.activeExternalBrokers = [...entries];
|
|
106864
|
+
const store = this.brokerStore;
|
|
106865
|
+
if (store !== null) {
|
|
106866
|
+
await store.replaceAll(entries);
|
|
106867
|
+
return;
|
|
106868
|
+
}
|
|
106869
|
+
await this.updateGlobalSettings({ brokers: entries });
|
|
106870
|
+
}
|
|
106871
|
+
async loadEmbeddedClients() {
|
|
106872
|
+
const store = this.clientStore;
|
|
106873
|
+
if (store !== null) {
|
|
106874
|
+
const fromRecords = await store.listResolved();
|
|
106875
|
+
if (fromRecords.length > 0) return fromRecords;
|
|
106876
|
+
}
|
|
106877
|
+
return this.config.embeddedClients;
|
|
106878
|
+
}
|
|
106879
|
+
async loadEmbeddedAcl() {
|
|
106880
|
+
const store = this.aclStore;
|
|
106881
|
+
if (store !== null) {
|
|
106882
|
+
const fromRecords = await store.listResolved();
|
|
106883
|
+
if (fromRecords.length > 0) return fromRecords;
|
|
106884
|
+
}
|
|
106885
|
+
return this.config.embeddedAcl;
|
|
106886
|
+
}
|
|
106887
|
+
async syncEmbeddedEntriesFromConfig() {
|
|
106888
|
+
const clients = [...this.config.embeddedClients];
|
|
106889
|
+
const acl = [...this.config.embeddedAcl];
|
|
106890
|
+
this.activeEmbeddedClients = clients;
|
|
106891
|
+
this.activeEmbeddedAcl = acl;
|
|
106892
|
+
const clientStore = this.clientStore;
|
|
106893
|
+
const aclStore = this.aclStore;
|
|
106894
|
+
if (clientStore !== null) await clientStore.replaceAll(clients);
|
|
106895
|
+
if (aclStore !== null) await aclStore.replaceAll(acl);
|
|
106896
|
+
}
|
|
105030
106897
|
async onInitialize() {
|
|
105031
106898
|
this.reportRetiredCiphertext();
|
|
106899
|
+
const records = this.ctx.records;
|
|
106900
|
+
if (records === void 0) throw new Error("mqtt-broker: ctx.records is required (hub-only addon)");
|
|
106901
|
+
await records.declare({
|
|
106902
|
+
brokers: mqttBrokersEntryKind,
|
|
106903
|
+
"mqtt-client": mqttClientEntryKind,
|
|
106904
|
+
"mqtt-acl-rule": mqttAclEntryKind
|
|
106905
|
+
});
|
|
106906
|
+
this.brokerStore = MqttBrokerStore.of(records);
|
|
106907
|
+
this.clientStore = MqttClientStore.of(records);
|
|
106908
|
+
this.aclStore = MqttAclStore.of(records);
|
|
106909
|
+
this.activeExternalBrokers = await this.loadPersistedExternalBrokers();
|
|
106910
|
+
if ((await this.brokerStore.listResolved()).length === 0 && this.config.brokers.length > 0) {
|
|
106911
|
+
await this.brokerStore.replaceAll(this.config.brokers);
|
|
106912
|
+
this.activeExternalBrokers = await this.loadPersistedExternalBrokers();
|
|
106913
|
+
}
|
|
106914
|
+
this.activeEmbeddedClients = await this.loadEmbeddedClients();
|
|
106915
|
+
this.activeEmbeddedAcl = await this.loadEmbeddedAcl();
|
|
106916
|
+
if ((await this.clientStore.listResolved()).length === 0 && this.config.embeddedClients.length > 0) {
|
|
106917
|
+
await this.clientStore.replaceAll(this.config.embeddedClients);
|
|
106918
|
+
this.activeEmbeddedClients = await this.loadEmbeddedClients();
|
|
106919
|
+
}
|
|
106920
|
+
if ((await this.aclStore.listResolved()).length === 0 && this.config.embeddedAcl.length > 0) {
|
|
106921
|
+
await this.aclStore.replaceAll(this.config.embeddedAcl);
|
|
106922
|
+
this.activeEmbeddedAcl = await this.loadEmbeddedAcl();
|
|
106923
|
+
}
|
|
105032
106924
|
this.probeCache = new BrokerProbeCache({
|
|
105033
106925
|
intervalMs: PROBE_INTERVAL_MS,
|
|
105034
106926
|
onError: (id, err) => {
|
|
@@ -105074,7 +106966,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105074
106966
|
startEmbeddedBroker: async (input) => this.startEmbeddedBroker(input),
|
|
105075
106967
|
stopEmbeddedBroker: async ({ id }) => this.stopEmbeddedBroker(id),
|
|
105076
106968
|
getStatus: async () => ({
|
|
105077
|
-
brokerCount: this.
|
|
106969
|
+
brokerCount: this.activeExternalBrokers.length + (this.config.embeddedEnabled ? 1 : 0),
|
|
105078
106970
|
embeddedRunning: this.embeddedRuntime !== null
|
|
105079
106971
|
})
|
|
105080
106972
|
};
|
|
@@ -105107,7 +106999,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105107
106999
|
error: info.error ?? null
|
|
105108
107000
|
});
|
|
105109
107001
|
const owns = (id) => {
|
|
105110
|
-
if (this.
|
|
107002
|
+
if (this.activeExternalBrokers.some((b) => b.id === id)) return true;
|
|
105111
107003
|
const embedded = this.deriveEmbeddedEntry();
|
|
105112
107004
|
return embedded !== null && embedded.id === id;
|
|
105113
107005
|
};
|
|
@@ -105201,7 +107093,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105201
107093
|
getStatus: async () => {
|
|
105202
107094
|
const all = await this.listBrokers();
|
|
105203
107095
|
return {
|
|
105204
|
-
brokerCount: this.
|
|
107096
|
+
brokerCount: this.activeExternalBrokers.length + (this.config.embeddedEnabled ? 1 : 0),
|
|
105205
107097
|
connectedCount: all.filter((b) => b.status === "connected").length
|
|
105206
107098
|
};
|
|
105207
107099
|
}
|
|
@@ -105213,6 +107105,8 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105213
107105
|
await this.shutdownAllOnboard();
|
|
105214
107106
|
}
|
|
105215
107107
|
async onConfigChanged() {
|
|
107108
|
+
this.activeExternalBrokers = await this.loadPersistedExternalBrokers();
|
|
107109
|
+
await this.syncEmbeddedEntriesFromConfig();
|
|
105216
107110
|
await this.reconcileOnboardRuntimes();
|
|
105217
107111
|
const desired = this.deriveEmbeddedEntry();
|
|
105218
107112
|
const running = this.embeddedRuntime?.entry ?? null;
|
|
@@ -105239,7 +107133,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105239
107133
|
}
|
|
105240
107134
|
async listBrokers() {
|
|
105241
107135
|
const out = [];
|
|
105242
|
-
for (const entry of this.
|
|
107136
|
+
for (const entry of this.activeExternalBrokers) {
|
|
105243
107137
|
if (entry.onboard) {
|
|
105244
107138
|
const running = this.onboardRuntimes.get(entry.id) ?? null;
|
|
105245
107139
|
const status = running !== null ? "connected" : "disconnected";
|
|
@@ -105288,7 +107182,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105288
107182
|
return out;
|
|
105289
107183
|
}
|
|
105290
107184
|
getBrokerConfig(id) {
|
|
105291
|
-
const external = this.
|
|
107185
|
+
const external = this.activeExternalBrokers.find((b) => b.id === id);
|
|
105292
107186
|
if (external) {
|
|
105293
107187
|
const password = this.storedPassword(external.password);
|
|
105294
107188
|
return {
|
|
@@ -105319,8 +107213,8 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105319
107213
|
...input.password !== void 0 ? { password: input.password } : {},
|
|
105320
107214
|
...input.clientIdPrefix !== void 0 ? { clientIdPrefix: input.clientIdPrefix } : {}
|
|
105321
107215
|
};
|
|
105322
|
-
const next = [...this.
|
|
105323
|
-
await this.
|
|
107216
|
+
const next = [...this.activeExternalBrokers, entry];
|
|
107217
|
+
await this.persistExternalBrokers(next);
|
|
105324
107218
|
this.ctx.logger.info("mqtt broker added", { meta: {
|
|
105325
107219
|
id,
|
|
105326
107220
|
url: entry.url
|
|
@@ -105349,8 +107243,8 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105349
107243
|
...built.password !== void 0 ? { password: built.password } : {},
|
|
105350
107244
|
onboard: built.onboard
|
|
105351
107245
|
};
|
|
105352
|
-
const next = [...this.
|
|
105353
|
-
await this.
|
|
107246
|
+
const next = [...this.activeExternalBrokers, entry];
|
|
107247
|
+
await this.persistExternalBrokers(next);
|
|
105354
107248
|
const spawn = this.onboardSpawnEntry(entry);
|
|
105355
107249
|
if (spawn) await this.spawnOnboard(spawn);
|
|
105356
107250
|
this.ctx.logger.info("onboard mqtt broker added", {
|
|
@@ -105366,10 +107260,10 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105366
107260
|
return { id };
|
|
105367
107261
|
}
|
|
105368
107262
|
async removeBroker(id) {
|
|
105369
|
-
const removed = this.
|
|
107263
|
+
const removed = this.activeExternalBrokers.find((b) => b.id === id);
|
|
105370
107264
|
if (removed) {
|
|
105371
|
-
const next = this.
|
|
105372
|
-
await this.
|
|
107265
|
+
const next = this.activeExternalBrokers.filter((b) => b.id !== id);
|
|
107266
|
+
await this.persistExternalBrokers(next);
|
|
105373
107267
|
this.probeCache?.evict(id);
|
|
105374
107268
|
if (removed.onboard) await this.shutdownOnboard(id);
|
|
105375
107269
|
this.ctx.logger.info("mqtt broker removed", { meta: { id } });
|
|
@@ -105590,7 +107484,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105590
107484
|
* entry disappeared, and respawn runtimes whose port changed.
|
|
105591
107485
|
*/
|
|
105592
107486
|
async reconcileOnboardRuntimes() {
|
|
105593
|
-
const desiredEntries = this.
|
|
107487
|
+
const desiredEntries = this.activeExternalBrokers.filter(hasOnboard);
|
|
105594
107488
|
const desired = desiredEntries.map((b) => ({
|
|
105595
107489
|
id: b.id,
|
|
105596
107490
|
port: b.onboard.port
|
|
@@ -105631,7 +107525,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105631
107525
|
done(null, allowAnonymous);
|
|
105632
107526
|
return;
|
|
105633
107527
|
}
|
|
105634
|
-
const match = this.
|
|
107528
|
+
const match = this.activeEmbeddedClients.find((c) => c.username === username);
|
|
105635
107529
|
if (match) {
|
|
105636
107530
|
done(null, bcryptCompareSafe(passwordStr, match.passwordHash));
|
|
105637
107531
|
return;
|
|
@@ -105671,7 +107565,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105671
107565
|
* requested topic and access mode.
|
|
105672
107566
|
*/
|
|
105673
107567
|
isAclAllowed(client, topic, access) {
|
|
105674
|
-
const acl = this.
|
|
107568
|
+
const acl = this.activeEmbeddedAcl;
|
|
105675
107569
|
if (acl.length === 0) return true;
|
|
105676
107570
|
const username = readClientUsername(client);
|
|
105677
107571
|
if (!username) return true;
|
|
@@ -105698,7 +107592,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105698
107592
|
runtime.aedes.authorizeSubscribe = this.makeSubscribeAuthorizer();
|
|
105699
107593
|
runtime.credentialsSnapshot = nextSnapshot;
|
|
105700
107594
|
if (affectedUsers.length === 0) {
|
|
105701
|
-
this.ctx.logger.debug("mqtt embedded hot-reload — no password changes", { meta: { aclRules: this.
|
|
107595
|
+
this.ctx.logger.debug("mqtt embedded hot-reload — no password changes", { meta: { aclRules: this.activeEmbeddedAcl.length } });
|
|
105702
107596
|
return;
|
|
105703
107597
|
}
|
|
105704
107598
|
const clients = readClientMap(runtime.aedes);
|
|
@@ -105724,7 +107618,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105724
107618
|
}
|
|
105725
107619
|
buildCredentialsSnapshot() {
|
|
105726
107620
|
const m = /* @__PURE__ */ new Map();
|
|
105727
|
-
for (const c of this.
|
|
107621
|
+
for (const c of this.activeEmbeddedClients) m.set(c.username, c.passwordHash);
|
|
105728
107622
|
return m;
|
|
105729
107623
|
}
|
|
105730
107624
|
/**
|
|
@@ -105749,7 +107643,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105749
107643
|
}
|
|
105750
107644
|
collectExternalProbeInputs() {
|
|
105751
107645
|
const out = [];
|
|
105752
|
-
for (const b of this.
|
|
107646
|
+
for (const b of this.activeExternalBrokers) {
|
|
105753
107647
|
if (b.onboard !== void 0) continue;
|
|
105754
107648
|
let password;
|
|
105755
107649
|
try {
|
|
@@ -105786,7 +107680,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
105786
107680
|
const paths = retiredCiphertextPaths(this.config);
|
|
105787
107681
|
if (paths.length === 0) return;
|
|
105788
107682
|
const unreadable = [];
|
|
105789
|
-
const values = [...this.
|
|
107683
|
+
const values = [...this.activeExternalBrokers.map((b) => ({
|
|
105790
107684
|
id: `brokers[${b.id}].password`,
|
|
105791
107685
|
v: b.password
|
|
105792
107686
|
})), {
|