camstack 1.1.13 → 1.1.15

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.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-C2M2zF7x.mjs
14525
+ // ../types/dist/sleep-BiDFW0E7.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -14691,6 +14691,7 @@ var CamStreamKindSchema = external_exports.enum([
14691
14691
  "pull-rtsp",
14692
14692
  "pull-rtmp",
14693
14693
  "pull-http",
14694
+ "pull-flv",
14694
14695
  "pull-rfc4571",
14695
14696
  "push-annexb",
14696
14697
  "derived"
@@ -14958,6 +14959,7 @@ var DeviceType = /* @__PURE__ */ (function(DeviceType2) {
14958
14959
  DeviceType2["LawnMower"] = "lawn-mower";
14959
14960
  DeviceType2["Container"] = "container";
14960
14961
  DeviceType2["Image"] = "image";
14962
+ DeviceType2["PetFeeder"] = "pet-feeder";
14961
14963
  return DeviceType2;
14962
14964
  })({});
14963
14965
  var DeviceFeature = /* @__PURE__ */ (function(DeviceFeature2) {
@@ -15913,6 +15915,513 @@ var APPLE_SA_TO_MACRO = {
15913
15915
  var _macroLookup = /* @__PURE__ */ new Map();
15914
15916
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
15915
15917
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
15918
+ var ExpressionParseError = class extends Error {
15919
+ position;
15920
+ constructor(message, position) {
15921
+ super(message);
15922
+ this.name = "ExpressionParseError";
15923
+ this.position = position;
15924
+ }
15925
+ };
15926
+ var ExpressionEvalError = class extends Error {
15927
+ constructor(message) {
15928
+ super(message);
15929
+ this.name = "ExpressionEvalError";
15930
+ }
15931
+ };
15932
+ var MAX_EXPRESSION_SOURCE_LENGTH = 2048;
15933
+ var EXPRESSION_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
15934
+ var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
15935
+ "now",
15936
+ "true",
15937
+ "false",
15938
+ "null"
15939
+ ]);
15940
+ var KEYWORDS = /* @__PURE__ */ new Set([
15941
+ "true",
15942
+ "false",
15943
+ "null"
15944
+ ]);
15945
+ function isDigit(ch) {
15946
+ return ch >= "0" && ch <= "9";
15947
+ }
15948
+ function isIdentStart(ch) {
15949
+ return ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch === "_";
15950
+ }
15951
+ function isIdentPart(ch) {
15952
+ return isIdentStart(ch) || isDigit(ch);
15953
+ }
15954
+ function isWhitespace(ch) {
15955
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f" || ch === "\v";
15956
+ }
15957
+ function tokenize(source) {
15958
+ if (source.length > 2048) throw new ExpressionParseError(`expression too long (${source.length} > ${MAX_EXPRESSION_SOURCE_LENGTH} chars)`, 0);
15959
+ const tokens = [];
15960
+ let i = 0;
15961
+ const n = source.length;
15962
+ while (i < n) {
15963
+ const ch = source[i];
15964
+ if (isWhitespace(ch)) {
15965
+ i += 1;
15966
+ continue;
15967
+ }
15968
+ if (isDigit(ch)) {
15969
+ const start = i;
15970
+ while (i < n && isDigit(source[i])) i += 1;
15971
+ if (i < n && source[i] === ".") {
15972
+ if (i + 1 >= n || !isDigit(source[i + 1])) throw new ExpressionParseError("malformed number: decimal point needs a digit", i);
15973
+ i += 1;
15974
+ while (i < n && isDigit(source[i])) i += 1;
15975
+ }
15976
+ const text = source.slice(start, i);
15977
+ const value = Number(text);
15978
+ if (!Number.isFinite(value)) throw new ExpressionParseError(`malformed number: '${text}'`, start);
15979
+ tokens.push({
15980
+ type: "number",
15981
+ value,
15982
+ pos: start
15983
+ });
15984
+ continue;
15985
+ }
15986
+ if (ch === "'" || ch === '"') {
15987
+ const quote = ch;
15988
+ const start = i;
15989
+ i += 1;
15990
+ let out = "";
15991
+ let closed = false;
15992
+ while (i < n) {
15993
+ const c = source[i];
15994
+ if (c === "\\") {
15995
+ const next = i + 1 < n ? source[i + 1] : "";
15996
+ if (next === "\\" || next === "'" || next === '"') {
15997
+ out += next;
15998
+ i += 2;
15999
+ continue;
16000
+ }
16001
+ throw new ExpressionParseError(`invalid string escape: '\\${next}'`, i);
16002
+ }
16003
+ if (c === quote) {
16004
+ closed = true;
16005
+ i += 1;
16006
+ break;
16007
+ }
16008
+ out += c;
16009
+ i += 1;
16010
+ }
16011
+ if (!closed) throw new ExpressionParseError("unterminated string literal", start);
16012
+ tokens.push({
16013
+ type: "string",
16014
+ value: out,
16015
+ pos: start
16016
+ });
16017
+ continue;
16018
+ }
16019
+ if (isIdentStart(ch)) {
16020
+ const start = i;
16021
+ while (i < n && isIdentPart(source[i])) i += 1;
16022
+ const text = source.slice(start, i);
16023
+ if (KEYWORDS.has(text)) tokens.push({
16024
+ type: "keyword",
16025
+ keyword: keywordOf(text),
16026
+ pos: start
16027
+ });
16028
+ else tokens.push({
16029
+ type: "identifier",
16030
+ name: text,
16031
+ pos: start
16032
+ });
16033
+ continue;
16034
+ }
16035
+ const two = i + 1 < n ? source.slice(i, i + 2) : "";
16036
+ if (two === "<=" || two === ">=" || two === "==" || two === "!=" || two === "&&" || two === "||") {
16037
+ tokens.push({
16038
+ type: "punct",
16039
+ punct: two,
16040
+ pos: i
16041
+ });
16042
+ i += 2;
16043
+ continue;
16044
+ }
16045
+ if (isSinglePunct(ch)) {
16046
+ tokens.push({
16047
+ type: "punct",
16048
+ punct: ch,
16049
+ pos: i
16050
+ });
16051
+ i += 1;
16052
+ continue;
16053
+ }
16054
+ throw new ExpressionParseError(`unexpected character '${ch}'`, i);
16055
+ }
16056
+ tokens.push({
16057
+ type: "eof",
16058
+ pos: n
16059
+ });
16060
+ return tokens;
16061
+ }
16062
+ function keywordOf(text) {
16063
+ if (text === "true") return "true";
16064
+ if (text === "false") return "false";
16065
+ return "null";
16066
+ }
16067
+ function isSinglePunct(ch) {
16068
+ return ch === "(" || ch === ")" || ch === "," || ch === "?" || ch === ":" || ch === "+" || ch === "-" || ch === "*" || ch === "/" || ch === "%" || ch === "!" || ch === "<" || ch === ">";
16069
+ }
16070
+ function asFiniteNumber(value, name, index) {
16071
+ if (typeof value !== "number" || !Number.isFinite(value)) throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a finite number`);
16072
+ return value;
16073
+ }
16074
+ function asString$1(value, name, index) {
16075
+ if (typeof value !== "string") throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a string`);
16076
+ return value;
16077
+ }
16078
+ function finiteResult(value, name) {
16079
+ if (!Number.isFinite(value)) throw new ExpressionEvalError(`${name}: produced a non-finite result`);
16080
+ return value;
16081
+ }
16082
+ function allFiniteNumbers(args, name) {
16083
+ return args.map((a, idx) => asFiniteNumber(a, name, idx));
16084
+ }
16085
+ var INF = Number.POSITIVE_INFINITY;
16086
+ var table = {
16087
+ min: {
16088
+ minArgs: 1,
16089
+ maxArgs: INF,
16090
+ apply: (args) => finiteResult(Math.min(...allFiniteNumbers(args, "min")), "min")
16091
+ },
16092
+ max: {
16093
+ minArgs: 1,
16094
+ maxArgs: INF,
16095
+ apply: (args) => finiteResult(Math.max(...allFiniteNumbers(args, "max")), "max")
16096
+ },
16097
+ abs: {
16098
+ minArgs: 1,
16099
+ maxArgs: 1,
16100
+ apply: (args) => finiteResult(Math.abs(asFiniteNumber(args[0], "abs", 0)), "abs")
16101
+ },
16102
+ floor: {
16103
+ minArgs: 1,
16104
+ maxArgs: 1,
16105
+ apply: (args) => finiteResult(Math.floor(asFiniteNumber(args[0], "floor", 0)), "floor")
16106
+ },
16107
+ ceil: {
16108
+ minArgs: 1,
16109
+ maxArgs: 1,
16110
+ apply: (args) => finiteResult(Math.ceil(asFiniteNumber(args[0], "ceil", 0)), "ceil")
16111
+ },
16112
+ sqrt: {
16113
+ minArgs: 1,
16114
+ maxArgs: 1,
16115
+ apply: (args) => finiteResult(Math.sqrt(asFiniteNumber(args[0], "sqrt", 0)), "sqrt")
16116
+ },
16117
+ round: {
16118
+ minArgs: 1,
16119
+ maxArgs: 2,
16120
+ apply: (args) => {
16121
+ const x = asFiniteNumber(args[0], "round", 0);
16122
+ const digits = args.length > 1 ? Math.trunc(asFiniteNumber(args[1], "round", 1)) : 0;
16123
+ if (digits < 0 || digits > 100) throw new ExpressionEvalError("round: digits must be between 0 and 100");
16124
+ const factor = 10 ** digits;
16125
+ return finiteResult(Math.round(x * factor) / factor, "round");
16126
+ }
16127
+ },
16128
+ pow: {
16129
+ minArgs: 2,
16130
+ maxArgs: 2,
16131
+ apply: (args) => finiteResult(asFiniteNumber(args[0], "pow", 0) ** asFiniteNumber(args[1], "pow", 1), "pow")
16132
+ },
16133
+ clamp: {
16134
+ minArgs: 3,
16135
+ maxArgs: 3,
16136
+ apply: (args) => {
16137
+ const x = asFiniteNumber(args[0], "clamp", 0);
16138
+ const lo = asFiniteNumber(args[1], "clamp", 1);
16139
+ const hi = asFiniteNumber(args[2], "clamp", 2);
16140
+ if (lo > hi) throw new ExpressionEvalError("clamp: lower bound is greater than upper bound");
16141
+ return finiteResult(Math.min(hi, Math.max(lo, x)), "clamp");
16142
+ }
16143
+ },
16144
+ avg: {
16145
+ minArgs: 1,
16146
+ maxArgs: INF,
16147
+ apply: (args) => {
16148
+ const nums = allFiniteNumbers(args, "avg");
16149
+ return finiteResult(nums.reduce((acc, v) => acc + v, 0) / nums.length, "avg");
16150
+ }
16151
+ },
16152
+ sum: {
16153
+ minArgs: 1,
16154
+ maxArgs: INF,
16155
+ apply: (args) => finiteResult(allFiniteNumbers(args, "sum").reduce((acc, v) => acc + v, 0), "sum")
16156
+ },
16157
+ coalesce: {
16158
+ minArgs: 1,
16159
+ maxArgs: INF,
16160
+ apply: (args) => {
16161
+ for (const a of args) if (a !== null) return a;
16162
+ return null;
16163
+ }
16164
+ },
16165
+ age: {
16166
+ minArgs: 2,
16167
+ maxArgs: 2,
16168
+ apply: (args) => finiteResult(asFiniteNumber(args[0], "age", 0) - asFiniteNumber(args[1], "age", 1), "age")
16169
+ },
16170
+ convert: {
16171
+ minArgs: 3,
16172
+ maxArgs: 3,
16173
+ apply: (args, hooks) => {
16174
+ const x = asFiniteNumber(args[0], "convert", 0);
16175
+ const from = asString$1(args[1], "convert", 1).trim();
16176
+ const to = asString$1(args[2], "convert", 2).trim();
16177
+ if (hooks.convert) {
16178
+ const out = hooks.convert(x, from, to);
16179
+ if (out === null) throw new ExpressionEvalError(`convert: cannot convert '${from}' to '${to}'`);
16180
+ return finiteResult(out, "convert");
16181
+ }
16182
+ if (from === to) return x;
16183
+ throw new ExpressionEvalError("convert: unit conversion table not installed");
16184
+ }
16185
+ }
16186
+ };
16187
+ var EXPRESSION_BUILTINS = Object.freeze(Object.assign(/* @__PURE__ */ Object.create(null), table));
16188
+ var EXPRESSION_BUILTIN_NAMES = new Set(Object.keys(table));
16189
+ var BINARY_PRECEDENCE = {
16190
+ "||": 1,
16191
+ "&&": 2,
16192
+ "==": 3,
16193
+ "!=": 3,
16194
+ "<": 4,
16195
+ "<=": 4,
16196
+ ">": 4,
16197
+ ">=": 4,
16198
+ "+": 5,
16199
+ "-": 5,
16200
+ "*": 6,
16201
+ "/": 6,
16202
+ "%": 6
16203
+ };
16204
+ function isLogicalOp(op) {
16205
+ return op === "&&" || op === "||";
16206
+ }
16207
+ function isBinaryOp(op) {
16208
+ return op === "+" || op === "-" || op === "*" || op === "/" || op === "%" || op === "==" || op === "!=" || op === "<" || op === "<=" || op === ">" || op === ">=";
16209
+ }
16210
+ var Parser = class {
16211
+ tokens;
16212
+ pos = 0;
16213
+ nodeCount = 0;
16214
+ identifiers = /* @__PURE__ */ new Set();
16215
+ callees = /* @__PURE__ */ new Set();
16216
+ constructor(tokens) {
16217
+ this.tokens = tokens;
16218
+ }
16219
+ parse() {
16220
+ const ast = this.parseTernary();
16221
+ const tok = this.peek();
16222
+ if (tok.type !== "eof") throw new ExpressionParseError("unexpected trailing input", tok.pos);
16223
+ return {
16224
+ ast,
16225
+ identifiers: this.identifiers,
16226
+ callees: this.callees,
16227
+ nodeCount: this.nodeCount
16228
+ };
16229
+ }
16230
+ peek() {
16231
+ return this.tokens[this.pos];
16232
+ }
16233
+ next() {
16234
+ return this.tokens[this.pos++];
16235
+ }
16236
+ /** Consume a punctuator token, erroring if the next token isn't it. */
16237
+ expectPunct(punct) {
16238
+ const tok = this.peek();
16239
+ if (tok.type !== "punct" || tok.punct !== punct) throw new ExpressionParseError(`expected '${punct}'`, tok.pos);
16240
+ this.pos += 1;
16241
+ }
16242
+ matchPunct(punct) {
16243
+ const tok = this.peek();
16244
+ if (tok.type === "punct" && tok.punct === punct) {
16245
+ this.pos += 1;
16246
+ return true;
16247
+ }
16248
+ return false;
16249
+ }
16250
+ countNode() {
16251
+ this.nodeCount += 1;
16252
+ if (this.nodeCount > 256) throw new ExpressionParseError("expression too complex", this.peek().pos);
16253
+ }
16254
+ parseTernary() {
16255
+ const test = this.parseBinary(1);
16256
+ if (this.matchPunct("?")) {
16257
+ const consequent = this.parseTernary();
16258
+ this.expectPunct(":");
16259
+ const alternate = this.parseTernary();
16260
+ this.countNode();
16261
+ return {
16262
+ kind: "conditional",
16263
+ test,
16264
+ consequent,
16265
+ alternate
16266
+ };
16267
+ }
16268
+ return test;
16269
+ }
16270
+ parseBinary(minPrec) {
16271
+ let left = this.parseUnary();
16272
+ for (; ; ) {
16273
+ const tok = this.peek();
16274
+ if (tok.type !== "punct") break;
16275
+ const prec = BINARY_PRECEDENCE[tok.punct];
16276
+ if (prec === void 0 || prec < minPrec) break;
16277
+ const op = tok.punct;
16278
+ this.pos += 1;
16279
+ const right = this.parseBinary(prec + 1);
16280
+ this.countNode();
16281
+ if (isLogicalOp(op)) left = {
16282
+ kind: "logical",
16283
+ op,
16284
+ left,
16285
+ right
16286
+ };
16287
+ else if (isBinaryOp(op)) left = {
16288
+ kind: "binary",
16289
+ op,
16290
+ left,
16291
+ right
16292
+ };
16293
+ else throw new ExpressionParseError(`unexpected operator '${op}'`, tok.pos);
16294
+ }
16295
+ return left;
16296
+ }
16297
+ parseUnary() {
16298
+ const tok = this.peek();
16299
+ if (tok.type === "punct" && (tok.punct === "!" || tok.punct === "-")) {
16300
+ const op = tok.punct;
16301
+ this.pos += 1;
16302
+ const operand = this.parseUnary();
16303
+ this.countNode();
16304
+ return {
16305
+ kind: "unary",
16306
+ op,
16307
+ operand
16308
+ };
16309
+ }
16310
+ return this.parsePrimary();
16311
+ }
16312
+ parsePrimary() {
16313
+ const tok = this.next();
16314
+ switch (tok.type) {
16315
+ case "number":
16316
+ this.countNode();
16317
+ return {
16318
+ kind: "literal",
16319
+ value: tok.value
16320
+ };
16321
+ case "string":
16322
+ this.countNode();
16323
+ return {
16324
+ kind: "literal",
16325
+ value: tok.value
16326
+ };
16327
+ case "keyword":
16328
+ this.countNode();
16329
+ return {
16330
+ kind: "literal",
16331
+ value: tok.keyword === "null" ? null : tok.keyword === "true"
16332
+ };
16333
+ case "identifier": {
16334
+ const nextTok = this.peek();
16335
+ if (nextTok.type === "punct" && nextTok.punct === "(") return this.parseCall(tok.name, tok.pos);
16336
+ this.identifiers.add(tok.name);
16337
+ this.countNode();
16338
+ return {
16339
+ kind: "identifier",
16340
+ name: tok.name
16341
+ };
16342
+ }
16343
+ case "punct":
16344
+ if (tok.punct === "(") {
16345
+ const inner = this.parseTernary();
16346
+ this.expectPunct(")");
16347
+ return inner;
16348
+ }
16349
+ throw new ExpressionParseError(`unexpected token '${tok.punct}'`, tok.pos);
16350
+ case "eof":
16351
+ throw new ExpressionParseError("unexpected end of expression", tok.pos);
16352
+ }
16353
+ }
16354
+ parseCall(callee, pos) {
16355
+ if (!EXPRESSION_BUILTIN_NAMES.has(callee)) throw new ExpressionParseError(`unknown function '${callee}'`, pos);
16356
+ this.expectPunct("(");
16357
+ const args = [];
16358
+ if (!this.matchPunct(")")) for (; ; ) {
16359
+ args.push(this.parseTernary());
16360
+ if (args.length > 16) throw new ExpressionParseError(`too many arguments to '${callee}'`, pos);
16361
+ if (this.matchPunct(",")) continue;
16362
+ this.expectPunct(")");
16363
+ break;
16364
+ }
16365
+ this.callees.add(callee);
16366
+ this.countNode();
16367
+ return {
16368
+ kind: "call",
16369
+ callee,
16370
+ args
16371
+ };
16372
+ }
16373
+ };
16374
+ function parseExpression(source) {
16375
+ return new Parser(tokenize(source)).parse();
16376
+ }
16377
+ var EMPTY_HOOKS = Object.freeze({});
16378
+ var cache = /* @__PURE__ */ new Map();
16379
+ function getCached(source) {
16380
+ const hit = cache.get(source);
16381
+ if (hit !== void 0) {
16382
+ cache.delete(source);
16383
+ cache.set(source, hit);
16384
+ return hit;
16385
+ }
16386
+ let result;
16387
+ try {
16388
+ result = {
16389
+ ok: true,
16390
+ parsed: parseExpression(source)
16391
+ };
16392
+ } catch (err) {
16393
+ result = {
16394
+ ok: false,
16395
+ error: err instanceof ExpressionParseError ? err.message : String(err)
16396
+ };
16397
+ }
16398
+ cache.set(source, result);
16399
+ if (cache.size > 256) {
16400
+ const oldest = cache.keys().next().value;
16401
+ if (oldest !== void 0) cache.delete(oldest);
16402
+ }
16403
+ return result;
16404
+ }
16405
+ function compileExpressionSafe(source) {
16406
+ return getCached(source);
16407
+ }
16408
+ function validateExpressionSource(src) {
16409
+ const names = Object.keys(src.bindings);
16410
+ if (names.length > 32) return `too many bindings (${names.length} > 32)`;
16411
+ for (const name of names) {
16412
+ if (!EXPRESSION_IDENTIFIER_RE.test(name)) return `invalid binding name '${name}'`;
16413
+ if (RESERVED_BINDING_NAMES.has(name)) return `binding name '${name}' is reserved`;
16414
+ if (EXPRESSION_BUILTIN_NAMES.has(name)) return `binding name '${name}' shadows a builtin function`;
16415
+ }
16416
+ const compiled = compileExpressionSafe(src.expr);
16417
+ if (!compiled.ok) return compiled.error;
16418
+ const bound = new Set(names);
16419
+ for (const id of compiled.parsed.identifiers) {
16420
+ if (id === "now") continue;
16421
+ if (!bound.has(id)) return `expression references unbound identifier '${id}'`;
16422
+ }
16423
+ return null;
16424
+ }
15916
16425
  var AccessoryKind = {
15917
16426
  Siren: DeviceRole.Siren,
15918
16427
  Floodlight: DeviceRole.Floodlight,
@@ -16398,7 +16907,13 @@ var batteryCapability = {
16398
16907
  },
16399
16908
  status: {
16400
16909
  schema: BatteryStatusSchema,
16401
- kind: "push"
16910
+ kind: "push",
16911
+ empty: {
16912
+ percentage: 0,
16913
+ charging: "none",
16914
+ sleeping: false,
16915
+ lastUpdated: 0
16916
+ }
16402
16917
  },
16403
16918
  /**
16404
16919
  * Runtime-state slice — every provider that registers this cap
@@ -17432,7 +17947,25 @@ var consumablesCapability = {
17432
17947
  },
17433
17948
  status: {
17434
17949
  schema: ConsumablesStatusSchema,
17435
- kind: "push"
17950
+ kind: "push",
17951
+ empty: {
17952
+ items: [],
17953
+ lastChangedAt: 0
17954
+ },
17955
+ itemArray: {
17956
+ path: "items",
17957
+ keyField: "key",
17958
+ labelField: "label",
17959
+ itemSchema: ConsumableItemSchema,
17960
+ emptyItem: {
17961
+ key: "",
17962
+ label: "",
17963
+ level: null,
17964
+ status: null,
17965
+ lastResetAt: null,
17966
+ resettable: false
17967
+ }
17968
+ }
17436
17969
  },
17437
17970
  runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: external_exports.number() })
17438
17971
  };
@@ -18392,7 +18925,8 @@ var motionDetectionCapability = {
18392
18925
  methods: {
18393
18926
  analyze: method(external_exports.object({
18394
18927
  deviceId: external_exports.number(),
18395
- frame: FrameInputSchema
18928
+ frame: FrameInputSchema.optional(),
18929
+ frameHandle: FrameHandleSchema.optional()
18396
18930
  }), MotionAnalysisResultSchema, { kind: "mutation" }),
18397
18931
  removeCamera: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), { kind: "mutation" }),
18398
18932
  reset: method(external_exports.void(), external_exports.void(), { kind: "mutation" })
@@ -18683,11 +19217,20 @@ var pipelineExecutorCapability = {
18683
19217
  * legacy call shape used by existing benchmark code; once all
18684
19218
  * callers pass it explicitly we make it required.
18685
19219
  *
18686
- * Exactly one of `frame`, `imageBase64`, `referenceImage` must be
18687
- * provided:
19220
+ * Exactly one of `frame`, `frameHandle`, `imageBase64`,
19221
+ * `referenceImage` must be provided:
18688
19222
  * - `frame`: runtime dispatch path (runner → decoded broker frame).
18689
19223
  * Carries the raw buffer, dimensions, and format; the executor
18690
19224
  * uses it directly without base64 round-tripping.
19225
+ * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
19226
+ * decoded frame. Both runner and executor are hub-local processes
19227
+ * sharing `/dev/shm`, so the executor maps the named segment and
19228
+ * reads the pixels back zero-copy — eliminating the ~1.2MB
19229
+ * re-serialisation over UDS/MsgPack the `frame` path pays per call.
19230
+ * High-risk: the FrameRing is a latest-wins seqlock with no
19231
+ * refcount, so a recycled slot yields a null read; the executor
19232
+ * then degrades to an empty result and the runner ships pixels via
19233
+ * `frame` as the fallback (queue-depth gated on the runner side).
18691
19234
  * - `imageBase64`: one-shot test path (benchmark ImageTab).
18692
19235
  * - `referenceImage`: named file from the reference-image store.
18693
19236
  */
@@ -18695,6 +19238,12 @@ var pipelineExecutorCapability = {
18695
19238
  engine: PipelineEngineChoiceSchema.optional(),
18696
19239
  steps: external_exports.array(PipelineStepInputSchema).min(1),
18697
19240
  frame: FrameInputSchema.optional(),
19241
+ /**
19242
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
19243
+ * the decoded pixels live in. One more member of the one-of
19244
+ * frame/frameHandle/image/imageBase64/referenceImage group.
19245
+ */
19246
+ frameHandle: FrameHandleSchema.optional(),
18698
19247
  imageBase64: external_exports.string().optional(),
18699
19248
  /**
18700
19249
  * Binary JPEG bytes — preferred over `imageBase64` on internal
@@ -18915,6 +19464,13 @@ var ReportMotionInputSchema = external_exports.object({
18915
19464
  source: MotionSourceEnum.default("analyzer"),
18916
19465
  regions: external_exports.array(MotionRegionSchema).readonly().optional()
18917
19466
  });
19467
+ var RunnerFrameSourceSchema = external_exports.discriminatedUnion("kind", [external_exports.object({ kind: external_exports.literal("local-broker") }), external_exports.object({
19468
+ kind: external_exports.literal("remote-restream"),
19469
+ /** The camera's source-owner node (slice 1: always the hub). */
19470
+ ownerNodeId: external_exports.string(),
19471
+ /** Operator override for the owner host the runner dials. */
19472
+ hubHostnameOverride: external_exports.string().optional()
19473
+ })]).describe("Per-camera frame-source mode for the runner (P2c)");
18918
19474
  var RunnerCameraConfigSchema = external_exports.object({
18919
19475
  deviceId: external_exports.number(),
18920
19476
  /**
@@ -19000,7 +19556,15 @@ var RunnerCameraConfigSchema = external_exports.object({
19000
19556
  */
19001
19557
  onboardMotionDrivesAnalyzer: external_exports.boolean().default(true),
19002
19558
  occupancyRecheckSec: external_exports.number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
19003
- occupancyRecheckFrames: external_exports.number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default)
19559
+ occupancyRecheckFrames: external_exports.number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
19560
+ /**
19561
+ * Where this runner gets the camera's decoded frames (P2c). Defaulted so
19562
+ * every existing payload behaves as `local-broker` — the pre-Phase-2 path.
19563
+ * Populated with `remote-restream` by the orchestrator ONLY when the
19564
+ * camera's detect node differs from its source-owner (P2d, gated by the
19565
+ * `remoteSourcingNodes` rollout setting).
19566
+ */
19567
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
19004
19568
  });
