camstack 1.2.212 → 1.2.213
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/{chunk-MLWXP2SI.js → chunk-MJJWVAEM.js} +941 -168
- package/dist/cli.js +4 -4
- package/dist/{launcher-EACOVUIT.js → launcher-6TBFW332.js} +3770 -1105
- package/dist/{server-2GOCBDEY.js → server-EFXGQ7FD.js} +1085 -589
- package/package.json +1 -1
|
@@ -1404,7 +1404,7 @@ var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
|
1404
1404
|
return EventCategory2;
|
|
1405
1405
|
})({});
|
|
1406
1406
|
|
|
1407
|
-
// ../types/dist/sleep-
|
|
1407
|
+
// ../types/dist/sleep-CeyhrcOk.mjs
|
|
1408
1408
|
import { z as z2 } from "zod";
|
|
1409
1409
|
var AUDIO_CHUNK_FORMATS = [
|
|
1410
1410
|
"f32le",
|
|
@@ -7308,6 +7308,149 @@ function metaOf(schema) {
|
|
|
7308
7308
|
function paramMetaOf(field) {
|
|
7309
7309
|
return metaOf(field) ?? metaOf(unwrapSchema(field));
|
|
7310
7310
|
}
|
|
7311
|
+
var MACRO_LABELS = [
|
|
7312
|
+
{
|
|
7313
|
+
id: "person",
|
|
7314
|
+
name: "Person"
|
|
7315
|
+
},
|
|
7316
|
+
{
|
|
7317
|
+
id: "vehicle",
|
|
7318
|
+
name: "Vehicle"
|
|
7319
|
+
},
|
|
7320
|
+
{
|
|
7321
|
+
id: "animal",
|
|
7322
|
+
name: "Animal"
|
|
7323
|
+
},
|
|
7324
|
+
{
|
|
7325
|
+
id: "package",
|
|
7326
|
+
name: "Package"
|
|
7327
|
+
}
|
|
7328
|
+
];
|
|
7329
|
+
var DETECTION_MACRO_CLASSES = new Set(MACRO_LABELS.map((l) => l.id));
|
|
7330
|
+
var COCO_TO_MACRO = {
|
|
7331
|
+
mapping: {
|
|
7332
|
+
person: "person",
|
|
7333
|
+
bicycle: "vehicle",
|
|
7334
|
+
car: "vehicle",
|
|
7335
|
+
motorcycle: "vehicle",
|
|
7336
|
+
airplane: "vehicle",
|
|
7337
|
+
bus: "vehicle",
|
|
7338
|
+
train: "vehicle",
|
|
7339
|
+
truck: "vehicle",
|
|
7340
|
+
boat: "vehicle",
|
|
7341
|
+
bird: "animal",
|
|
7342
|
+
cat: "animal",
|
|
7343
|
+
dog: "animal",
|
|
7344
|
+
horse: "animal",
|
|
7345
|
+
sheep: "animal",
|
|
7346
|
+
cow: "animal",
|
|
7347
|
+
elephant: "animal",
|
|
7348
|
+
bear: "animal",
|
|
7349
|
+
zebra: "animal",
|
|
7350
|
+
giraffe: "animal",
|
|
7351
|
+
suitcase: "package",
|
|
7352
|
+
backpack: "package",
|
|
7353
|
+
handbag: "package"
|
|
7354
|
+
},
|
|
7355
|
+
preserveOriginal: false
|
|
7356
|
+
};
|
|
7357
|
+
var SubClassLabelSchema = z4.object({
|
|
7358
|
+
kind: z4.literal("sub-class"),
|
|
7359
|
+
className: z4.string().min(1)
|
|
7360
|
+
});
|
|
7361
|
+
var IdentityLabelSchema = z4.object({
|
|
7362
|
+
kind: z4.literal("identity"),
|
|
7363
|
+
/** The `face-gallery` identity id (stable across a rename). */
|
|
7364
|
+
identityId: z4.string().min(1),
|
|
7365
|
+
/** The identity's name — what a track-lifecycle event carries as `label`. */
|
|
7366
|
+
name: z4.string().min(1)
|
|
7367
|
+
});
|
|
7368
|
+
var PlateLabelSchema = z4.object({
|
|
7369
|
+
kind: z4.literal("plate"),
|
|
7370
|
+
text: z4.string().min(1)
|
|
7371
|
+
});
|
|
7372
|
+
function macroOf$1(className) {
|
|
7373
|
+
return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
|
|
7374
|
+
}
|
|
7375
|
+
function subClassBelongs(macro2, label) {
|
|
7376
|
+
return label === void 0 || macroOf$1(label.className) === macro2;
|
|
7377
|
+
}
|
|
7378
|
+
var subClassOf = (macro2) => SubClassLabelSchema.refine((l) => subClassBelongs(macro2, l), { message: `that class is not a kind of ${macro2} in the detection taxonomy` });
|
|
7379
|
+
var DetectionSubjectSchema = z4.discriminatedUnion("macro", [
|
|
7380
|
+
z4.object({
|
|
7381
|
+
macro: z4.literal("animal"),
|
|
7382
|
+
label: subClassOf("animal").optional()
|
|
7383
|
+
}),
|
|
7384
|
+
z4.object({
|
|
7385
|
+
macro: z4.literal("person"),
|
|
7386
|
+
label: IdentityLabelSchema.optional()
|
|
7387
|
+
}),
|
|
7388
|
+
z4.object({
|
|
7389
|
+
macro: z4.literal("vehicle"),
|
|
7390
|
+
label: z4.discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
|
|
7391
|
+
}),
|
|
7392
|
+
z4.object({
|
|
7393
|
+
macro: z4.literal("package"),
|
|
7394
|
+
label: subClassOf("package").optional()
|
|
7395
|
+
}),
|
|
7396
|
+
z4.object({
|
|
7397
|
+
macro: z4.literal("other"),
|
|
7398
|
+
className: z4.string().min(1)
|
|
7399
|
+
})
|
|
7400
|
+
]);
|
|
7401
|
+
var StationarySubjectSchema = DetectionSubjectSchema.refine((s) => s.macro === "other" || s.label === void 0 || s.label.kind === "sub-class", { message: "a parked object carries no face or plate \u2014 only a class can narrow it" });
|
|
7402
|
+
function subjectFromClassName(className) {
|
|
7403
|
+
const name = className.trim().toLowerCase();
|
|
7404
|
+
const macro2 = COCO_TO_MACRO.mapping[name];
|
|
7405
|
+
if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
|
|
7406
|
+
if (macro2 === void 0) return {
|
|
7407
|
+
macro: "other",
|
|
7408
|
+
className
|
|
7409
|
+
};
|
|
7410
|
+
const label = {
|
|
7411
|
+
kind: "sub-class",
|
|
7412
|
+
className: name
|
|
7413
|
+
};
|
|
7414
|
+
switch (macro2) {
|
|
7415
|
+
case "animal":
|
|
7416
|
+
return {
|
|
7417
|
+
macro: "animal",
|
|
7418
|
+
label
|
|
7419
|
+
};
|
|
7420
|
+
case "vehicle":
|
|
7421
|
+
return {
|
|
7422
|
+
macro: "vehicle",
|
|
7423
|
+
label
|
|
7424
|
+
};
|
|
7425
|
+
case "package":
|
|
7426
|
+
return {
|
|
7427
|
+
macro: "package",
|
|
7428
|
+
label
|
|
7429
|
+
};
|
|
7430
|
+
default:
|
|
7431
|
+
return {
|
|
7432
|
+
macro: "other",
|
|
7433
|
+
className
|
|
7434
|
+
};
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
function macroOnly(macro2) {
|
|
7438
|
+
switch (macro2) {
|
|
7439
|
+
case "animal":
|
|
7440
|
+
return { macro: "animal" };
|
|
7441
|
+
case "person":
|
|
7442
|
+
return { macro: "person" };
|
|
7443
|
+
case "vehicle":
|
|
7444
|
+
return { macro: "vehicle" };
|
|
7445
|
+
case "package":
|
|
7446
|
+
return { macro: "package" };
|
|
7447
|
+
default:
|
|
7448
|
+
return {
|
|
7449
|
+
macro: "other",
|
|
7450
|
+
className: macro2
|
|
7451
|
+
};
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7311
7454
|
var AutomationConditionOperatorSchema = z4.enum([
|
|
7312
7455
|
"eq",
|
|
7313
7456
|
"ne",
|
|
@@ -7318,12 +7461,69 @@ var AutomationConditionOperatorSchema = z4.enum([
|
|
|
7318
7461
|
"contains",
|
|
7319
7462
|
"in"
|
|
7320
7463
|
]);
|
|
7321
|
-
var
|
|
7322
|
-
kind: z4.literal("
|
|
7464
|
+
var DeviceBecomesLeafSchema = z4.object({
|
|
7465
|
+
kind: z4.literal("device-becomes"),
|
|
7466
|
+
deviceId: z4.number().int().nonnegative(),
|
|
7467
|
+
cap: z4.string().min(1),
|
|
7468
|
+
fieldPath: z4.string().min(1),
|
|
7469
|
+
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
7470
|
+
becomes: z4.union([
|
|
7471
|
+
z4.string(),
|
|
7472
|
+
z4.number(),
|
|
7473
|
+
z4.boolean()
|
|
7474
|
+
]).optional(),
|
|
7475
|
+
/** Only on a CHANGE of value, not on every re-report. */
|
|
7476
|
+
edge: z4.boolean().optional(),
|
|
7477
|
+
/** The new value must hold this long before it counts. */
|
|
7478
|
+
forMs: z4.number().int().min(0).max(864e5).optional(),
|
|
7479
|
+
/** Collapse a burst into one occurrence. */
|
|
7480
|
+
debounceMs: z4.number().int().min(0).max(6e5).optional()
|
|
7481
|
+
});
|
|
7482
|
+
var DeviceEventLeafSchema = z4.object({
|
|
7483
|
+
kind: z4.literal("device-event"),
|
|
7484
|
+
/** An `EventCategory` value. */
|
|
7485
|
+
category: z4.string().min(1),
|
|
7486
|
+
deviceId: z4.number().int().nonnegative().optional()
|
|
7487
|
+
});
|
|
7488
|
+
var ScheduleLeafSchema = z4.object({
|
|
7489
|
+
kind: z4.literal("schedule"),
|
|
7490
|
+
cron: z4.string().min(1).max(120)
|
|
7491
|
+
});
|
|
7492
|
+
var ManualLeafSchema = z4.object({ kind: z4.literal("manual") });
|
|
7493
|
+
var IntervalLeafSchema = z4.object({
|
|
7494
|
+
kind: z4.literal("interval"),
|
|
7495
|
+
minutes: z4.number().int().min(1).max(1440)
|
|
7496
|
+
});
|
|
7497
|
+
var DetectionLeafSchema = z4.object({
|
|
7498
|
+
kind: z4.literal("detection"),
|
|
7499
|
+
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
7500
|
+
subjects: z4.array(DetectionSubjectSchema).min(1),
|
|
7501
|
+
deviceIds: z4.array(z4.number().int().nonnegative()).min(1),
|
|
7502
|
+
/** The track must have visited at least one of these zones. */
|
|
7503
|
+
zones: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7504
|
+
/** Best-so-far confidence the track must reach. */
|
|
7505
|
+
minScore: z4.number().min(0).max(1).optional()
|
|
7506
|
+
});
|
|
7507
|
+
var StationaryLeafSchema = z4.object({
|
|
7508
|
+
kind: z4.literal("stationary"),
|
|
7509
|
+
deviceIds: z4.array(z4.number().int().nonnegative()).min(1),
|
|
7510
|
+
subjects: z4.array(StationarySubjectSchema).min(1).optional(),
|
|
7511
|
+
zones: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7512
|
+
/** `appeared`: it became parked. `departed`: the registry retired it. */
|
|
7513
|
+
phase: z4.enum(["appeared", "departed"])
|
|
7514
|
+
});
|
|
7515
|
+
var DeviceIsLeafSchema = z4.object({
|
|
7516
|
+
kind: z4.literal("device-is"),
|
|
7323
7517
|
deviceId: z4.number().int().nonnegative(),
|
|
7324
7518
|
cap: z4.string().min(1),
|
|
7325
7519
|
fieldPath: z4.string().min(1),
|
|
7326
7520
|
operator: AutomationConditionOperatorSchema,
|
|
7521
|
+
/**
|
|
7522
|
+
* The value. A number on a quantity field is stored in the field's base unit
|
|
7523
|
+
* (D944); a quantity value is the WRITE form, which the save boundary
|
|
7524
|
+
* replaces with the converted number and `unit`. A leaf that still carries
|
|
7525
|
+
* one is never evaluated (it fails).
|
|
7526
|
+
*/
|
|
7327
7527
|
value: z4.union([
|
|
7328
7528
|
z4.string(),
|
|
7329
7529
|
z4.number(),
|
|
@@ -7331,17 +7531,16 @@ var AutomationConditionLeafSchema = z4.object({
|
|
|
7331
7531
|
z4.array(z4.union([z4.string(), z4.number()])),
|
|
7332
7532
|
QuantityValueSchema
|
|
7333
7533
|
]),
|
|
7534
|
+
/** The unit the person picked for `value` (display only; `value` is in the base unit). */
|
|
7334
7535
|
unit: z4.string().min(1).optional()
|
|
7335
7536
|
});
|
|
7336
|
-
var
|
|
7537
|
+
var StationaryPresentLeafSchema = z4.object({
|
|
7337
7538
|
kind: z4.literal("stationary-present"),
|
|
7338
7539
|
deviceId: z4.number().int().nonnegative(),
|
|
7339
|
-
|
|
7340
|
-
classes: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7341
|
-
/** The parked object must be in at least one of these zones. */
|
|
7540
|
+
subjects: z4.array(StationarySubjectSchema).min(1).optional(),
|
|
7342
7541
|
zones: z4.array(z4.string().min(1)).min(1).optional()
|
|
7343
7542
|
});
|
|
7344
|
-
var
|
|
7543
|
+
var ExpressionLeafSchema = z4.object({
|
|
7345
7544
|
kind: z4.literal("expression"),
|
|
7346
7545
|
expr: z4.string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
7347
7546
|
bindings: z4.record(z4.string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
@@ -7353,45 +7552,150 @@ var AutomationConditionExpressionSchema = z4.object({
|
|
|
7353
7552
|
path: ["expr"]
|
|
7354
7553
|
});
|
|
7355
7554
|
});
|
|
7356
|
-
var
|
|
7555
|
+
var AutomationWhenSchema = z4.lazy(() => z4.discriminatedUnion("kind", [
|
|
7357
7556
|
z4.object({
|
|
7358
7557
|
kind: z4.literal("all"),
|
|
7359
|
-
children: z4.array(
|
|
7558
|
+
children: z4.array(AutomationWhenSchema)
|
|
7360
7559
|
}),
|
|
7361
7560
|
z4.object({
|
|
7362
7561
|
kind: z4.literal("any"),
|
|
7363
|
-
children: z4.array(
|
|
7562
|
+
children: z4.array(AutomationWhenSchema)
|
|
7364
7563
|
}),
|
|
7365
7564
|
z4.object({
|
|
7366
7565
|
kind: z4.literal("not"),
|
|
7367
|
-
child:
|
|
7566
|
+
child: AutomationWhenSchema
|
|
7368
7567
|
}),
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7568
|
+
DeviceBecomesLeafSchema,
|
|
7569
|
+
DeviceEventLeafSchema,
|
|
7570
|
+
ScheduleLeafSchema,
|
|
7571
|
+
ManualLeafSchema,
|
|
7572
|
+
IntervalLeafSchema,
|
|
7573
|
+
DetectionLeafSchema,
|
|
7574
|
+
StationaryLeafSchema,
|
|
7575
|
+
DeviceIsLeafSchema,
|
|
7576
|
+
StationaryPresentLeafSchema,
|
|
7577
|
+
ExpressionLeafSchema
|
|
7372
7578
|
]));
|
|
7373
|
-
var
|
|
7579
|
+
var EVENT_KINDS = /* @__PURE__ */ new Set([
|
|
7580
|
+
"device-becomes",
|
|
7581
|
+
"device-event",
|
|
7582
|
+
"schedule",
|
|
7583
|
+
"manual",
|
|
7584
|
+
"interval",
|
|
7585
|
+
"detection",
|
|
7586
|
+
"stationary"
|
|
7587
|
+
]);
|
|
7588
|
+
function isEventLeaf(node) {
|
|
7589
|
+
return EVENT_KINDS.has(node.kind);
|
|
7590
|
+
}
|
|
7591
|
+
function isLeaf(node) {
|
|
7592
|
+
return node.kind !== "all" && node.kind !== "any" && node.kind !== "not";
|
|
7593
|
+
}
|
|
7594
|
+
function whenDepth(node) {
|
|
7595
|
+
if (isLeaf(node)) return 1;
|
|
7596
|
+
if (node.kind === "not") return 1 + whenDepth(node.child);
|
|
7597
|
+
let deepest = 0;
|
|
7598
|
+
for (const child of node.children) deepest = Math.max(deepest, whenDepth(child));
|
|
7599
|
+
return 1 + deepest;
|
|
7600
|
+
}
|
|
7601
|
+
function countWhenLeaves(node) {
|
|
7602
|
+
if (isLeaf(node)) return 1;
|
|
7603
|
+
if (node.kind === "not") return countWhenLeaves(node.child);
|
|
7604
|
+
let total = 0;
|
|
7605
|
+
for (const child of node.children) total += countWhenLeaves(child);
|
|
7606
|
+
return total;
|
|
7607
|
+
}
|
|
7608
|
+
function forEachLeaf(node, visit, path2 = "when") {
|
|
7609
|
+
if (isLeaf(node)) {
|
|
7610
|
+
visit(node, path2);
|
|
7611
|
+
return;
|
|
7612
|
+
}
|
|
7613
|
+
if (node.kind === "not") {
|
|
7614
|
+
forEachLeaf(node.child, visit, `${path2}.0`);
|
|
7615
|
+
return;
|
|
7616
|
+
}
|
|
7617
|
+
node.children.forEach((child, i) => {
|
|
7618
|
+
forEachLeaf(child, visit, `${path2}.${i}`);
|
|
7619
|
+
});
|
|
7620
|
+
}
|
|
7621
|
+
function hasEventLeaves(node) {
|
|
7622
|
+
let found = false;
|
|
7623
|
+
forEachLeaf(node, (leaf) => {
|
|
7624
|
+
if (isEventLeaf(leaf)) found = true;
|
|
7625
|
+
});
|
|
7626
|
+
return found;
|
|
7627
|
+
}
|
|
7628
|
+
function hasEmptyContainer(node) {
|
|
7629
|
+
if (isLeaf(node)) return false;
|
|
7630
|
+
if (node.kind === "not") return hasEmptyContainer(node.child);
|
|
7631
|
+
if (node.children.length === 0) return true;
|
|
7632
|
+
return node.children.some(hasEmptyContainer);
|
|
7633
|
+
}
|
|
7634
|
+
var LEAF_WORDS = {
|
|
7635
|
+
"device-becomes": "a device value that becomes",
|
|
7636
|
+
"device-event": "a device event",
|
|
7637
|
+
schedule: "a schedule",
|
|
7638
|
+
manual: "a manual run",
|
|
7639
|
+
interval: "an interval",
|
|
7640
|
+
detection: "a detection",
|
|
7641
|
+
stationary: "a parked object appearing or leaving"
|
|
7642
|
+
};
|
|
7643
|
+
function say(kind) {
|
|
7644
|
+
return LEAF_WORDS[kind] ?? kind;
|
|
7645
|
+
}
|
|
7646
|
+
function eventNeed(node, path2, negated) {
|
|
7647
|
+
if (isLeaf(node)) {
|
|
7648
|
+
if (!isEventLeaf(node)) return { paths: [] };
|
|
7649
|
+
if (negated) return { error: `"not" is applied to ${say(node.kind)} (${path2}) \u2014 an event is an instant, so "it did not happen" is true at every other moment and can never start a run. Negate a state ("is") instead, or remove the "not"` };
|
|
7650
|
+
return { paths: [{
|
|
7651
|
+
path: path2,
|
|
7652
|
+
kind: node.kind
|
|
7653
|
+
}] };
|
|
7654
|
+
}
|
|
7655
|
+
if (node.kind === "not") return eventNeed(node.child, `${path2}.0`, !negated);
|
|
7656
|
+
const conjunctive = node.kind === "all" !== negated;
|
|
7657
|
+
const parts = [];
|
|
7658
|
+
for (let i = 0; i < node.children.length; i += 1) {
|
|
7659
|
+
const child = node.children[i];
|
|
7660
|
+
if (child === void 0) continue;
|
|
7661
|
+
const need = eventNeed(child, `${path2}.${i}`, negated);
|
|
7662
|
+
if ("error" in need) return need;
|
|
7663
|
+
parts.push(need);
|
|
7664
|
+
}
|
|
7665
|
+
if (conjunctive) return { paths: parts.flatMap((p) => p.paths) };
|
|
7666
|
+
let best;
|
|
7667
|
+
for (const part of parts) if (best === void 0 || part.paths.length < best.paths.length) best = part;
|
|
7668
|
+
return best ?? { paths: [] };
|
|
7669
|
+
}
|
|
7670
|
+
function validateWhenTree(when) {
|
|
7671
|
+
if (hasEmptyContainer(when)) return "a group is empty \u2014 group existing rows instead of leaving a container to fill";
|
|
7672
|
+
const depth = whenDepth(when);
|
|
7673
|
+
if (depth > 4) return `the rules nest ${depth} levels deep \u2014 the limit is 4. "not (any of \u2026)" is the same as "all of (not \u2026)", which is one level shallower`;
|
|
7674
|
+
const leaves = countWhenLeaves(when);
|
|
7675
|
+
if (leaves > 32) return `the rules use ${leaves} rows \u2014 the limit is 32`;
|
|
7676
|
+
const need = eventNeed(when, "when", false);
|
|
7677
|
+
if ("error" in need) return need.error;
|
|
7678
|
+
const [first, second] = need.paths;
|
|
7679
|
+
if (first !== void 0 && second !== void 0) return `${say(first.kind)} (${first.path}) and ${say(second.kind)} (${second.path}) would both have to happen at the same instant, and an event happens at one moment \u2014 this can never run. Use "any of" to start on either, or turn one of them into a state ("is") that must already hold`;
|
|
7680
|
+
return null;
|
|
7681
|
+
}
|
|
7682
|
+
var LegacyTriggerSchema = z4.discriminatedUnion("kind", [
|
|
7374
7683
|
z4.object({
|
|
7375
7684
|
kind: z4.literal("device-state"),
|
|
7376
7685
|
deviceId: z4.number().int().nonnegative(),
|
|
7377
7686
|
cap: z4.string().min(1),
|
|
7378
7687
|
fieldPath: z4.string().min(1),
|
|
7379
|
-
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
7380
7688
|
becomes: z4.union([
|
|
7381
7689
|
z4.string(),
|
|
7382
7690
|
z4.number(),
|
|
7383
7691
|
z4.boolean()
|
|
7384
7692
|
]).optional(),
|
|
7385
|
-
/** Only on a CHANGE of value, not on every re-report. */
|
|
7386
7693
|
edge: z4.boolean().optional(),
|
|
7387
|
-
/** The condition must hold this long before the run starts. */
|
|
7388
7694
|
forMs: z4.number().int().min(0).max(864e5).optional(),
|
|
7389
|
-
/** Collapse a burst into one run. */
|
|
7390
7695
|
debounceMs: z4.number().int().min(0).max(6e5).optional()
|
|
7391
7696
|
}),
|
|
7392
7697
|
z4.object({
|
|
7393
7698
|
kind: z4.literal("device-event"),
|
|
7394
|
-
/** An `EventCategory` value. */
|
|
7395
7699
|
category: z4.string().min(1),
|
|
7396
7700
|
deviceId: z4.number().int().nonnegative().optional()
|
|
7397
7701
|
}),
|
|
@@ -7402,25 +7706,16 @@ var AutomationTriggerSchema = z4.discriminatedUnion("kind", [
|
|
|
7402
7706
|
z4.object({ kind: z4.literal("manual") }),
|
|
7403
7707
|
z4.object({
|
|
7404
7708
|
kind: z4.literal("detection"),
|
|
7405
|
-
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
7406
|
-
* matches every raw class that maps to it; any other name matches
|
|
7407
|
-
* exactly. */
|
|
7408
7709
|
classes: z4.array(z4.string().min(1)).min(1),
|
|
7409
7710
|
deviceIds: z4.array(z4.number().int().nonnegative()).min(1),
|
|
7410
|
-
/** The track must have visited at least one of these zones. */
|
|
7411
7711
|
zones: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7412
|
-
/** Best-so-far confidence the track must reach. */
|
|
7413
7712
|
minScore: z4.number().min(0).max(1).optional()
|
|
7414
7713
|
}),
|
|
7415
7714
|
z4.object({
|
|
7416
7715
|
kind: z4.literal("stationary"),
|
|
7417
7716
|
deviceIds: z4.array(z4.number().int().nonnegative()).min(1),
|
|
7418
|
-
/** Class names; a macro class matches like the detection trigger. */
|
|
7419
7717
|
classes: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7420
|
-
/** The parked object must be in at least one of these zones. */
|
|
7421
7718
|
zones: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7422
|
-
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
7423
|
-
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
7424
7719
|
phase: z4.enum(["appeared", "departed"])
|
|
7425
7720
|
}),
|
|
7426
7721
|
z4.object({
|
|
@@ -7428,6 +7723,123 @@ var AutomationTriggerSchema = z4.discriminatedUnion("kind", [
|
|
|
7428
7723
|
minutes: z4.number().int().min(1).max(1440)
|
|
7429
7724
|
})
|
|
7430
7725
|
]);
|
|
7726
|
+
var LegacyConditionLeafSchema = z4.object({
|
|
7727
|
+
kind: z4.literal("condition"),
|
|
7728
|
+
deviceId: z4.number().int().nonnegative(),
|
|
7729
|
+
cap: z4.string().min(1),
|
|
7730
|
+
fieldPath: z4.string().min(1),
|
|
7731
|
+
operator: AutomationConditionOperatorSchema,
|
|
7732
|
+
value: z4.union([
|
|
7733
|
+
z4.string(),
|
|
7734
|
+
z4.number(),
|
|
7735
|
+
z4.boolean(),
|
|
7736
|
+
z4.array(z4.union([z4.string(), z4.number()])),
|
|
7737
|
+
QuantityValueSchema
|
|
7738
|
+
]),
|
|
7739
|
+
unit: z4.string().min(1).optional()
|
|
7740
|
+
});
|
|
7741
|
+
var LegacyStationaryPresentSchema = z4.object({
|
|
7742
|
+
kind: z4.literal("stationary-present"),
|
|
7743
|
+
deviceId: z4.number().int().nonnegative(),
|
|
7744
|
+
classes: z4.array(z4.string().min(1)).min(1).optional(),
|
|
7745
|
+
zones: z4.array(z4.string().min(1)).min(1).optional()
|
|
7746
|
+
});
|
|
7747
|
+
var LegacyExpressionSchema = z4.object({
|
|
7748
|
+
kind: z4.literal("expression"),
|
|
7749
|
+
expr: z4.string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
7750
|
+
bindings: z4.record(z4.string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
7751
|
+
}).superRefine((src, ctx) => {
|
|
7752
|
+
const err = validateExpressionSource(src);
|
|
7753
|
+
if (err !== null) ctx.addIssue({
|
|
7754
|
+
code: "custom",
|
|
7755
|
+
message: err,
|
|
7756
|
+
path: ["expr"]
|
|
7757
|
+
});
|
|
7758
|
+
});
|
|
7759
|
+
var LegacyConditionSchema = z4.lazy(() => z4.discriminatedUnion("kind", [
|
|
7760
|
+
z4.object({
|
|
7761
|
+
kind: z4.literal("all"),
|
|
7762
|
+
children: z4.array(LegacyConditionSchema)
|
|
7763
|
+
}),
|
|
7764
|
+
z4.object({
|
|
7765
|
+
kind: z4.literal("any"),
|
|
7766
|
+
children: z4.array(LegacyConditionSchema)
|
|
7767
|
+
}),
|
|
7768
|
+
z4.object({
|
|
7769
|
+
kind: z4.literal("not"),
|
|
7770
|
+
child: LegacyConditionSchema
|
|
7771
|
+
}),
|
|
7772
|
+
LegacyConditionLeafSchema,
|
|
7773
|
+
LegacyExpressionSchema,
|
|
7774
|
+
LegacyStationaryPresentSchema
|
|
7775
|
+
]));
|
|
7776
|
+
function convertTrigger(trigger) {
|
|
7777
|
+
switch (trigger.kind) {
|
|
7778
|
+
case "device-state":
|
|
7779
|
+
return {
|
|
7780
|
+
...trigger,
|
|
7781
|
+
kind: "device-becomes"
|
|
7782
|
+
};
|
|
7783
|
+
case "detection": {
|
|
7784
|
+
const { classes, ...rest } = trigger;
|
|
7785
|
+
return {
|
|
7786
|
+
...rest,
|
|
7787
|
+
subjects: classes.map(subjectFromClassName)
|
|
7788
|
+
};
|
|
7789
|
+
}
|
|
7790
|
+
case "stationary": {
|
|
7791
|
+
const { classes, ...rest } = trigger;
|
|
7792
|
+
return {
|
|
7793
|
+
...rest,
|
|
7794
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
7795
|
+
};
|
|
7796
|
+
}
|
|
7797
|
+
default:
|
|
7798
|
+
return trigger;
|
|
7799
|
+
}
|
|
7800
|
+
}
|
|
7801
|
+
function convertLegacyCondition(condition) {
|
|
7802
|
+
switch (condition.kind) {
|
|
7803
|
+
case "condition":
|
|
7804
|
+
return {
|
|
7805
|
+
...condition,
|
|
7806
|
+
kind: "device-is"
|
|
7807
|
+
};
|
|
7808
|
+
case "stationary-present": {
|
|
7809
|
+
const { classes, ...rest } = condition;
|
|
7810
|
+
return {
|
|
7811
|
+
...rest,
|
|
7812
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
7813
|
+
};
|
|
7814
|
+
}
|
|
7815
|
+
case "expression":
|
|
7816
|
+
return condition;
|
|
7817
|
+
case "not":
|
|
7818
|
+
return {
|
|
7819
|
+
kind: "not",
|
|
7820
|
+
child: convertLegacyCondition(condition.child)
|
|
7821
|
+
};
|
|
7822
|
+
case "all":
|
|
7823
|
+
case "any":
|
|
7824
|
+
return {
|
|
7825
|
+
kind: condition.kind,
|
|
7826
|
+
children: condition.children.map(convertLegacyCondition)
|
|
7827
|
+
};
|
|
7828
|
+
}
|
|
7829
|
+
}
|
|
7830
|
+
function convertLegacyWhen(half) {
|
|
7831
|
+
const leaves = half.triggers.map(convertTrigger);
|
|
7832
|
+
const first = leaves[0];
|
|
7833
|
+
const triggers = leaves.length === 1 && first !== void 0 ? first : {
|
|
7834
|
+
kind: "any",
|
|
7835
|
+
children: leaves
|
|
7836
|
+
};
|
|
7837
|
+
if (half.conditions === void 0) return triggers;
|
|
7838
|
+
return {
|
|
7839
|
+
kind: "all",
|
|
7840
|
+
children: [triggers, convertLegacyCondition(half.conditions)]
|
|
7841
|
+
};
|
|
7842
|
+
}
|
|
7431
7843
|
var AutomationWaitStepSchema = z4.object({
|
|
7432
7844
|
kind: z4.literal("wait"),
|
|
7433
7845
|
seconds: z4.number().min(0).max(300)
|
|
@@ -7495,54 +7907,34 @@ var AutomationRunModeSchema = z4.discriminatedUnion("mode", [
|
|
|
7495
7907
|
minutes: z4.number().int().min(1).max(1440)
|
|
7496
7908
|
})
|
|
7497
7909
|
]);
|
|
7498
|
-
var
|
|
7499
|
-
|
|
7500
|
-
conditions: AutomationConditionSchema.optional(),
|
|
7910
|
+
var CurrentAutomationRecipeSchema = z4.object({
|
|
7911
|
+
when: AutomationWhenSchema,
|
|
7501
7912
|
actions: z4.array(AutomationActionSchema),
|
|
7502
7913
|
runMode: AutomationRunModeSchema.optional(),
|
|
7503
7914
|
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
7504
7915
|
keepTraces: z4.number().int().min(1).max(100).optional()
|
|
7505
7916
|
});
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
if (isLeaf(cond)) return 1;
|
|
7523
|
-
if (cond.kind === "not") return countConditionLeaves(cond.child);
|
|
7524
|
-
let total = 0;
|
|
7525
|
-
for (const child of cond.children) total += countConditionLeaves(child);
|
|
7526
|
-
return total;
|
|
7527
|
-
}
|
|
7528
|
-
function findEmptyContainer(cond) {
|
|
7529
|
-
if (isLeaf(cond)) return false;
|
|
7530
|
-
if (cond.kind === "not") return findEmptyContainer(cond.child);
|
|
7531
|
-
if (cond.children.length === 0) return true;
|
|
7532
|
-
return cond.children.some(findEmptyContainer);
|
|
7917
|
+
var LegacyAutomationRecipeSchema = z4.object({
|
|
7918
|
+
triggers: z4.array(LegacyTriggerSchema),
|
|
7919
|
+
conditions: LegacyConditionSchema.optional(),
|
|
7920
|
+
actions: z4.array(AutomationActionSchema),
|
|
7921
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
7922
|
+
keepTraces: z4.number().int().min(1).max(100).optional()
|
|
7923
|
+
});
|
|
7924
|
+
function convertLegacyRecipe(legacy) {
|
|
7925
|
+
const { triggers, conditions, ...rest } = legacy;
|
|
7926
|
+
return {
|
|
7927
|
+
when: convertLegacyWhen({
|
|
7928
|
+
triggers,
|
|
7929
|
+
conditions
|
|
7930
|
+
}),
|
|
7931
|
+
...rest
|
|
7932
|
+
};
|
|
7533
7933
|
}
|
|
7934
|
+
var AutomationRecipeSchema = z4.union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
7534
7935
|
function validateRecipeBounds(recipe) {
|
|
7535
|
-
if (recipe.triggers.length === 0) return "an automation needs at least one trigger \u2014 without one nothing can start a run";
|
|
7536
7936
|
if (recipe.actions.length === 0) return "an automation needs at least one action \u2014 a rule that does nothing is not a rule";
|
|
7537
|
-
|
|
7538
|
-
if (cond !== void 0) {
|
|
7539
|
-
if (findEmptyContainer(cond)) return "a condition group is empty \u2014 group existing rows instead of leaving a container to fill";
|
|
7540
|
-
const depth = conditionDepth(cond);
|
|
7541
|
-
if (depth > 3) return `conditions nest ${depth} levels deep \u2014 the limit is 3. "not (any of \u2026)" is the same as "all of (not \u2026)", which is one level shallower`;
|
|
7542
|
-
const leaves = countConditionLeaves(cond);
|
|
7543
|
-
if (leaves > 32) return `conditions use ${leaves} leaves \u2014 the limit is 32`;
|
|
7544
|
-
}
|
|
7545
|
-
return null;
|
|
7937
|
+
return validateWhenTree(recipe.when);
|
|
7546
7938
|
}
|
|
7547
7939
|
var RECONCILE_MS = 15e3;
|
|
7548
7940
|
function stepMetaOf(action) {
|
|
@@ -7565,10 +7957,11 @@ function stepMetaOf(action) {
|
|
|
7565
7957
|
function j(value) {
|
|
7566
7958
|
return JSON.stringify(value);
|
|
7567
7959
|
}
|
|
7568
|
-
function
|
|
7960
|
+
function emitLeaf(trigger, path2) {
|
|
7961
|
+
const at = j(path2);
|
|
7569
7962
|
switch (trigger.kind) {
|
|
7570
7963
|
case "schedule":
|
|
7571
|
-
return ` //
|
|
7964
|
+
return ` // event leaf ${path2}: schedule \u2014 armed to the next minute boundary and
|
|
7572
7965
|
// RE-ARMED from the wall clock every tick, so it cannot drift the way a
|
|
7573
7966
|
// setInterval(24h) does. No cron library: a block cannot resolve one.
|
|
7574
7967
|
{
|
|
@@ -7576,7 +7969,7 @@ function emitTrigger(trigger, index) {
|
|
|
7576
7969
|
const arm = (): void => {
|
|
7577
7970
|
timer = setTimeout(() => {
|
|
7578
7971
|
if (cronMatches(${j(trigger.cron)}, new Date())) {
|
|
7579
|
-
void run({ kind: 'schedule', at: Date.now() })
|
|
7972
|
+
void run({ kind: 'schedule', leaf: ${at}, at: Date.now() })
|
|
7580
7973
|
}
|
|
7581
7974
|
arm()
|
|
7582
7975
|
}, nextMinuteBoundary())
|
|
@@ -7585,13 +7978,14 @@ function emitTrigger(trigger, index) {
|
|
|
7585
7978
|
disposers.push(() => { if (timer !== undefined) clearTimeout(timer) })
|
|
7586
7979
|
}`;
|
|
7587
7980
|
case "device-event":
|
|
7588
|
-
return ` //
|
|
7981
|
+
return ` // event leaf ${path2}: device event
|
|
7589
7982
|
disposers.push(
|
|
7590
7983
|
ctx.eventBus.subscribe(
|
|
7591
7984
|
{ category: ${j(trigger.category)}${trigger.deviceId === void 0 ? "" : `, deviceId: ${j(trigger.deviceId)}`} },
|
|
7592
7985
|
(event) => {
|
|
7593
7986
|
void run({
|
|
7594
7987
|
kind: 'device-event',
|
|
7988
|
+
leaf: ${at},
|
|
7595
7989
|
at: Date.now(),
|
|
7596
7990
|
deviceId: event.deviceId,
|
|
7597
7991
|
category: ${j(trigger.category)},
|
|
@@ -7599,9 +7993,9 @@ function emitTrigger(trigger, index) {
|
|
|
7599
7993
|
},
|
|
7600
7994
|
),
|
|
7601
7995
|
)`;
|
|
7602
|
-
case "device-
|
|
7996
|
+
case "device-becomes": {
|
|
7603
7997
|
const becomes = trigger.becomes === void 0 ? "undefined" : j(trigger.becomes);
|
|
7604
|
-
return ` //
|
|
7998
|
+
return ` // event leaf ${path2}: device state \u2014 the mirror is the authority, the
|
|
7605
7999
|
// event only WAKES the evaluation, so a dropped event delays and never loses.
|
|
7606
8000
|
{
|
|
7607
8001
|
let previous: unknown = undefined
|
|
@@ -7622,7 +8016,8 @@ function emitTrigger(trigger, index) {
|
|
|
7622
8016
|
}
|
|
7623
8017
|
heldSince = undefined
|
|
7624
8018
|
void run({
|
|
7625
|
-
kind: 'device-
|
|
8019
|
+
kind: 'device-becomes',
|
|
8020
|
+
leaf: ${at},
|
|
7626
8021
|
at: Date.now(),
|
|
7627
8022
|
deviceId: ${j(trigger.deviceId)},
|
|
7628
8023
|
cap: ${j(trigger.cap)},
|
|
@@ -7642,17 +8037,17 @@ function emitTrigger(trigger, index) {
|
|
|
7642
8037
|
}`;
|
|
7643
8038
|
}
|
|
7644
8039
|
case "manual":
|
|
7645
|
-
return ` //
|
|
8040
|
+
return ` // event leaf ${path2}: manual \u2014 fired by automation-control.trigger
|
|
7646
8041
|
// ("Run actions"), which runs the THEN half with an EMPTY vars bag. A manual
|
|
7647
8042
|
// run bypasses only the cooldown wait, never the single/queued limits.`;
|
|
7648
8043
|
case "interval":
|
|
7649
|
-
return ` //
|
|
8044
|
+
return ` // event leaf ${path2}: interval \u2014 every ${trigger.minutes} min, first run jittered so
|
|
7650
8045
|
// blocks restarted together do not all fire on the same second.
|
|
7651
8046
|
{
|
|
7652
8047
|
let interval: ReturnType<typeof setInterval> | undefined
|
|
7653
8048
|
const first = setTimeout(() => {
|
|
7654
|
-
void run({ kind: 'interval', at: Date.now() })
|
|
7655
|
-
interval = setInterval(() => { void run({ kind: 'interval', at: Date.now() }) }, ${j(trigger.minutes * 6e4)})
|
|
8049
|
+
void run({ kind: 'interval', leaf: ${at}, at: Date.now() })
|
|
8050
|
+
interval = setInterval(() => { void run({ kind: 'interval', leaf: ${at}, at: Date.now() }) }, ${j(trigger.minutes * 6e4)})
|
|
7656
8051
|
}, Math.floor(Math.random() * Math.min(${j(trigger.minutes * 6e4)}, 30_000)) + 1)
|
|
7657
8052
|
disposers.push(() => {
|
|
7658
8053
|
clearTimeout(first)
|
|
@@ -7660,7 +8055,7 @@ function emitTrigger(trigger, index) {
|
|
|
7660
8055
|
})
|
|
7661
8056
|
}`;
|
|
7662
8057
|
case "detection":
|
|
7663
|
-
return ` //
|
|
8058
|
+
return ` // event leaf ${path2}: detection \u2014 ONE fire per (device, track); a rejected event is
|
|
7664
8059
|
// traced with its reason and never fires.
|
|
7665
8060
|
{
|
|
7666
8061
|
const filter = new DetectionTriggerFilter(${j(trigger)})
|
|
@@ -7677,7 +8072,7 @@ function emitTrigger(trigger, index) {
|
|
|
7677
8072
|
})
|
|
7678
8073
|
}
|
|
7679
8074
|
void run(
|
|
7680
|
-
{ kind: 'detection', at: Date.now(), ...decision.vars },
|
|
8075
|
+
{ kind: 'detection', leaf: ${at}, at: Date.now(), ...decision.vars },
|
|
7681
8076
|
decision.reason,
|
|
7682
8077
|
decision.kind === 'fire',
|
|
7683
8078
|
)
|
|
@@ -7686,9 +8081,9 @@ function emitTrigger(trigger, index) {
|
|
|
7686
8081
|
)
|
|
7687
8082
|
}`;
|
|
7688
8083
|
case "stationary":
|
|
7689
|
-
return ` //
|
|
8084
|
+
return ` // event leaf ${path2}: stationary \u2014 a parked object appeared / departed. Stateless:
|
|
7690
8085
|
// the registry emits one event per transition. A dropped event only misses a fire;
|
|
7691
|
-
// the stationary-present
|
|
8086
|
+
// the stationary-present STATE leaf reads the registry, not this event.
|
|
7692
8087
|
{
|
|
7693
8088
|
const trigger = ${j(trigger)} as const
|
|
7694
8089
|
disposers.push(
|
|
@@ -7704,7 +8099,7 @@ function emitTrigger(trigger, index) {
|
|
|
7704
8099
|
})
|
|
7705
8100
|
}
|
|
7706
8101
|
void run(
|
|
7707
|
-
{ kind: 'stationary', at: Date.now(), ...decision.vars },
|
|
8102
|
+
{ kind: 'stationary', leaf: ${at}, at: Date.now(), ...decision.vars },
|
|
7708
8103
|
decision.reason,
|
|
7709
8104
|
decision.kind === 'fire',
|
|
7710
8105
|
)
|
|
@@ -7727,11 +8122,11 @@ ${body}
|
|
|
7727
8122
|
case "cap":
|
|
7728
8123
|
if (Array.isArray(action.deviceId)) return step(` for (const deviceId of ${j(action.deviceId)}) {
|
|
7729
8124
|
scope.setDeviceId(deviceId)
|
|
7730
|
-
await actuate(deviceId, ${j(action.cap)}, ${j(action.method)}, ${j(action.args ?? {})}, vars)
|
|
8125
|
+
await actuate(scope, deviceId, ${j(action.cap)}, ${j(action.method)}, ${j(action.args ?? {})}, vars)
|
|
7731
8126
|
}`);
|
|
7732
8127
|
return step(` const deviceId = ${typeof action.deviceId === "number" ? j(action.deviceId) : `resolveTarget(vars, ${j(action.deviceId.$var)})`}
|
|
7733
8128
|
scope.setDeviceId(deviceId)
|
|
7734
|
-
await actuate(deviceId, ${j(action.cap)}, ${j(action.method)}, ${j(action.args ?? {})}, vars)`);
|
|
8129
|
+
await actuate(scope, deviceId, ${j(action.cap)}, ${j(action.method)}, ${j(action.args ?? {})}, vars)`);
|
|
7735
8130
|
case "action":
|
|
7736
8131
|
return step(` // an Action run, awaited: the step takes THAT run's outcome
|
|
7737
8132
|
const result = await ctx.api.actions.run.mutate({
|
|
@@ -7756,6 +8151,13 @@ function generateAutomationBlock(input) {
|
|
|
7756
8151
|
const { name, recipe } = input;
|
|
7757
8152
|
const runMode = recipe.runMode ?? { mode: "single" };
|
|
7758
8153
|
const stepMeta = recipe.actions.map(stepMetaOf);
|
|
8154
|
+
const eventLeaves = [];
|
|
8155
|
+
forEachLeaf(recipe.when, (leaf, path2) => {
|
|
8156
|
+
if (isEventLeaf(leaf)) eventLeaves.push({
|
|
8157
|
+
leaf,
|
|
8158
|
+
path: path2
|
|
8159
|
+
});
|
|
8160
|
+
});
|
|
7759
8161
|
return `import type {
|
|
7760
8162
|
AddonContext,
|
|
7761
8163
|
AutomationConditionTraceNode,
|
|
@@ -7763,6 +8165,7 @@ function generateAutomationBlock(input) {
|
|
|
7763
8165
|
AutomationRunVerdict,
|
|
7764
8166
|
AutomationStepTrace,
|
|
7765
8167
|
RunnableStep,
|
|
8168
|
+
StepScope,
|
|
7766
8169
|
} from '@camstack/types'
|
|
7767
8170
|
import {
|
|
7768
8171
|
ConditionMirror,
|
|
@@ -7770,9 +8173,9 @@ import {
|
|
|
7770
8173
|
EventCategory,
|
|
7771
8174
|
RunGate,
|
|
7772
8175
|
buildTriggerTrace,
|
|
7773
|
-
|
|
8176
|
+
collectWhenMirrorSources,
|
|
7774
8177
|
coreBlockIdFromAddonId,
|
|
7775
|
-
|
|
8178
|
+
evaluateWhenTree,
|
|
7776
8179
|
evaluateStationaryTrigger,
|
|
7777
8180
|
isSkippedTrace,
|
|
7778
8181
|
mergeSkipTraces,
|
|
@@ -7800,7 +8203,11 @@ const RUN_MODE = ${j(runMode)} as const
|
|
|
7800
8203
|
/** Run traces kept (a ring, held in the hub's runs ledger). */
|
|
7801
8204
|
const KEEP_TRACES = ${recipe.keepTraces ?? 20}
|
|
7802
8205
|
|
|
7803
|
-
|
|
8206
|
+
/** The ONE tree: event leaves (instants) and state leaves (durations). */
|
|
8207
|
+
const WHEN = AUTOMATION_RECIPE.when
|
|
8208
|
+
|
|
8209
|
+
/** No event leaf: the automation fires when the tree goes false \u2192 true, never because it was already true. */
|
|
8210
|
+
const STATE_ONLY = ${!hasEventLeaves(recipe.when)}
|
|
7804
8211
|
|
|
7805
8212
|
/** Skips that repeat are folded into one trace; it goes to the hub after this. */
|
|
7806
8213
|
const SKIP_FLUSH_MS = 5_000
|
|
@@ -7859,7 +8266,8 @@ function matchField(field: string, value: number): boolean {
|
|
|
7859
8266
|
export default function block(ctx: AddonContext) {
|
|
7860
8267
|
const disposers: Array<() => void> = []
|
|
7861
8268
|
const gate = new RunGate(RUN_MODE)
|
|
7862
|
-
const
|
|
8269
|
+
const MIRROR_SOURCES = collectWhenMirrorSources(WHEN)
|
|
8270
|
+
const mirror = new ConditionMirror(MIRROR_SOURCES)
|
|
7863
8271
|
const BLOCK_ID = coreBlockIdFromAddonId(ctx.id)
|
|
7864
8272
|
/** The cooldown stamp: memory is the authority at fire time (D49); the hub ledger is read once at start. */
|
|
7865
8273
|
let lastRunStartedAt: number | null = null
|
|
@@ -8025,6 +8433,7 @@ export default function block(ctx: AddonContext) {
|
|
|
8025
8433
|
}
|
|
8026
8434
|
|
|
8027
8435
|
async function actuate(
|
|
8436
|
+
scope: StepScope,
|
|
8028
8437
|
deviceId: number,
|
|
8029
8438
|
cap: string,
|
|
8030
8439
|
method: string,
|
|
@@ -8034,6 +8443,7 @@ export default function block(ctx: AddonContext) {
|
|
|
8034
8443
|
const substituted = substitute(args, vars)
|
|
8035
8444
|
const resolved: Record<string, unknown> =
|
|
8036
8445
|
substituted !== null && typeof substituted === 'object' ? { ...substituted } : {}
|
|
8446
|
+
scope.setArgs(resolved)
|
|
8037
8447
|
// Forked-addon ctx.api namespaces are tRPC Proxies over a noop function
|
|
8038
8448
|
// (typeof === 'function'), not plain objects \u2014 reject only null/undefined.
|
|
8039
8449
|
const client: unknown = Reflect.get(ctx.api, capToRouterKey(cap))
|
|
@@ -8122,10 +8532,13 @@ ${recipe.actions.map((a, i) => emitAction(a, i)).join("\n")}
|
|
|
8122
8532
|
let nodes: AutomationConditionTraceNode[] = []
|
|
8123
8533
|
let steps: AutomationStepTrace[] = []
|
|
8124
8534
|
try {
|
|
8125
|
-
const
|
|
8535
|
+
const firedLeaf = trigger['leaf']
|
|
8536
|
+
const evaluation = evaluateWhenTree(WHEN, mirror, Date.now(), {
|
|
8537
|
+
...(typeof firedLeaf === 'string' ? { firedPath: firedLeaf } : {}),
|
|
8538
|
+
})
|
|
8126
8539
|
nodes = [...evaluation.nodes]
|
|
8127
8540
|
for (const node of nodes) {
|
|
8128
|
-
if (node.kind === '
|
|
8541
|
+
if (node.kind === 'device-is' && node.observed === null && node.deviceId !== undefined) {
|
|
8129
8542
|
ctx.logger.warn('condition leaf value unknown \u2014 treated as false', {
|
|
8130
8543
|
tags: { deviceId: node.deviceId },
|
|
8131
8544
|
path: node.path,
|
|
@@ -8136,7 +8549,7 @@ ${recipe.actions.map((a, i) => emitAction(a, i)).join("\n")}
|
|
|
8136
8549
|
}
|
|
8137
8550
|
if (!evaluation.pass) {
|
|
8138
8551
|
outcome = 'skipped-conditions'
|
|
8139
|
-
ctx.logger.debug('automation did not run \u2014
|
|
8552
|
+
ctx.logger.debug('automation did not run \u2014 the rule tree was false at that instant', { trigger, ...deviceTags(trigger) })
|
|
8140
8553
|
return
|
|
8141
8554
|
}
|
|
8142
8555
|
const letThrough = stampUnknown && RUN_MODE.mode === 'cooldown'
|
|
@@ -8235,9 +8648,30 @@ ${recipe.actions.map((a, i) => emitAction(a, i)).join("\n")}
|
|
|
8235
8648
|
const ready: Promise<void> = (async () => {
|
|
8236
8649
|
await loadState()
|
|
8237
8650
|
await refreshMirror()
|
|
8651
|
+
stateWatching = true
|
|
8652
|
+
evaluateStateTree()
|
|
8238
8653
|
})()
|
|
8239
8654
|
|
|
8240
|
-
|
|
8655
|
+
/**
|
|
8656
|
+
* Rule 2 \u2014 a tree of states only. It fires when the tree goes false \u2192 true.
|
|
8657
|
+
* The FIRST evaluation only seeds \`treeWasTrue\` (a tree that is already true
|
|
8658
|
+
* when the hub or runner starts must not fire), and an evaluation that read an
|
|
8659
|
+
* unknown value neither seeds nor moves it: unknown is not false.
|
|
8660
|
+
*/
|
|
8661
|
+
let treeWasTrue: boolean | undefined
|
|
8662
|
+
let stateWatching = false
|
|
8663
|
+
function evaluateStateTree(): void {
|
|
8664
|
+
if (!STATE_ONLY || !stateWatching || stopped) return
|
|
8665
|
+
const evaluation = evaluateWhenTree(WHEN, mirror, Date.now())
|
|
8666
|
+
if (evaluation.unknown) return
|
|
8667
|
+
const was = treeWasTrue
|
|
8668
|
+
treeWasTrue = evaluation.pass
|
|
8669
|
+
if (was === false && evaluation.pass) {
|
|
8670
|
+
void run({ kind: 'state', leaf: 'when', at: Date.now() }, 'the rules became true', true)
|
|
8671
|
+
}
|
|
8672
|
+
}
|
|
8673
|
+
|
|
8674
|
+
if (MIRROR_SOURCES.length > 0) {
|
|
8241
8675
|
disposers.push(
|
|
8242
8676
|
ctx.eventBus.subscribe({ category: EventCategory.DeviceStateChanged }, (event) => {
|
|
8243
8677
|
const data = event.data
|
|
@@ -8245,14 +8679,17 @@ ${recipe.actions.map((a, i) => emitAction(a, i)).join("\n")}
|
|
|
8245
8679
|
const capName = data['capName']
|
|
8246
8680
|
if (typeof deviceId === 'number' && typeof capName === 'string') {
|
|
8247
8681
|
mirror.apply(deviceId, capName, data['slice'])
|
|
8682
|
+
evaluateStateTree()
|
|
8248
8683
|
}
|
|
8249
8684
|
}),
|
|
8250
8685
|
)
|
|
8251
|
-
const mirrorReconcile = setInterval(() => {
|
|
8686
|
+
const mirrorReconcile = setInterval(() => {
|
|
8687
|
+
void refreshMirror().then(evaluateStateTree)
|
|
8688
|
+
}, RECONCILE_MS)
|
|
8252
8689
|
disposers.push(() => clearInterval(mirrorReconcile))
|
|
8253
8690
|
}
|
|
8254
8691
|
|
|
8255
|
-
${
|
|
8692
|
+
${eventLeaves.map(({ leaf, path: path2 }) => emitLeaf(leaf, path2)).join("\n\n")}
|
|
8256
8693
|
|
|
8257
8694
|
return () => {
|
|
8258
8695
|
stopped = true
|
|
@@ -8267,52 +8704,6 @@ ${recipe.triggers.map((t2, i) => emitTrigger(t2, i)).join("\n\n")}
|
|
|
8267
8704
|
}
|
|
8268
8705
|
`;
|
|
8269
8706
|
}
|
|
8270
|
-
var MACRO_LABELS = [
|
|
8271
|
-
{
|
|
8272
|
-
id: "person",
|
|
8273
|
-
name: "Person"
|
|
8274
|
-
},
|
|
8275
|
-
{
|
|
8276
|
-
id: "vehicle",
|
|
8277
|
-
name: "Vehicle"
|
|
8278
|
-
},
|
|
8279
|
-
{
|
|
8280
|
-
id: "animal",
|
|
8281
|
-
name: "Animal"
|
|
8282
|
-
},
|
|
8283
|
-
{
|
|
8284
|
-
id: "package",
|
|
8285
|
-
name: "Package"
|
|
8286
|
-
}
|
|
8287
|
-
];
|
|
8288
|
-
var DETECTION_MACRO_CLASSES = new Set(MACRO_LABELS.map((l) => l.id));
|
|
8289
|
-
var COCO_TO_MACRO = {
|
|
8290
|
-
mapping: {
|
|
8291
|
-
person: "person",
|
|
8292
|
-
bicycle: "vehicle",
|
|
8293
|
-
car: "vehicle",
|
|
8294
|
-
motorcycle: "vehicle",
|
|
8295
|
-
airplane: "vehicle",
|
|
8296
|
-
bus: "vehicle",
|
|
8297
|
-
train: "vehicle",
|
|
8298
|
-
truck: "vehicle",
|
|
8299
|
-
boat: "vehicle",
|
|
8300
|
-
bird: "animal",
|
|
8301
|
-
cat: "animal",
|
|
8302
|
-
dog: "animal",
|
|
8303
|
-
horse: "animal",
|
|
8304
|
-
sheep: "animal",
|
|
8305
|
-
cow: "animal",
|
|
8306
|
-
elephant: "animal",
|
|
8307
|
-
bear: "animal",
|
|
8308
|
-
zebra: "animal",
|
|
8309
|
-
giraffe: "animal",
|
|
8310
|
-
suitcase: "package",
|
|
8311
|
-
backpack: "package",
|
|
8312
|
-
handbag: "package"
|
|
8313
|
-
},
|
|
8314
|
-
preserveOriginal: false
|
|
8315
|
-
};
|
|
8316
8707
|
var MACRO_NAMES = new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
8317
8708
|
var TraceValueSchema = z4.union([
|
|
8318
8709
|
z4.string(),
|
|
@@ -8337,6 +8728,8 @@ var TraceNodeOutcomeSchema = z4.enum([
|
|
|
8337
8728
|
]);
|
|
8338
8729
|
var AutomationTriggerTraceSchema = z4.object({
|
|
8339
8730
|
kind: z4.string().min(1),
|
|
8731
|
+
/** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
|
|
8732
|
+
path: z4.string().min(1).optional(),
|
|
8340
8733
|
outcome: z4.enum(["pass", "fail"]),
|
|
8341
8734
|
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
8342
8735
|
reason: z4.string(),
|
|
@@ -8344,15 +8737,23 @@ var AutomationTriggerTraceSchema = z4.object({
|
|
|
8344
8737
|
vars: z4.record(z4.string(), TraceValueSchema)
|
|
8345
8738
|
});
|
|
8346
8739
|
var AutomationConditionTraceNodeSchema = z4.object({
|
|
8347
|
-
/** `
|
|
8740
|
+
/** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
|
|
8348
8741
|
path: z4.string().min(1),
|
|
8349
8742
|
kind: z4.enum([
|
|
8350
8743
|
"all",
|
|
8351
8744
|
"any",
|
|
8352
8745
|
"not",
|
|
8353
8746
|
"condition",
|
|
8747
|
+
"device-is",
|
|
8354
8748
|
"expression",
|
|
8355
|
-
"stationary-present"
|
|
8749
|
+
"stationary-present",
|
|
8750
|
+
"device-becomes",
|
|
8751
|
+
"device-event",
|
|
8752
|
+
"schedule",
|
|
8753
|
+
"interval",
|
|
8754
|
+
"manual",
|
|
8755
|
+
"detection",
|
|
8756
|
+
"stationary"
|
|
8356
8757
|
]),
|
|
8357
8758
|
outcome: TraceNodeOutcomeSchema,
|
|
8358
8759
|
reason: z4.string(),
|
|
@@ -8385,6 +8786,12 @@ var AutomationStepTraceSchema = z4.object({
|
|
|
8385
8786
|
deviceId: z4.number().int().optional(),
|
|
8386
8787
|
startedAt: z4.number().optional(),
|
|
8387
8788
|
endedAt: z4.number().optional(),
|
|
8789
|
+
/**
|
|
8790
|
+
* The parameters the step SENT, after `{{vars.x}}` substitution — what the
|
|
8791
|
+
* device was actually asked. Bounded like trigger `vars`; a secret parameter
|
|
8792
|
+
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
8793
|
+
*/
|
|
8794
|
+
args: z4.record(z4.string(), TraceValueSchema).optional(),
|
|
8388
8795
|
outcome: z4.enum([
|
|
8389
8796
|
"ok",
|
|
8390
8797
|
"error",
|
|
@@ -8652,9 +9059,9 @@ function normalizeLeaf(leaf, resolver, path2) {
|
|
|
8652
9059
|
})]
|
|
8653
9060
|
};
|
|
8654
9061
|
}
|
|
8655
|
-
function normalizeConditionUnits(condition, resolver, path2 = "
|
|
9062
|
+
function normalizeConditionUnits(condition, resolver, path2 = "when") {
|
|
8656
9063
|
switch (condition.kind) {
|
|
8657
|
-
case "
|
|
9064
|
+
case "device-is": {
|
|
8658
9065
|
const { leaf, warnings } = normalizeLeaf(condition, resolver, path2);
|
|
8659
9066
|
return {
|
|
8660
9067
|
value: leaf,
|
|
@@ -8745,20 +9152,20 @@ function normalizeActionUnits(actions, resolver, path2 = "steps") {
|
|
|
8745
9152
|
};
|
|
8746
9153
|
}
|
|
8747
9154
|
function normalizeRecipeUnits(recipe, resolver) {
|
|
8748
|
-
const
|
|
9155
|
+
const when = normalizeConditionUnits(recipe.when, resolver, "when");
|
|
8749
9156
|
const actions = normalizeActionUnits(recipe.actions, resolver, "actions");
|
|
8750
9157
|
return {
|
|
8751
9158
|
value: {
|
|
8752
9159
|
...recipe,
|
|
8753
|
-
|
|
9160
|
+
when: when.value,
|
|
8754
9161
|
actions: actions.value
|
|
8755
9162
|
},
|
|
8756
|
-
warnings: [...
|
|
9163
|
+
warnings: [...when.warnings, ...actions.warnings]
|
|
8757
9164
|
};
|
|
8758
9165
|
}
|
|
8759
9166
|
function stampCondition(condition, resolver) {
|
|
8760
9167
|
switch (condition.kind) {
|
|
8761
|
-
case "
|
|
9168
|
+
case "device-is": {
|
|
8762
9169
|
if (typeof condition.value !== "number" || condition.unit !== void 0) return condition;
|
|
8763
9170
|
const quantity = resolver.fieldQuantity(condition.cap, condition.fieldPath);
|
|
8764
9171
|
if (quantity === void 0 || unitsForQuantity(quantity).length < 2) return condition;
|
|
@@ -8799,7 +9206,7 @@ function stampAction(action, resolver) {
|
|
|
8799
9206
|
function prepareRecipeUnits(recipe, resolver) {
|
|
8800
9207
|
const stamped = {
|
|
8801
9208
|
...recipe,
|
|
8802
|
-
|
|
9209
|
+
when: stampCondition(recipe.when, resolver),
|
|
8803
9210
|
actions: recipe.actions.map((action) => stampAction(action, resolver))
|
|
8804
9211
|
};
|
|
8805
9212
|
try {
|
|
@@ -9288,6 +9695,25 @@ var addonSettingsCapability = {
|
|
|
9288
9695
|
addonId: z4.string(),
|
|
9289
9696
|
nodeId: z4.string().optional()
|
|
9290
9697
|
}), SettingsSchemaWithValuesSchema.nullable()),
|
|
9698
|
+
getDeclaredSettingsKeys: method(z4.object({
|
|
9699
|
+
addonId: z4.string(),
|
|
9700
|
+
nodeId: z4.string().optional()
|
|
9701
|
+
}), z4.object({ keys: z4.array(z4.string()) }).nullable(), { auth: "admin" }),
|
|
9702
|
+
unsetStoredSettingsKeys: method(z4.object({
|
|
9703
|
+
addonId: z4.string(),
|
|
9704
|
+
nodeId: z4.string().optional(),
|
|
9705
|
+
keys: z4.array(z4.string())
|
|
9706
|
+
}), z4.object({
|
|
9707
|
+
removed: z4.array(z4.string()),
|
|
9708
|
+
refused: z4.array(z4.object({
|
|
9709
|
+
key: z4.string(),
|
|
9710
|
+
reason: z4.string()
|
|
9711
|
+
})),
|
|
9712
|
+
secretsCleared: z4.number()
|
|
9713
|
+
}), {
|
|
9714
|
+
kind: "mutation",
|
|
9715
|
+
auth: "admin"
|
|
9716
|
+
}),
|
|
9291
9717
|
getDeviceSettings: method(z4.object({
|
|
9292
9718
|
addonId: z4.string(),
|
|
9293
9719
|
deviceId: z4.number(),
|
|
@@ -14154,6 +14580,8 @@ var StorageKindSchema = z4.object({
|
|
|
14154
14580
|
"orphaned",
|
|
14155
14581
|
"dropped"
|
|
14156
14582
|
]),
|
|
14583
|
+
/** An owner declared this kind since the engine started. */
|
|
14584
|
+
declaredThisBoot: z4.boolean(),
|
|
14157
14585
|
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
14158
14586
|
rows: z4.number().int().nonnegative().nullable(),
|
|
14159
14587
|
/** Rows x the average size of a sample; null for an empty kind. */
|
|
@@ -14165,6 +14593,48 @@ var StorageKindSchema = z4.object({
|
|
|
14165
14593
|
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
14166
14594
|
declaration: RecordKindDeclarationSchema
|
|
14167
14595
|
});
|
|
14596
|
+
z4.enum([
|
|
14597
|
+
"orphaned",
|
|
14598
|
+
"stale",
|
|
14599
|
+
"unknown"
|
|
14600
|
+
]);
|
|
14601
|
+
var StaleKindFindingSchema = z4.object({
|
|
14602
|
+
owner: z4.string(),
|
|
14603
|
+
kind: z4.string(),
|
|
14604
|
+
/** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
|
|
14605
|
+
verdict: z4.enum(["orphaned", "unknown"]),
|
|
14606
|
+
reason: z4.string(),
|
|
14607
|
+
rows: z4.number().int().nonnegative().nullable(),
|
|
14608
|
+
/** local-auth's tables are never dropped by this audit. */
|
|
14609
|
+
protected: z4.boolean()
|
|
14610
|
+
});
|
|
14611
|
+
var StaleSettingsKeyFindingSchema = z4.object({
|
|
14612
|
+
addonId: z4.string(),
|
|
14613
|
+
/** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
|
|
14614
|
+
key: z4.string(),
|
|
14615
|
+
/**
|
|
14616
|
+
* Keychain refs whose ONLY marker sits under this key: in use by stale data,
|
|
14617
|
+
* not orphans yet. Removing the key releases them.
|
|
14618
|
+
*/
|
|
14619
|
+
secretRefs: z4.array(z4.string())
|
|
14620
|
+
});
|
|
14621
|
+
var StaleUnknownSchema = z4.object({
|
|
14622
|
+
subject: z4.string(),
|
|
14623
|
+
reason: z4.string()
|
|
14624
|
+
});
|
|
14625
|
+
var StaleDataReportSchema = z4.object({
|
|
14626
|
+
/** Epoch ms of the last finished scan; `null` before the first. */
|
|
14627
|
+
scannedAt: z4.number().nullable(),
|
|
14628
|
+
scanning: z4.boolean(),
|
|
14629
|
+
trigger: z4.enum([
|
|
14630
|
+
"boot",
|
|
14631
|
+
"interval",
|
|
14632
|
+
"manual"
|
|
14633
|
+
]).nullable(),
|
|
14634
|
+
kinds: z4.array(StaleKindFindingSchema),
|
|
14635
|
+
settingsKeys: z4.array(StaleSettingsKeyFindingSchema),
|
|
14636
|
+
unknown: z4.array(StaleUnknownSchema)
|
|
14637
|
+
});
|
|
14168
14638
|
var storageAdminCapability = {
|
|
14169
14639
|
name: "storage-admin",
|
|
14170
14640
|
scope: "system",
|
|
@@ -14189,7 +14659,49 @@ var storageAdminCapability = {
|
|
|
14189
14659
|
readRecords: method(RecordQueryInputSchema.extend({
|
|
14190
14660
|
owner: z4.string().min(1),
|
|
14191
14661
|
kind: RecordKindNameSchema
|
|
14192
|
-
}), RecordQueryResultSchema, { auth: "admin" })
|
|
14662
|
+
}), RecordQueryResultSchema, { auth: "admin" }),
|
|
14663
|
+
/**
|
|
14664
|
+
* The last stale-data scan (read-only detection): record kinds left behind
|
|
14665
|
+
* and settings keys no running addon declares. Held in memory; a scan runs
|
|
14666
|
+
* at hub boot, every 24 h and on `scanStaleData`.
|
|
14667
|
+
*/
|
|
14668
|
+
staleDataReport: method(z4.object({}), StaleDataReportSchema, { auth: "admin" }),
|
|
14669
|
+
/** Scan now. Detection only: it logs findings (owner and key, never values) and changes nothing. */
|
|
14670
|
+
scanStaleData: method(z4.object({}), StaleDataReportSchema, {
|
|
14671
|
+
kind: "mutation",
|
|
14672
|
+
auth: "admin"
|
|
14673
|
+
}),
|
|
14674
|
+
/**
|
|
14675
|
+
* Drop an ORPHANED kind: its rows, their keychain values, then mark it
|
|
14676
|
+
* dropped. The verdict is re-checked at call time; anything not still
|
|
14677
|
+
* orphaned (or a protected owner) is refused.
|
|
14678
|
+
*/
|
|
14679
|
+
dropOrphanedKind: method(z4.object({
|
|
14680
|
+
owner: z4.string().min(1),
|
|
14681
|
+
kind: RecordKindNameSchema
|
|
14682
|
+
}), z4.object({
|
|
14683
|
+
deleted: z4.number().int().nonnegative(),
|
|
14684
|
+
secretsReleased: z4.number().int().nonnegative()
|
|
14685
|
+
}), {
|
|
14686
|
+
kind: "mutation",
|
|
14687
|
+
auth: "admin",
|
|
14688
|
+
caller: "required"
|
|
14689
|
+
}),
|
|
14690
|
+
/**
|
|
14691
|
+
* Unset one stale settings key on a running addon (an owner-side write that
|
|
14692
|
+
* also clears the keychain values the key held). Re-checked at call time.
|
|
14693
|
+
*/
|
|
14694
|
+
unsetStaleSettingsKey: method(z4.object({
|
|
14695
|
+
addonId: z4.string().min(1),
|
|
14696
|
+
key: z4.string().min(1)
|
|
14697
|
+
}), z4.object({
|
|
14698
|
+
removed: z4.boolean(),
|
|
14699
|
+
secretsCleared: z4.number().int().nonnegative()
|
|
14700
|
+
}), {
|
|
14701
|
+
kind: "mutation",
|
|
14702
|
+
auth: "admin",
|
|
14703
|
+
caller: "required"
|
|
14704
|
+
})
|
|
14193
14705
|
}
|
|
14194
14706
|
};
|
|
14195
14707
|
var EntryKindInfoSchema = z4.object({
|
|
@@ -14321,6 +14833,19 @@ var keychainCapability = {
|
|
|
14321
14833
|
kind: "mutation",
|
|
14322
14834
|
auth: "admin"
|
|
14323
14835
|
}),
|
|
14836
|
+
/**
|
|
14837
|
+
* Release the values rows of a DROPPED record kind held, whatever the kind's
|
|
14838
|
+
* owner (stale-data audit). Reserved to `sqlite-settings`, the engine that
|
|
14839
|
+
* just deleted the rows: only refs scoped `row/records:<kind>/...` are
|
|
14840
|
+
* accepted, and the audit names the operator it acted for. Answers a count.
|
|
14841
|
+
*/
|
|
14842
|
+
releaseRecordRefs: method(z4.object({
|
|
14843
|
+
refs: RefListSchema,
|
|
14844
|
+
onBehalfOf: z4.string().min(1)
|
|
14845
|
+
}), z4.object({ released: z4.number().int() }), {
|
|
14846
|
+
kind: "mutation",
|
|
14847
|
+
auth: "admin"
|
|
14848
|
+
}),
|
|
14324
14849
|
/** The caller's own refs, metadata only. */
|
|
14325
14850
|
listMeta: method(z4.object({}), z4.array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }),
|
|
14326
14851
|
/** Whether values can be served — the save path refuses a write while locked. */
|
|
@@ -15215,7 +15740,9 @@ var DiscoveredChildDeviceSchema = z4.lazy(() => z4.object({
|
|
|
15215
15740
|
/** Upstream-system identity + rendering hints for the candidate. */
|
|
15216
15741
|
sourceInfo: SourceInfoSchema.optional(),
|
|
15217
15742
|
/** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
|
|
15218
|
-
children: z4.array(DiscoveredChildDeviceSchema).readonly().optional()
|
|
15743
|
+
children: z4.array(DiscoveredChildDeviceSchema).readonly().optional(),
|
|
15744
|
+
/** `childNativeId` of the gateway candidate in the same list this device sits behind. */
|
|
15745
|
+
parentStableId: z4.string().optional()
|
|
15219
15746
|
}));
|
|
15220
15747
|
var DeviceDiscoveryStatusSchema = z4.object({
|
|
15221
15748
|
discovered: z4.array(DiscoveredChildDeviceSchema),
|
|
@@ -35433,6 +35960,71 @@ var DiscoveredDeviceSchema = z4.object({
|
|
|
35433
35960
|
type: z4.string(),
|
|
35434
35961
|
metadata: z4.record(z4.string(), z4.unknown()).optional()
|
|
35435
35962
|
});
|
|
35963
|
+
var DiscoveryModeSchema = z4.discriminatedUnion("kind", [
|
|
35964
|
+
z4.object({
|
|
35965
|
+
kind: z4.literal("mdns"),
|
|
35966
|
+
/** DNS-SD service type, e.g. `_esphomelib._tcp`. */
|
|
35967
|
+
serviceType: z4.string().min(1),
|
|
35968
|
+
/** Regular expression an instance name must match (case-insensitive). */
|
|
35969
|
+
instancePattern: z4.string().optional()
|
|
35970
|
+
}),
|
|
35971
|
+
z4.object({
|
|
35972
|
+
kind: z4.literal("ws-discovery"),
|
|
35973
|
+
/** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
|
|
35974
|
+
types: z4.array(z4.string()).optional()
|
|
35975
|
+
}),
|
|
35976
|
+
z4.object({
|
|
35977
|
+
kind: z4.literal("ssdp"),
|
|
35978
|
+
searchTarget: z4.string().min(1)
|
|
35979
|
+
}),
|
|
35980
|
+
z4.object({
|
|
35981
|
+
kind: z4.literal("udp-broadcast"),
|
|
35982
|
+
port: z4.number().int().min(1).max(65535),
|
|
35983
|
+
payloadHex: z4.string().regex(/^([0-9a-fA-F]{2})*$/),
|
|
35984
|
+
/** Port replies arrive on, when the device answers somewhere other than the source port. */
|
|
35985
|
+
listenPort: z4.number().int().min(1).max(65535).optional()
|
|
35986
|
+
}),
|
|
35987
|
+
z4.object({
|
|
35988
|
+
kind: z4.literal("http-probe"),
|
|
35989
|
+
port: z4.number().int().min(1).max(65535),
|
|
35990
|
+
path: z4.string().startsWith("/"),
|
|
35991
|
+
method: z4.enum(["GET", "POST"]).default("GET"),
|
|
35992
|
+
bodyText: z4.string().optional(),
|
|
35993
|
+
contentType: z4.string().optional(),
|
|
35994
|
+
tls: z4.boolean().default(false),
|
|
35995
|
+
/** Status codes that count as a hit. Omitted = any HTTP answer. */
|
|
35996
|
+
acceptStatus: z4.array(z4.number().int()).optional()
|
|
35997
|
+
}),
|
|
35998
|
+
z4.object({
|
|
35999
|
+
kind: z4.literal("custom"),
|
|
36000
|
+
/** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
|
|
36001
|
+
reason: z4.string().min(1)
|
|
36002
|
+
})
|
|
36003
|
+
]);
|
|
36004
|
+
var DiscoveryHitSchema = z4.object({
|
|
36005
|
+
/** Index into the provider's own `getDiscoveryModes()` array. */
|
|
36006
|
+
modeIndex: z4.number().int().min(0),
|
|
36007
|
+
host: z4.string(),
|
|
36008
|
+
port: z4.number().int(),
|
|
36009
|
+
http: z4.object({
|
|
36010
|
+
status: z4.number().int(),
|
|
36011
|
+
headers: z4.record(z4.string(), z4.string()),
|
|
36012
|
+
/** At most 16 KiB. */
|
|
36013
|
+
bodyText: z4.string()
|
|
36014
|
+
}).optional(),
|
|
36015
|
+
mdns: z4.object({
|
|
36016
|
+
instance: z4.string(),
|
|
36017
|
+
host: z4.string(),
|
|
36018
|
+
port: z4.number().int(),
|
|
36019
|
+
txt: z4.record(z4.string(), z4.string())
|
|
36020
|
+
}).optional(),
|
|
36021
|
+
udp: z4.object({ payloadHex: z4.string() }).optional(),
|
|
36022
|
+
wsd: z4.object({
|
|
36023
|
+
xaddrs: z4.array(z4.string()),
|
|
36024
|
+
types: z4.array(z4.string()),
|
|
36025
|
+
scopes: z4.array(z4.string())
|
|
36026
|
+
}).optional()
|
|
36027
|
+
});
|
|
35436
36028
|
var DiscoveryCandidateSchema = z4.object({
|
|
35437
36029
|
stableId: z4.string(),
|
|
35438
36030
|
type: z4.enum(DeviceType),
|
|
@@ -35463,7 +36055,17 @@ var DiscoveryCandidateSchema = z4.object({
|
|
|
35463
36055
|
/** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
|
|
35464
36056
|
onboardedDeviceId: z4.number().optional(),
|
|
35465
36057
|
/** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
|
|
35466
|
-
onboardedName: z4.string().optional()
|
|
36058
|
+
onboardedName: z4.string().optional(),
|
|
36059
|
+
/**
|
|
36060
|
+
* This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
|
|
36061
|
+
* not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
|
|
36062
|
+
* which is still read for one release.
|
|
36063
|
+
*/
|
|
36064
|
+
setupIntegration: z4.boolean().optional(),
|
|
36065
|
+
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
36066
|
+
parentStableId: z4.string().optional(),
|
|
36067
|
+
/** `false` = shown for context but not pickable. Default true. */
|
|
36068
|
+
selectable: z4.boolean().optional()
|
|
35467
36069
|
});
|
|
35468
36070
|
var DeviceSummarySchema = z4.object({
|
|
35469
36071
|
id: z4.number(),
|
|
@@ -35541,6 +36143,24 @@ var deviceProviderCapability = {
|
|
|
35541
36143
|
auth: "admin"
|
|
35542
36144
|
}),
|
|
35543
36145
|
/**
|
|
36146
|
+
* How this provider is found on the network. `[]` = the provider has no
|
|
36147
|
+
* declarative mode and keeps answering `discoverDevices` itself.
|
|
36148
|
+
*/
|
|
36149
|
+
getDiscoveryModes: method(z4.object({}), z4.array(DiscoveryModeSchema)),
|
|
36150
|
+
/**
|
|
36151
|
+
* Turn the engine's raw hits into candidates. The provider may make
|
|
36152
|
+
* follow-up UNICAST requests to a hit host (an authenticated call, an
|
|
36153
|
+
* identity check) and never sweeps a subnet. `params` is the per-scan form
|
|
36154
|
+
* from `getDiscoveryParamsSchema`.
|
|
36155
|
+
*/
|
|
36156
|
+
classifyDiscoveryHits: method(z4.object({
|
|
36157
|
+
hits: z4.array(DiscoveryHitSchema),
|
|
36158
|
+
params: z4.record(z4.string(), z4.unknown()).optional()
|
|
36159
|
+
}), z4.array(DiscoveryCandidateSchema), {
|
|
36160
|
+
kind: "mutation",
|
|
36161
|
+
auth: "admin"
|
|
36162
|
+
}),
|
|
36163
|
+
/**
|
|
35544
36164
|
* Optional form schema (`ConfigUISchema`) for the EXTRA per-scan inputs a
|
|
35545
36165
|
* provider accepts (e.g. Gree's broadcast address for a different subnet).
|
|
35546
36166
|
* `null` when the provider takes no extra scan params — the generic
|
|
@@ -36796,20 +37416,26 @@ var deviceManagerCapability = {
|
|
|
36796
37416
|
label: z4.string()
|
|
36797
37417
|
})).readonly() }), { auth: "admin" }),
|
|
36798
37418
|
/** Scan EVERY discovery-capable provider; returns candidates grouped by addon. */
|
|
36799
|
-
discoverAllProviders: method(
|
|
36800
|
-
|
|
36801
|
-
|
|
36802
|
-
|
|
36803
|
-
|
|
36804
|
-
|
|
36805
|
-
|
|
36806
|
-
|
|
36807
|
-
|
|
37419
|
+
discoverAllProviders: method(
|
|
37420
|
+
/** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
|
|
37421
|
+
z4.object({ subnets: z4.array(z4.string()).optional() }),
|
|
37422
|
+
z4.object({ groups: z4.array(z4.object({
|
|
37423
|
+
addonId: z4.string(),
|
|
37424
|
+
label: z4.string(),
|
|
37425
|
+
candidates: z4.array(DiscoveryCandidateSchema).readonly(),
|
|
37426
|
+
error: z4.string().nullable()
|
|
37427
|
+
})).readonly() }),
|
|
37428
|
+
{
|
|
37429
|
+
kind: "mutation",
|
|
37430
|
+
auth: "admin"
|
|
37431
|
+
}
|
|
37432
|
+
),
|
|
36808
37433
|
/** Scan ONE provider with optional provider-specific params (subnet /
|
|
36809
37434
|
* broadcast addr) — the per-integration "Scan network" surface. */
|
|
36810
37435
|
discoverProvider: method(z4.object({
|
|
36811
37436
|
addonId: z4.string(),
|
|
36812
|
-
params: z4.record(z4.string(), z4.unknown()).optional()
|
|
37437
|
+
params: z4.record(z4.string(), z4.unknown()).optional(),
|
|
37438
|
+
subnets: z4.array(z4.string()).optional()
|
|
36813
37439
|
}), z4.object({ candidates: z4.array(DiscoveryCandidateSchema).readonly() }), {
|
|
36814
37440
|
kind: "mutation",
|
|
36815
37441
|
auth: "admin"
|
|
@@ -56177,7 +56803,9 @@ var integrationsCapability = {
|
|
|
56177
56803
|
id: z4.string(),
|
|
56178
56804
|
settings: z4.record(z4.string(), z4.unknown()),
|
|
56179
56805
|
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
56180
|
-
secretKeys: z4.array(z4.string()).optional()
|
|
56806
|
+
secretKeys: z4.array(z4.string()).optional(),
|
|
56807
|
+
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
56808
|
+
skipRestart: z4.boolean().optional()
|
|
56181
56809
|
}), z4.object({ success: z4.boolean() }), {
|
|
56182
56810
|
kind: "mutation",
|
|
56183
56811
|
auth: "admin"
|
|
@@ -56713,6 +57341,37 @@ var TlsStatusSchema = z4.object({
|
|
|
56713
57341
|
hasCa: z4.boolean()
|
|
56714
57342
|
}).nullable()
|
|
56715
57343
|
});
|
|
57344
|
+
var ScanRequestSchema = z4.object({
|
|
57345
|
+
providerId: z4.string().min(1),
|
|
57346
|
+
modes: z4.array(DiscoveryModeSchema)
|
|
57347
|
+
});
|
|
57348
|
+
var ScanErrorSchema = z4.object({
|
|
57349
|
+
/** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
|
|
57350
|
+
providerId: z4.string().optional(),
|
|
57351
|
+
/** The mode family that failed (`mdns`, `http-probe`, …). */
|
|
57352
|
+
mode: z4.string().optional(),
|
|
57353
|
+
message: z4.string()
|
|
57354
|
+
});
|
|
57355
|
+
var ScanStatsSchema = z4.object({
|
|
57356
|
+
hostsProbed: z4.number().int(),
|
|
57357
|
+
hits: z4.number().int(),
|
|
57358
|
+
timeouts: z4.number().int(),
|
|
57359
|
+
byMode: z4.record(z4.string(), z4.object({
|
|
57360
|
+
probed: z4.number().int(),
|
|
57361
|
+
hits: z4.number().int(),
|
|
57362
|
+
timeouts: z4.number().int()
|
|
57363
|
+
}))
|
|
57364
|
+
});
|
|
57365
|
+
var ScanNetworkResultSchema = z4.object({
|
|
57366
|
+
hits: z4.array(z4.object({
|
|
57367
|
+
providerId: z4.string(),
|
|
57368
|
+
hit: DiscoveryHitSchema
|
|
57369
|
+
})),
|
|
57370
|
+
/** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
|
|
57371
|
+
subnetsScanned: z4.array(z4.string()),
|
|
57372
|
+
errors: z4.array(ScanErrorSchema),
|
|
57373
|
+
stats: ScanStatsSchema
|
|
57374
|
+
});
|
|
56716
57375
|
var localNetworkCapability = {
|
|
56717
57376
|
name: "local-network",
|
|
56718
57377
|
scope: "system",
|
|
@@ -56926,6 +57585,34 @@ var localNetworkCapability = {
|
|
|
56926
57585
|
kind: "mutation",
|
|
56927
57586
|
auth: "admin"
|
|
56928
57587
|
}),
|
|
57588
|
+
/**
|
|
57589
|
+
* The LAN discovery engine. Every provider that declared discovery modes
|
|
57590
|
+
* (`device-provider.getDiscoveryModes`) is scanned in ONE pass: a single
|
|
57591
|
+
* mDNS browse for the union of service types, one WS-Discovery probe, one
|
|
57592
|
+
* SSDP search per target and one sweep that visits each host once and runs
|
|
57593
|
+
* every http-probe of every provider. Subnets scanned = the hub's own
|
|
57594
|
+
* non-internal IPv4 /24s ∪ the saved list (`getScanSubnets`) ∪ `subnets`.
|
|
57595
|
+
* A bad CIDR, or more than 4096 hosts in total, refuses the scan.
|
|
57596
|
+
*/
|
|
57597
|
+
scanNetwork: method(z4.object({
|
|
57598
|
+
requests: z4.array(ScanRequestSchema),
|
|
57599
|
+
subnets: z4.array(z4.string()).optional()
|
|
57600
|
+
}), ScanNetworkResultSchema, {
|
|
57601
|
+
kind: "mutation",
|
|
57602
|
+
auth: "admin",
|
|
57603
|
+
timeoutMs: 9e4
|
|
57604
|
+
}),
|
|
57605
|
+
/** Extra subnets every scan covers besides the hub's own, as CIDR strings. */
|
|
57606
|
+
getScanSubnets: method(z4.void(), z4.object({ subnets: z4.array(z4.string()) }), { auth: "admin" }),
|
|
57607
|
+
/**
|
|
57608
|
+
* Replace the saved extra subnets (a cluster setting). Each entry is IPv4
|
|
57609
|
+
* CIDR, at most a /22; the list is refused when it is invalid or its hosts
|
|
57610
|
+
* would exceed the scan cap.
|
|
57611
|
+
*/
|
|
57612
|
+
setScanSubnets: method(z4.object({ subnets: z4.array(z4.string()) }), z4.object({ subnets: z4.array(z4.string()) }), {
|
|
57613
|
+
kind: "mutation",
|
|
57614
|
+
auth: "admin"
|
|
57615
|
+
}),
|
|
56929
57616
|
/** Return to the generated local CA. The uploaded certificate stays stored (D873). */
|
|
56930
57617
|
revertToGeneratedCertificate: method(z4.void(), TlsStatusSchema, {
|
|
56931
57618
|
kind: "mutation",
|
|
@@ -66357,6 +67044,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
66357
67044
|
access: "create",
|
|
66358
67045
|
role: "admin"
|
|
66359
67046
|
},
|
|
67047
|
+
"addonSettings.getDeclaredSettingsKeys": {
|
|
67048
|
+
capName: "addon-settings",
|
|
67049
|
+
capScope: "system",
|
|
67050
|
+
addonId: null,
|
|
67051
|
+
access: "view",
|
|
67052
|
+
role: "admin"
|
|
67053
|
+
},
|
|
66360
67054
|
"addonSettings.getDeviceSettings": {
|
|
66361
67055
|
capName: "addon-settings",
|
|
66362
67056
|
capScope: "system",
|
|
@@ -66378,6 +67072,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
66378
67072
|
access: "view",
|
|
66379
67073
|
role: "admin"
|
|
66380
67074
|
},
|
|
67075
|
+
"addonSettings.unsetStoredSettingsKeys": {
|
|
67076
|
+
capName: "addon-settings",
|
|
67077
|
+
capScope: "system",
|
|
67078
|
+
addonId: null,
|
|
67079
|
+
access: "create",
|
|
67080
|
+
role: "admin"
|
|
67081
|
+
},
|
|
66381
67082
|
"addonSettings.updateDeviceSettings": {
|
|
66382
67083
|
capName: "addon-settings",
|
|
66383
67084
|
capScope: "system",
|
|
@@ -68793,6 +69494,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
68793
69494
|
access: "create",
|
|
68794
69495
|
role: "admin"
|
|
68795
69496
|
},
|
|
69497
|
+
"deviceProvider.classifyDiscoveryHits": {
|
|
69498
|
+
capName: "device-provider",
|
|
69499
|
+
capScope: "system",
|
|
69500
|
+
addonId: null,
|
|
69501
|
+
access: "create",
|
|
69502
|
+
role: "admin"
|
|
69503
|
+
},
|
|
68796
69504
|
"deviceProvider.createDevice": {
|
|
68797
69505
|
capName: "device-provider",
|
|
68798
69506
|
capScope: "system",
|
|
@@ -68821,6 +69529,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
68821
69529
|
access: "view",
|
|
68822
69530
|
role: "admin"
|
|
68823
69531
|
},
|
|
69532
|
+
"deviceProvider.getDiscoveryModes": {
|
|
69533
|
+
capName: "device-provider",
|
|
69534
|
+
capScope: "system",
|
|
69535
|
+
addonId: null,
|
|
69536
|
+
access: "view",
|
|
69537
|
+
role: "admin"
|
|
69538
|
+
},
|
|
68824
69539
|
"deviceProvider.getDiscoveryParamsSchema": {
|
|
68825
69540
|
capName: "device-provider",
|
|
68826
69541
|
capScope: "system",
|
|
@@ -69598,6 +70313,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
69598
70313
|
access: "create",
|
|
69599
70314
|
role: "admin"
|
|
69600
70315
|
},
|
|
70316
|
+
"keychain.releaseRecordRefs": {
|
|
70317
|
+
capName: "keychain",
|
|
70318
|
+
capScope: "system",
|
|
70319
|
+
addonId: null,
|
|
70320
|
+
access: "create",
|
|
70321
|
+
role: "admin"
|
|
70322
|
+
},
|
|
69601
70323
|
"keychain.resolve": {
|
|
69602
70324
|
capName: "keychain",
|
|
69603
70325
|
capScope: "system",
|
|
@@ -70018,6 +70740,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
70018
70740
|
access: "view",
|
|
70019
70741
|
role: "admin"
|
|
70020
70742
|
},
|
|
70743
|
+
"localNetwork.getScanSubnets": {
|
|
70744
|
+
capName: "local-network",
|
|
70745
|
+
capScope: "system",
|
|
70746
|
+
addonId: null,
|
|
70747
|
+
access: "view",
|
|
70748
|
+
role: "admin"
|
|
70749
|
+
},
|
|
70021
70750
|
"localNetwork.getTlsStatus": {
|
|
70022
70751
|
capName: "local-network",
|
|
70023
70752
|
capScope: "system",
|
|
@@ -70067,6 +70796,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
70067
70796
|
access: "create",
|
|
70068
70797
|
role: "admin"
|
|
70069
70798
|
},
|
|
70799
|
+
"localNetwork.scanNetwork": {
|
|
70800
|
+
capName: "local-network",
|
|
70801
|
+
capScope: "system",
|
|
70802
|
+
addonId: null,
|
|
70803
|
+
access: "create",
|
|
70804
|
+
role: "admin"
|
|
70805
|
+
},
|
|
70070
70806
|
"localNetwork.setAddressRole": {
|
|
70071
70807
|
capName: "local-network",
|
|
70072
70808
|
capScope: "system",
|
|
@@ -70088,6 +70824,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
70088
70824
|
access: "create",
|
|
70089
70825
|
role: "admin"
|
|
70090
70826
|
},
|
|
70827
|
+
"localNetwork.setScanSubnets": {
|
|
70828
|
+
capName: "local-network",
|
|
70829
|
+
capScope: "system",
|
|
70830
|
+
addonId: null,
|
|
70831
|
+
access: "create",
|
|
70832
|
+
role: "admin"
|
|
70833
|
+
},
|
|
70091
70834
|
"localNetwork.setViewerEndpoints": {
|
|
70092
70835
|
capName: "local-network",
|
|
70093
70836
|
capScope: "system",
|
|
@@ -74309,6 +75052,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
74309
75052
|
access: "create",
|
|
74310
75053
|
role: "admin"
|
|
74311
75054
|
},
|
|
75055
|
+
"storageAdmin.dropOrphanedKind": {
|
|
75056
|
+
capName: "storage-admin",
|
|
75057
|
+
capScope: "system",
|
|
75058
|
+
addonId: null,
|
|
75059
|
+
access: "delete",
|
|
75060
|
+
role: "admin"
|
|
75061
|
+
},
|
|
74312
75062
|
"storageAdmin.listKinds": {
|
|
74313
75063
|
capName: "storage-admin",
|
|
74314
75064
|
capScope: "system",
|
|
@@ -74330,6 +75080,27 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
74330
75080
|
access: "view",
|
|
74331
75081
|
role: "admin"
|
|
74332
75082
|
},
|
|
75083
|
+
"storageAdmin.scanStaleData": {
|
|
75084
|
+
capName: "storage-admin",
|
|
75085
|
+
capScope: "system",
|
|
75086
|
+
addonId: null,
|
|
75087
|
+
access: "create",
|
|
75088
|
+
role: "admin"
|
|
75089
|
+
},
|
|
75090
|
+
"storageAdmin.staleDataReport": {
|
|
75091
|
+
capName: "storage-admin",
|
|
75092
|
+
capScope: "system",
|
|
75093
|
+
addonId: null,
|
|
75094
|
+
access: "view",
|
|
75095
|
+
role: "admin"
|
|
75096
|
+
},
|
|
75097
|
+
"storageAdmin.unsetStaleSettingsKey": {
|
|
75098
|
+
capName: "storage-admin",
|
|
75099
|
+
capScope: "system",
|
|
75100
|
+
addonId: null,
|
|
75101
|
+
access: "create",
|
|
75102
|
+
role: "admin"
|
|
75103
|
+
},
|
|
74333
75104
|
"storageEvictable.evict": {
|
|
74334
75105
|
capName: "storage-evictable",
|
|
74335
75106
|
capScope: "system",
|
|
@@ -85341,10 +86112,12 @@ function describeHub(hub) {
|
|
|
85341
86112
|
export {
|
|
85342
86113
|
isReadingQuantity,
|
|
85343
86114
|
FFMPEG_PATH_ENV,
|
|
86115
|
+
MACRO_LABELS,
|
|
86116
|
+
countWhenLeaves,
|
|
86117
|
+
convertLegacyWhen,
|
|
85344
86118
|
AutomationRecipeSchema,
|
|
85345
86119
|
validateRecipeBounds,
|
|
85346
86120
|
generateAutomationBlock,
|
|
85347
|
-
MACRO_LABELS,
|
|
85348
86121
|
unitsForQuantity,
|
|
85349
86122
|
capUnitResolver,
|
|
85350
86123
|
prepareRecipeUnits,
|