19005
19569
  var RunnerCameraDeviceUIFields = [
19006
19570
  {
@@ -19557,6 +20121,152 @@ var numericSensorCapability = {
19557
20121
  },
19558
20122
  runtimeState: NumericSensorStatusSchema
19559
20123
  };
20124
+ var PetFeederDeviceStatusSchema = external_exports.enum([
20125
+ "normal",
20126
+ "offline",
20127
+ "on_batteries"
20128
+ ]);
20129
+ var gramsPortion = external_exports.number().int().min(4).max(200);
20130
+ var PetFeederStatusSchema = external_exports.object({
20131
+ /** Food currently in the bowl (grams). Null when the device has not
20132
+ * reported a reading yet. On dual-hopper models this is the combined
20133
+ * bowl reading; per-hopper levels live in `food1`/`food2`. */
20134
+ foodLevel: external_exports.number().nullable(),
20135
+ /** Hopper-1 food level (grams) on dual-hopper feeders; null on
20136
+ * single-hopper models. */
20137
+ food1: external_exports.number().nullable(),
20138
+ /** Hopper-2 food level (grams) on dual-hopper feeders; null on
20139
+ * single-hopper models. */
20140
+ food2: external_exports.number().nullable(),
20141
+ /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
20142
+ * (`device_class: problem`, on = low). True when the bowl is empty /
20143
+ * below the feeder's low threshold. */
20144
+ lowFood: external_exports.boolean(),
20145
+ /** Battery charge 0..100 (%). Null on mains-powered models or when the
20146
+ * device has no battery reading. */
20147
+ batteryPower: external_exports.number().min(0).max(100).nullable(),
20148
+ /** Days of desiccant life remaining. Null when the model has no
20149
+ * desiccant sensor. */
20150
+ desiccantLeftDays: external_exports.number().nullable(),
20151
+ /** True while a feed is in progress. */
20152
+ feeding: external_exports.boolean(),
20153
+ /** Decoded connectivity / power status (HA petkit device-status enum).
20154
+ * Null until the device has reported a status. */
20155
+ status: PetFeederDeviceStatusSchema.nullable(),
20156
+ /** Decoded human-readable fault message. Null (or the device's `no_error`
20157
+ * sentinel) means healthy; a non-null string is an active fault. Pairs
20158
+ * with `errorCode` for consumers that want the raw integer. */
20159
+ error: external_exports.string().nullable(),
20160
+ /** Raw device error code (0 / null = no error). */
20161
+ errorCode: external_exports.number().nullable(),
20162
+ /** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
20163
+ isDualHopper: external_exports.boolean(),
20164
+ /** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
20165
+ childLock: external_exports.boolean(),
20166
+ /** Front indicator-light setting. */
20167
+ indicatorLight: external_exports.boolean(),
20168
+ /** Play a chime when dispensing. */
20169
+ feedSound: external_exports.boolean(),
20170
+ /** Speaker / prompt volume level (device-scaled integer). */
20171
+ volume: external_exports.number(),
20172
+ /** Ms epoch when the slice was last refreshed from the cloud. */
20173
+ lastFetchedAt: external_exports.number()
20174
+ });
20175
+ var petFeederCapability = {
20176
+ name: "pet-feeder",
20177
+ scope: "device",
20178
+ deviceNative: true,
20179
+ mode: "singleton",
20180
+ deviceTypes: [DeviceType.PetFeeder],
20181
+ methods: {
20182
+ /**
20183
+ * Dispense food now. Single-hopper feeders take `grams`; dual-hopper
20184
+ * feeders (D4S/D4SH) accept `hopper1`/`hopper2` to target one or both
20185
+ * hoppers. All portions honour the 4–200 g hardware range. At least
20186
+ * one of the three must be present — the provider rejects an empty
20187
+ * request.
20188
+ */
20189
+ feed: method(external_exports.object({
20190
+ deviceId: external_exports.number().int().nonnegative(),
20191
+ grams: gramsPortion.optional(),
20192
+ hopper1: gramsPortion.optional(),
20193
+ hopper2: gramsPortion.optional()
20194
+ }), external_exports.void(), {
20195
+ kind: "mutation",
20196
+ auth: "admin"
20197
+ }),
20198
+ /** Cancel an in-progress manual feed. */
20199
+ cancelFeed: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
20200
+ kind: "mutation",
20201
+ auth: "admin"
20202
+ }),
20203
+ /** Reset the desiccant "days remaining" counter after replacing it. */
20204
+ resetDesiccant: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
20205
+ kind: "mutation",
20206
+ auth: "admin"
20207
+ }),
20208
+ /** Mark a hopper as refilled (D4H/D4S/D4SH). */
20209
+ markFoodReplenished: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
20210
+ kind: "mutation",
20211
+ auth: "admin"
20212
+ }),
20213
+ /** Call the pet with the recorded prompt (D3). */
20214
+ callPet: method(external_exports.object({ deviceId: external_exports.number().int().nonnegative() }), external_exports.void(), {
20215
+ kind: "mutation",
20216
+ auth: "admin"
20217
+ }),
20218
+ /** Play a stored sound by id (D3 / D4H / D4SH). */
20219
+ playSound: method(external_exports.object({
20220
+ deviceId: external_exports.number().int().nonnegative(),
20221
+ soundId: external_exports.number().int().nonnegative()
20222
+ }), external_exports.void(), {
20223
+ kind: "mutation",
20224
+ auth: "admin"
20225
+ }),
20226
+ /** Toggle the child-lock (manual-lock) setting. */
20227
+ setChildLock: method(external_exports.object({
20228
+ deviceId: external_exports.number().int().nonnegative(),
20229
+ on: external_exports.boolean()
20230
+ }), external_exports.void(), {
20231
+ kind: "mutation",
20232
+ auth: "admin"
20233
+ }),
20234
+ /** Toggle the front indicator light. */
20235
+ setIndicatorLight: method(external_exports.object({
20236
+ deviceId: external_exports.number().int().nonnegative(),
20237
+ on: external_exports.boolean()
20238
+ }), external_exports.void(), {
20239
+ kind: "mutation",
20240
+ auth: "admin"
20241
+ }),
20242
+ /** Toggle the dispense chime. */
20243
+ setFeedSound: method(external_exports.object({
20244
+ deviceId: external_exports.number().int().nonnegative(),
20245
+ on: external_exports.boolean()
20246
+ }), external_exports.void(), {
20247
+ kind: "mutation",
20248
+ auth: "admin"
20249
+ }),
20250
+ /** Set the speaker / prompt volume level. */
20251
+ setVolume: method(external_exports.object({
20252
+ deviceId: external_exports.number().int().nonnegative(),
20253
+ level: external_exports.number().int().nonnegative()
20254
+ }), external_exports.void(), {
20255
+ kind: "mutation",
20256
+ auth: "admin"
20257
+ })
20258
+ },
20259
+ status: {
20260
+ schema: PetFeederStatusSchema,
20261
+ kind: "poll"
20262
+ },
20263
+ /**
20264
+ * Runtime-state slice — mirrored by the kernel. UI feeder cards read
20265
+ * the full slice via `device.state.petFeeder.value` and refresh on
20266
+ * every poll without re-querying the provider.
20267
+ */
20268
+ runtimeState: PetFeederStatusSchema
20269
+ };
19560
20270
  var PowerMeterStatusSchema = external_exports.object({
19561
20271
  /** Instantaneous power draw in watts. */
19562
20272
  watts: external_exports.number().optional(),
@@ -20569,6 +21279,233 @@ var zoneRulesCapability = {
20569
21279
  detection: external_exports.array(ZoneRuleSchema).readonly()
20570
21280
  })
20571
21281
  };
21282
+ var UNIT_TABLE = {
21283
+ "\xB0C": {
21284
+ dimension: "temperature",
21285
+ factor: 1,
21286
+ offset: 0
21287
+ },
21288
+ "\xB0F": {
21289
+ dimension: "temperature",
21290
+ factor: 5 / 9,
21291
+ offset: -160 / 9
21292
+ },
21293
+ K: {
21294
+ dimension: "temperature",
21295
+ factor: 1,
21296
+ offset: -273.15
21297
+ },
21298
+ hPa: {
21299
+ dimension: "pressure",
21300
+ factor: 1,
21301
+ offset: 0
21302
+ },
21303
+ kPa: {
21304
+ dimension: "pressure",
21305
+ factor: 10,
21306
+ offset: 0
21307
+ },
21308
+ Pa: {
21309
+ dimension: "pressure",
21310
+ factor: 0.01,
21311
+ offset: 0
21312
+ },
21313
+ mbar: {
21314
+ dimension: "pressure",
21315
+ factor: 1,
21316
+ offset: 0
21317
+ },
21318
+ bar: {
21319
+ dimension: "pressure",
21320
+ factor: 1e3,
21321
+ offset: 0
21322
+ },
21323
+ inHg: {
21324
+ dimension: "pressure",
21325
+ factor: 33.8639,
21326
+ offset: 0
21327
+ },
21328
+ mmHg: {
21329
+ dimension: "pressure",
21330
+ factor: 1.33322,
21331
+ offset: 0
21332
+ },
21333
+ psi: {
21334
+ dimension: "pressure",
21335
+ factor: 68.9476,
21336
+ offset: 0
21337
+ },
21338
+ "m/s": {
21339
+ dimension: "speed",
21340
+ factor: 1,
21341
+ offset: 0
21342
+ },
21343
+ "km/h": {
21344
+ dimension: "speed",
21345
+ factor: 1 / 3.6,
21346
+ offset: 0
21347
+ },
21348
+ mph: {
21349
+ dimension: "speed",
21350
+ factor: 0.44704,
21351
+ offset: 0
21352
+ },
21353
+ kn: {
21354
+ dimension: "speed",
21355
+ factor: 0.514444,
21356
+ offset: 0
21357
+ },
21358
+ "mm/h": {
21359
+ dimension: "precipitation-rate",
21360
+ factor: 1,
21361
+ offset: 0
21362
+ },
21363
+ "in/h": {
21364
+ dimension: "precipitation-rate",
21365
+ factor: 25.4,
21366
+ offset: 0
21367
+ },
21368
+ m: {
21369
+ dimension: "length",
21370
+ factor: 1,
21371
+ offset: 0
21372
+ },
21373
+ mm: {
21374
+ dimension: "length",
21375
+ factor: 1e-3,
21376
+ offset: 0
21377
+ },
21378
+ cm: {
21379
+ dimension: "length",
21380
+ factor: 0.01,
21381
+ offset: 0
21382
+ },
21383
+ km: {
21384
+ dimension: "length",
21385
+ factor: 1e3,
21386
+ offset: 0
21387
+ },
21388
+ in: {
21389
+ dimension: "length",
21390
+ factor: 0.0254,
21391
+ offset: 0
21392
+ },
21393
+ ft: {
21394
+ dimension: "length",
21395
+ factor: 0.3048,
21396
+ offset: 0
21397
+ },
21398
+ mi: {
21399
+ dimension: "length",
21400
+ factor: 1609.344,
21401
+ offset: 0
21402
+ },
21403
+ lx: {
21404
+ dimension: "illuminance",
21405
+ factor: 1,
21406
+ offset: 0
21407
+ },
21408
+ "W/m\xB2": {
21409
+ dimension: "irradiance",
21410
+ factor: 1,
21411
+ offset: 0
21412
+ },
21413
+ W: {
21414
+ dimension: "power",
21415
+ factor: 1,
21416
+ offset: 0
21417
+ },
21418
+ kW: {
21419
+ dimension: "power",
21420
+ factor: 1e3,
21421
+ offset: 0
21422
+ },
21423
+ VA: {
21424
+ dimension: "apparent-power",
21425
+ factor: 1,
21426
+ offset: 0
21427
+ },
21428
+ Wh: {
21429
+ dimension: "energy",
21430
+ factor: 1,
21431
+ offset: 0
21432
+ },
21433
+ kWh: {
21434
+ dimension: "energy",
21435
+ factor: 1e3,
21436
+ offset: 0
21437
+ },
21438
+ MWh: {
21439
+ dimension: "energy",
21440
+ factor: 1e6,
21441
+ offset: 0
21442
+ },
21443
+ V: {
21444
+ dimension: "voltage",
21445
+ factor: 1,
21446
+ offset: 0
21447
+ },
21448
+ mV: {
21449
+ dimension: "voltage",
21450
+ factor: 1e-3,
21451
+ offset: 0
21452
+ },
21453
+ A: {
21454
+ dimension: "current",
21455
+ factor: 1,
21456
+ offset: 0
21457
+ },
21458
+ mA: {
21459
+ dimension: "current",
21460
+ factor: 1e-3,
21461
+ offset: 0
21462
+ },
21463
+ ppm: {
21464
+ dimension: "concentration-volume",
21465
+ factor: 1,
21466
+ offset: 0
21467
+ },
21468
+ ppb: {
21469
+ dimension: "concentration-volume",
21470
+ factor: 1e-3,
21471
+ offset: 0
21472
+ },
21473
+ "\xB5g/m\xB3": {
21474
+ dimension: "concentration-mass",
21475
+ factor: 1,
21476
+ offset: 0
21477
+ },
21478
+ "mg/m\xB3": {
21479
+ dimension: "concentration-mass",
21480
+ factor: 1e3,
21481
+ offset: 0
21482
+ },
21483
+ kg: {
21484
+ dimension: "mass",
21485
+ factor: 1,
21486
+ offset: 0
21487
+ },
21488
+ g: {
21489
+ dimension: "mass",
21490
+ factor: 1e-3,
21491
+ offset: 0
21492
+ },
21493
+ lb: {
21494
+ dimension: "mass",
21495
+ factor: 0.453592,
21496
+ offset: 0
21497
+ },
21498
+ oz: {
21499
+ dimension: "mass",
21500
+ factor: 0.0283495,
21501
+ offset: 0
21502
+ },
21503
+ "%": {
21504
+ dimension: "percentage",
21505
+ factor: 1,
21506
+ offset: 0
21507
+ }
21508
+ };
20572
21509
  var ProviderStatusSchema = external_exports.object({
20573
21510
  connected: external_exports.boolean(),
20574
21511
  deviceCount: external_exports.number(),
@@ -21754,11 +22691,13 @@ var decoderCapability = {
21754
22691
  }), external_exports.void()),
21755
22692
  pullFrames: method(external_exports.object({
21756
22693
  sessionId: external_exports.string(),
21757
- maxCount: external_exports.number().default(1)
22694
+ maxCount: external_exports.number().default(1),
22695
+ waitMs: external_exports.number().optional()
21758
22696
  }), external_exports.array(DecodedFrameSchema)),
21759
22697
  pullHandles: method(external_exports.object({
21760
22698
  sessionId: external_exports.string(),
21761
- maxCount: external_exports.number().default(1)
22699
+ maxCount: external_exports.number().default(1),
22700
+ waitMs: external_exports.number().optional()
21762
22701
  }), external_exports.array(FrameHandleSchema)),
21763
22702
  getFrame: method(external_exports.object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()),
21764
22703
  getShmStats: method(external_exports.object({ sessionId: external_exports.string() }), ShmRingStatsSchema.nullable()),
@@ -22021,13 +22960,51 @@ var ChildLayoutEntrySchema = external_exports.object({
22021
22960
  order: external_exports.number().optional(),
22022
22961
  collapsed: external_exports.boolean().optional()
22023
22962
  });
22963
+ var DeviceLinkFieldSourceSchema = external_exports.object({
22964
+ kind: external_exports.literal("field").optional(),
22965
+ sourceKey: external_exports.string(),
22966
+ cap: external_exports.string(),
22967
+ fieldPath: external_exports.string()
22968
+ });
22969
+ var DeviceLinkLiteralSourceSchema = external_exports.object({
22970
+ kind: external_exports.literal("literal"),
22971
+ value: external_exports.union([
22972
+ external_exports.string(),
22973
+ external_exports.number(),
22974
+ external_exports.boolean(),
22975
+ external_exports.null()
22976
+ ])
22977
+ });
22978
+ var DeviceLinkGlobalSourceSchema = external_exports.object({
22979
+ kind: external_exports.literal("global"),
22980
+ sourceStableId: external_exports.string(),
22981
+ cap: external_exports.string(),
22982
+ fieldPath: external_exports.string()
22983
+ });
22984
+ var DeviceLinkExpressionSourceSchema = external_exports.object({
22985
+ kind: external_exports.literal("expression"),
22986
+ expr: external_exports.string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
22987
+ bindings: external_exports.record(external_exports.string().regex(EXPRESSION_IDENTIFIER_RE), external_exports.union([
22988
+ DeviceLinkFieldSourceSchema,
22989
+ DeviceLinkLiteralSourceSchema,
22990
+ DeviceLinkGlobalSourceSchema
22991
+ ]))
22992
+ }).superRefine((src, ctx) => {
22993
+ const err = validateExpressionSource(src);
22994
+ if (err !== null) ctx.addIssue({
22995
+ code: "custom",
22996
+ message: err,
22997
+ path: ["expr"]
22998
+ });
22999
+ });
22024
23000
  var DeviceLinkSchema = external_exports.object({
22025
23001
  id: external_exports.string(),
22026
- source: external_exports.object({
22027
- sourceKey: external_exports.string(),
22028
- cap: external_exports.string(),
22029
- fieldPath: external_exports.string()
22030
- }),
23002
+ source: external_exports.union([
23003
+ DeviceLinkFieldSourceSchema,
23004
+ DeviceLinkLiteralSourceSchema,
23005
+ DeviceLinkGlobalSourceSchema,
23006
+ DeviceLinkExpressionSourceSchema
23007
+ ]),
22031
23008
  target: external_exports.object({
22032
23009
  cap: external_exports.string(),
22033
23010
  fieldPath: external_exports.string(),
@@ -22056,6 +23033,23 @@ var DeviceLinkSchema = external_exports.object({
22056
23033
  })
22057
23034
  ]).optional()
22058
23035
  });
23036
+ var DeviceCapDisplayOverrideSchema = external_exports.object({
23037
+ unit: external_exports.string().min(1).optional(),
23038
+ precision: external_exports.number().int().min(0).max(10).optional()
23039
+ });
23040
+ var DeviceDisplayOverrideSchema = external_exports.object({
23041
+ icon: external_exports.string().min(1).optional(),
23042
+ label: external_exports.string().min(1).optional(),
23043
+ unit: external_exports.string().min(1).optional(),
23044
+ precision: external_exports.number().int().min(0).max(10).optional(),
23045
+ hidden: external_exports.boolean().optional(),
23046
+ perCap: external_exports.record(external_exports.string(), DeviceCapDisplayOverrideSchema).optional()
23047
+ });
23048
+ var RoleDisplayDefaultSchema = external_exports.object({
23049
+ unit: external_exports.string().min(1).optional(),
23050
+ precision: external_exports.number().int().min(0).max(10).optional(),
23051
+ icon: external_exports.string().min(1).optional()
23052
+ });
22059
23053
  var DeviceInfoSchema = external_exports.object({
22060
23054
  /** Progressive, system-wide unique number. Allocated synchronously by
22061
23055
  * `device-manager.allocateDeviceId` BEFORE the owning `IDevice` is
@@ -22106,7 +23100,9 @@ var DeviceInfoSchema = external_exports.object({
22106
23100
  * `DeviceMeta.childLayout`. Absent ⇒ no layout declared. */
22107
23101
  childLayout: external_exports.array(ChildLayoutEntrySchema).readonly().optional(),
22108
23102
  /** Operator-authored cross-device field wirings. See `DeviceMeta.deviceLinks`. */
22109
- deviceLinks: external_exports.array(DeviceLinkSchema).readonly().optional()
23103
+ deviceLinks: external_exports.array(DeviceLinkSchema).readonly().optional(),
23104
+ /** Operator-authored per-device display override. See `DeviceMeta.display`. */
23105
+ display: DeviceDisplayOverrideSchema.optional()
22110
23106
  });
22111
23107
  var ConfigEntrySchema2 = external_exports.object({
22112
23108
  key: external_exports.string(),
@@ -22167,7 +23163,9 @@ var DeviceMetaSchema = external_exports.object({
22167
23163
  deviceLinks: external_exports.array(DeviceLinkSchema).readonly().optional(),
22168
23164
  /** Semantic role string (`DeviceRole`) — propagated from the spawn pre-seed.
22169
23165
  * Optional: only present for accessory children that carry a known role. */
22170
- role: external_exports.string().nullable().optional()
23166
+ role: external_exports.string().nullable().optional(),
23167
+ /** Operator-authored per-device display override. See `DeviceMeta.display`. */
23168
+ display: DeviceDisplayOverrideSchema.optional()
22171
23169
  });
22172
23170
  var ConfigUISchemaOutput = external_exports.unknown().nullable();
22173
23171
  var StreamProbeResultSchema = external_exports.object({
@@ -22322,9 +23320,42 @@ var deviceManagerCapability = {
22322
23320
  kind: "mutation",
22323
23321
  auth: "admin"
22324
23322
  }),
23323
+ /** Set (or clear) the per-device display override on the meta row. Mirrors
23324
+ * `setChildLayout` persistence; `null` clears the override entirely. The
23325
+ * override unit(s) are normalized (`normalizeUnit`) at write so the render
23326
+ * path's `UNIT_TABLE` lookups always hit canonical spellings. Persisted,
23327
+ * projected, and preserved across re-register/restore. Idempotent. */
23328
+ setDisplay: method(external_exports.object({
23329
+ deviceId: external_exports.number(),
23330
+ display: DeviceDisplayOverrideSchema.nullable()
23331
+ }), external_exports.void(), {
23332
+ kind: "mutation",
23333
+ auth: "admin"
23334
+ }),
23335
+ /** Read the operator-authored per-role display defaults (unit/precision/
23336
+ * icon), keyed by `DeviceRole` string. Empty record when none set. */
23337
+ getRoleDisplayDefaults: method(external_exports.object({}), external_exports.object({ defaults: external_exports.record(external_exports.string(), RoleDisplayDefaultSchema) }), { kind: "query" }),
23338
+ /** Replace the per-role display defaults whole-record (full replace — the
23339
+ * caller sends the complete map). Override unit(s) are normalized at write.
23340
+ * Not per-device, so nothing is emitted; the UI invalidates its own query
23341
+ * on mutate. */
23342
+ setRoleDisplayDefaults: method(external_exports.object({ defaults: external_exports.record(external_exports.string(), RoleDisplayDefaultSchema) }), external_exports.void(), {
23343
+ kind: "mutation",
23344
+ auth: "admin"
23345
+ }),
22325
23346
  /** List the wireable status-schema fields per cap bound to a device.
22326
- * Powers the Wiring tab's field pickers. Caps without a status schema are omitted. */
22327
- getWireableFields: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.object({ caps: external_exports.array(external_exports.object({
23347
+ * Powers the Wiring tab's field pickers. Caps without a status schema are
23348
+ * omitted. Item-array caps (`status.itemArray`, e.g. consumables) also
23349
+ * emit their per-item fields tagged `item: true` (a link targeting one
23350
+ * must carry a `target.itemKey`) plus the cap-level `itemArray`
23351
+ * descriptor. `includeSynthesizable: true` (TARGET pickers only) unions
23352
+ * in unbound device-scoped caps that declare `status.empty` and match
23353
+ * the device's type — so the FIRST link to a synthesize-only cap
23354
+ * (consumables on an HA vacuum) can be authored. */
23355
+ getWireableFields: method(external_exports.object({
23356
+ deviceId: external_exports.number(),
23357
+ includeSynthesizable: external_exports.boolean().optional()
23358
+ }), external_exports.object({ caps: external_exports.array(external_exports.object({
22328
23359
  cap: external_exports.string(),
22329
23360
  fields: external_exports.array(external_exports.object({
22330
23361
  path: external_exports.string(),
@@ -22334,8 +23365,13 @@ var deviceManagerCapability = {
22334
23365
  "boolean",
22335
23366
  "enum"
22336
23367
  ]),
22337
- enumValues: external_exports.array(external_exports.string()).optional()
22338
- })).readonly()
23368
+ enumValues: external_exports.array(external_exports.string()).optional(),
23369
+ item: external_exports.boolean().optional()
23370
+ })).readonly(),
23371
+ itemArray: external_exports.object({
23372
+ path: external_exports.string(),
23373
+ keyField: external_exports.string()
23374
+ }).optional()
22339
23375
  })).readonly() }), { kind: "query" }),
22340
23376
  /** Stamp (or update) the semantic role on the device's meta row.
22341
23377
  * Called by the kernel's `create()` / `spawnAccessoryChild` pre-seed
@@ -22486,7 +23522,11 @@ var deviceManagerCapability = {
22486
23522
  deviceId: external_exports.number(),
22487
23523
  entries: external_exports.array(external_exports.object({
22488
23524
  capName: external_exports.string(),
22489
- kind: external_exports.enum(["native", "wrapped"]),
23525
+ kind: external_exports.enum([
23526
+ "native",
23527
+ "wrapped",
23528
+ "linked"
23529
+ ]),
22490
23530
  providerAddonId: external_exports.string(),
22491
23531
  providerNodeId: external_exports.string(),
22492
23532
  nativeAddonId: external_exports.string()
@@ -22504,7 +23544,11 @@ var deviceManagerCapability = {
22504
23544
  deviceId: external_exports.number(),
22505
23545
  entries: external_exports.array(external_exports.object({
22506
23546
  capName: external_exports.string(),
22507
- kind: external_exports.enum(["native", "wrapped"]),
23547
+ kind: external_exports.enum([
23548
+ "native",
23549
+ "wrapped",
23550
+ "linked"
23551
+ ]),
22508
23552
  providerAddonId: external_exports.string(),
22509
23553
  providerNodeId: external_exports.string(),
22510
23554
  nativeAddonId: external_exports.string()
@@ -23228,7 +24272,7 @@ var AddBrokerInputSchema = external_exports.object({
23228
24272
  });
23229
24273
  var AddBrokerResultSchema = external_exports.object({ id: external_exports.string() });
23230
24274
  var IdInputSchema = external_exports.object({ id: external_exports.string() });
23231
- var TestResultSchema = external_exports.discriminatedUnion("ok", [external_exports.object({
24275
+ var TestResultSchema$1 = external_exports.discriminatedUnion("ok", [external_exports.object({
23232
24276
  ok: external_exports.literal(true),
23233
24277
  latencyMs: external_exports.number()
23234
24278
  }), external_exports.object({
@@ -23265,7 +24309,7 @@ var mqttBrokerCapability = {
23265
24309
  getBrokerConfig: method(IdInputSchema, BrokerConnectionDetailsSchema),
23266
24310
  addBroker: method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }),
23267
24311
  removeBroker: method(IdInputSchema, external_exports.void(), { kind: "mutation" }),
23268
- testConnection: method(IdInputSchema, TestResultSchema, { kind: "mutation" }),
24312
+ testConnection: method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }),
23269
24313
  startEmbeddedBroker: method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }),
23270
24314
  stopEmbeddedBroker: method(IdInputSchema, external_exports.void(), { kind: "mutation" }),
23271
24315
  getStatus: method(external_exports.void(), StatusSchema)
@@ -23313,30 +24357,144 @@ var networkAccessCapability = {
23313
24357
  listEndpoints: method(external_exports.void(), external_exports.array(NetworkEndpointEntrySchema).readonly())
23314
24358
  }
23315
24359
  };
24360
+ var AttachmentMediaTypeSchema = external_exports.enum([
24361
+ "image",
24362
+ "video",
24363
+ "gif",
24364
+ "audio",
24365
+ "icon"
24366
+ ]);
24367
+ var AttachmentSchema = external_exports.object({
24368
+ mediaType: AttachmentMediaTypeSchema,
24369
+ url: external_exports.string().optional(),
24370
+ bytes: external_exports.instanceof(Uint8Array).optional(),
24371
+ mime: external_exports.string().optional(),
24372
+ name: external_exports.string().optional()
24373
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
24374
+ var NotificationFormatSchema = external_exports.enum([
24375
+ "text",
24376
+ "markdown",
24377
+ "html"
24378
+ ]);
24379
+ var NotificationActionSchema = external_exports.object({
24380
+ id: external_exports.string(),
24381
+ label: external_exports.string(),
24382
+ url: external_exports.string().optional()
24383
+ });
23316
24384
  var NotificationSchema = external_exports.object({
23317
- title: external_exports.string(),
23318
24385
  body: external_exports.string(),
23319
- imageUrl: external_exports.string().optional(),
24386
+ title: external_exports.string().optional(),
24387
+ format: NotificationFormatSchema.default("text"),
24388
+ priority: external_exports.number().int().min(1).max(5).default(3),
24389
+ level: external_exports.string().optional(),
24390
+ attachments: external_exports.array(AttachmentSchema).optional(),
24391
+ clickUrl: external_exports.string().optional(),
24392
+ actions: external_exports.array(NotificationActionSchema).optional(),
24393
+ sound: external_exports.string().optional(),
24394
+ ttl: external_exports.number().optional(),
24395
+ tag: external_exports.string().optional(),
23320
24396
  deviceId: external_exports.number().optional(),
23321
24397
  eventId: external_exports.string().optional(),
23322
- priority: external_exports.enum([
23323
- "low",
23324
- "normal",
23325
- "high",
23326
- "critical"
23327
- ]).default("normal"),
23328
24398
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
23329
24399
  });
24400
+ var TargetKindLevelSchema = external_exports.object({
24401
+ id: external_exports.string(),
24402
+ label: external_exports.string(),
24403
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
24404
+ ordinal: external_exports.number().int().min(1).max(5).nullable(),
24405
+ flags: external_exports.object({
24406
+ critical: external_exports.boolean().optional(),
24407
+ silent: external_exports.boolean().optional(),
24408
+ noPush: external_exports.boolean().optional()
24409
+ }).optional(),
24410
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
24411
+ requires: external_exports.array(external_exports.string()).optional(),
24412
+ description: external_exports.string().optional()
24413
+ });
24414
+ var TargetKindAttachmentsCapsSchema = external_exports.object({
24415
+ mediaTypes: external_exports.array(AttachmentMediaTypeSchema),
24416
+ mode: external_exports.enum([
24417
+ "url",
24418
+ "bytes",
24419
+ "both"
24420
+ ]),
24421
+ max: external_exports.number().int().nonnegative(),
24422
+ maxBytes: external_exports.number().int().positive().optional()
24423
+ });
24424
+ var TargetKindCapsSchema = external_exports.object({
24425
+ attachments: TargetKindAttachmentsCapsSchema,
24426
+ /** Max action buttons (0 = none). */
24427
+ actions: external_exports.number().int().nonnegative(),
24428
+ levels: external_exports.array(TargetKindLevelSchema),
24429
+ format: external_exports.array(NotificationFormatSchema),
24430
+ clickUrl: external_exports.boolean(),
24431
+ sound: external_exports.boolean(),
24432
+ ttl: external_exports.boolean(),
24433
+ bodyMaxLen: external_exports.number().int().positive()
24434
+ });
24435
+ var ConfigSchemaPassthrough = external_exports.unknown();
24436
+ var TargetKindSchema = external_exports.object({
24437
+ kind: external_exports.string(),
24438
+ label: external_exports.string(),
24439
+ icon: external_exports.string(),
24440
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
24441
+ addonId: external_exports.string(),
24442
+ configSchema: ConfigSchemaPassthrough,
24443
+ supportsDiscovery: external_exports.boolean(),
24444
+ caps: TargetKindCapsSchema
24445
+ });
24446
+ var TargetSchema = external_exports.object({
24447
+ id: external_exports.string(),
24448
+ name: external_exports.string(),
24449
+ kind: external_exports.string(),
24450
+ addonId: external_exports.string(),
24451
+ enabled: external_exports.boolean(),
24452
+ config: external_exports.record(external_exports.string(), external_exports.unknown())
24453
+ });
24454
+ var DiscoveredTargetSchema = external_exports.object({
24455
+ kind: external_exports.string(),
24456
+ suggestedName: external_exports.string(),
24457
+ config: external_exports.record(external_exports.string(), external_exports.unknown())
24458
+ });
24459
+ var RenderedAsSchema = external_exports.object({
24460
+ level: external_exports.string(),
24461
+ format: NotificationFormatSchema,
24462
+ attachmentsSent: external_exports.number().int().nonnegative(),
24463
+ actionsSent: external_exports.number().int().nonnegative(),
24464
+ truncated: external_exports.boolean(),
24465
+ dropped: external_exports.array(external_exports.string())
24466
+ });
24467
+ var SendResultSchema = external_exports.object({
24468
+ success: external_exports.boolean(),
24469
+ error: external_exports.string().optional(),
24470
+ renderedAs: RenderedAsSchema.optional()
24471
+ });
24472
+ var TestResultSchema = SendResultSchema;
23330
24473
  var notificationOutputCapability = {
23331
24474
  name: "notification-output",
23332
24475
  scope: "system",
23333
24476
  mode: "collection",
23334
24477
  methods: {
23335
- send: method(NotificationSchema, external_exports.void(), { kind: "mutation" }),
23336
- sendTest: method(external_exports.void(), external_exports.object({
23337
- success: external_exports.boolean(),
23338
- error: external_exports.string().optional()
23339
- }), { kind: "mutation" })
24478
+ listTargetKinds: method(external_exports.object({}), external_exports.array(TargetKindSchema)),
24479
+ listTargets: method(external_exports.object({}), external_exports.array(TargetSchema)),
24480
+ discoverTargets: method(external_exports.object({
24481
+ kind: external_exports.string(),
24482
+ config: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
24483
+ }), external_exports.array(DiscoveredTargetSchema)),
24484
+ send: method(external_exports.object({
24485
+ targetId: external_exports.string(),
24486
+ notification: NotificationSchema
24487
+ }), SendResultSchema, { kind: "mutation" }),
24488
+ testTarget: method(external_exports.object({
24489
+ targetId: external_exports.string(),
24490
+ sample: NotificationSchema.optional()
24491
+ }), TestResultSchema, { kind: "mutation" }),
24492
+ upsertTarget: method(external_exports.object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
24493
+ deleteTarget: method(external_exports.object({ targetId: external_exports.string() }), external_exports.void(), { kind: "mutation" }),
24494
+ setTargetEnabled: method(external_exports.object({
24495
+ targetId: external_exports.string(),
24496
+ enabled: external_exports.boolean()
24497
+ }), external_exports.void(), { kind: "mutation" })
23340
24498
  }
23341
24499
  };
23342
24500
  var MethodAccessSchema = external_exports.enum([
@@ -23934,7 +25092,10 @@ var pipelineOrchestratorCapability = {
23934
25092
  methods: {
23935
25093
  /**
23936
25094
  * Pin a camera's pipeline to a specific agent (L1 affinity).
23937
- * The orchestrator re-evaluates the assignment immediately.
25095
+ * The orchestrator re-evaluates the assignment immediately and persists
25096
+ * the pin under the canonical `pipelineNodeId` device-store key (the
25097
+ * legacy `preferredAgent` key is nulled on write and kept only as a
25098
+ * read-only fallback for stores written before the unification).
23938
25099
  */
23939
25100
  assignPipeline: method(external_exports.object({
23940
25101
  deviceId: external_exports.number(),
@@ -23945,8 +25106,9 @@ var pipelineOrchestratorCapability = {
23945
25106
  }),
23946
25107
  /**
23947
25108
  * Clear a camera's pipeline pin and let the auto-balancer re-pick
23948
- * the optimal agent. The orchestrator persists `preferredAgent=null`
23949
- * (and `pipelineNodeId='auto'`), then re-runs the balancer with the
25109
+ * the optimal agent. The orchestrator persists the canonical
25110
+ * `pipelineNodeId='auto'` (and nulls the legacy `preferredAgent`),
25111
+ * then re-runs the balancer with the
23950
25112
  * cached `RunnerCameraConfig` and migrates only when the chosen
23951
25113
  * node differs. The camera stays in `getPipelineAssignments()` —
23952
25114
  * just with `pinned=false`. If no runner is currently available
@@ -24044,10 +25206,11 @@ var pipelineOrchestratorCapability = {
24044
25206
  }))),
24045
25207
  /**
24046
25208
  * Get one camera's decoder placement (computed if not yet pinned).
24047
- * Consumed by `stream-broker.createBroker` so decoder provider
24048
- * selection is deterministic fixes the 2026-04-18 race where
24049
- * `capProviders[0]` silently picked ffmpeg-on-agent-0 for a
24050
- * hub-assigned camera.
25209
+ *
25210
+ * ADVISORY today: reports the orchestrator's decoder preference only.
25211
+ * Actual decode placement is broker-owned (local-node pin + frame-plane
25212
+ * co-location guard). Reserved to become the binding source/decoder-owner
25213
+ * control in the stream-LB epic (Phase 2).
24051
25214
  *
24052
25215
  * `pipelineNodeId` is the node already chosen to run inference for
24053
25216
  * this camera. When provided, the balancer prefers co-location with
@@ -26980,7 +28143,10 @@ var HwAccelBackendInputSchema = external_exports.enum([
26980
28143
  "webgpu",
26981
28144
  "none"
26982
28145
  ]).nullable().optional();
26983
- var HwAccelResolutionSchema = external_exports.object({ preferred: external_exports.array(external_exports.string()).readonly() });
28146
+ var HwAccelResolutionSchema = external_exports.object({
28147
+ preferred: external_exports.array(external_exports.string()).readonly(),
28148
+ rationale: external_exports.string()
28149
+ });
26984
28150
  var HardwareEncoderIdSchema = external_exports.enum([
26985
28151
  "h264_videotoolbox",
26986
28152
  "hevc_videotoolbox",
@@ -27086,10 +28252,7 @@ var platformProbeCapability = {
27086
28252
  getCapabilities: method(external_exports.void(), PlatformCapabilitiesSchema),
27087
28253
  getHardware: method(external_exports.void(), HardwareInfoSchema),
27088
28254
  resolveInferenceConfig: method(external_exports.object({ requirements: external_exports.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
27089
- resolveHwAccel: method(external_exports.object({
27090
- prefer: HwAccelBackendInputSchema,
27091
- nodeId: external_exports.string().optional()
27092
- }), HwAccelResolutionSchema),
28255
+ resolveHwAccel: method(external_exports.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
27093
28256
  /**
27094
28257
  * Hardware-encoder probe — see Task #185. Cached after first call.
27095
28258
  */
@@ -28889,6 +30052,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28889
30052
  addonId: null,
28890
30053
  access: "view"
28891
30054
  },
30055
+ "deviceManager.getRoleDisplayDefaults": {
30056
+ capName: "device-manager",
30057
+ capScope: "system",
30058
+ addonId: null,
30059
+ access: "view"
30060
+ },
28892
30061
  "deviceManager.getSettingsSchema": {
28893
30062
  capName: "device-manager",
28894
30063
  capScope: "system",
@@ -29039,6 +30208,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
29039
30208
  addonId: null,
29040
30209
  access: "create"
29041
30210
  },
30211
+ "deviceManager.setDisplay": {
30212
+ capName: "device-manager",
30213
+ capScope: "system",
30214
+ addonId: null,
30215
+ access: "create"
30216
+ },
29042
30217
  "deviceManager.setIntegrationId": {
29043
30218
  capName: "device-manager",
29044
30219
  capScope: "system",
@@ -29081,6 +30256,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
29081
30256
  addonId: null,
29082
30257
  access: "create"
29083
30258
  },
30259
+ "deviceManager.setRoleDisplayDefaults": {
30260
+ capName: "device-manager",
30261
+ capScope: "system",
30262
+ addonId: null,
30263
+ access: "create"
30264
+ },
29084
30265
  "deviceManager.setStreamProfileMap": {
29085
30266
  capName: "device-manager",
29086
30267
  capScope: "system",
@@ -30059,13 +31240,49 @@ var METHOD_ACCESS_MAP = Object.freeze({
30059
31240
  addonId: null,
30060
31241
  access: "create"
30061
31242
  },
31243
+ "notificationOutput.deleteTarget": {
31244
+ capName: "notification-output",
31245
+ capScope: "system",
31246
+ addonId: null,
31247
+ access: "delete"
31248
+ },
31249
+ "notificationOutput.discoverTargets": {
31250
+ capName: "notification-output",
31251
+ capScope: "system",
31252
+ addonId: null,
31253
+ access: "view"
31254
+ },
31255
+ "notificationOutput.listTargetKinds": {
31256
+ capName: "notification-output",
31257
+ capScope: "system",
31258
+ addonId: null,
31259
+ access: "view"
31260
+ },
31261
+ "notificationOutput.listTargets": {
31262
+ capName: "notification-output",
31263
+ capScope: "system",
31264
+ addonId: null,
31265
+ access: "view"
31266
+ },
30062
31267
  "notificationOutput.send": {
30063
31268
  capName: "notification-output",
30064
31269
  capScope: "system",
30065
31270
  addonId: null,
30066
31271
  access: "create"
30067
31272
  },
30068
- "notificationOutput.sendTest": {
31273
+ "notificationOutput.setTargetEnabled": {
31274
+ capName: "notification-output",
31275
+ capScope: "system",
31276
+ addonId: null,
31277
+ access: "create"
31278
+ },
31279
+ "notificationOutput.testTarget": {
31280
+ capName: "notification-output",
31281
+ capScope: "system",
31282
+ addonId: null,
31283
+ access: "create"
31284
+ },
31285
+ "notificationOutput.upsertTarget": {
30069
31286
  capName: "notification-output",
30070
31287
  capScope: "system",
30071
31288
  addonId: null,
@@ -30095,6 +31312,66 @@ var METHOD_ACCESS_MAP = Object.freeze({
30095
31312
  addonId: null,
30096
31313
  access: "create"
30097
31314
  },
31315
+ "petFeeder.callPet": {
31316
+ capName: "pet-feeder",
31317
+ capScope: "device",
31318
+ addonId: null,
31319
+ access: "create"
31320
+ },
31321
+ "petFeeder.cancelFeed": {
31322
+ capName: "pet-feeder",
31323
+ capScope: "device",
31324
+ addonId: null,
31325
+ access: "create"
31326
+ },
31327
+ "petFeeder.feed": {
31328
+ capName: "pet-feeder",
31329
+ capScope: "device",
31330
+ addonId: null,
31331
+ access: "create"
31332
+ },
31333
+ "petFeeder.markFoodReplenished": {
31334
+ capName: "pet-feeder",
31335
+ capScope: "device",
31336
+ addonId: null,
31337
+ access: "create"
31338
+ },
31339
+ "petFeeder.playSound": {
31340
+ capName: "pet-feeder",
31341
+ capScope: "device",
31342
+ addonId: null,
31343
+ access: "create"
31344
+ },
31345
+ "petFeeder.resetDesiccant": {
31346
+ capName: "pet-feeder",
31347
+ capScope: "device",
31348
+ addonId: null,
31349
+ access: "delete"
31350
+ },
31351
+ "petFeeder.setChildLock": {
31352
+ capName: "pet-feeder",
31353
+ capScope: "device",
31354
+ addonId: null,
31355
+ access: "create"
31356
+ },
31357
+ "petFeeder.setFeedSound": {
31358
+ capName: "pet-feeder",
31359
+ capScope: "device",
31360
+ addonId: null,
31361
+ access: "create"
31362
+ },
31363
+ "petFeeder.setIndicatorLight": {
31364
+ capName: "pet-feeder",
31365
+ capScope: "device",
31366
+ addonId: null,
31367
+ access: "create"
31368
+ },
31369
+ "petFeeder.setVolume": {
31370
+ capName: "pet-feeder",
31371
+ capScope: "device",
31372
+ addonId: null,
31373
+ access: "create"
31374
+ },
30098
31375
  "pipelineAnalytics.clearTracks": {
30099
31376
  capName: "pipeline-analytics",
30100
31377
  capScope: "device",